6

Why this happened? One line in the code works well while the other similar line doesn't. Does the automatic type cast only happen in certain conditions? I have tried to assign gt.echoV() to an Object and it works well; but when I assign it to a String, the same error will come out again.

public class GeneMethodTest {

    public static void main(String... args) {
        GeneMethodTest gt = new GeneMethodTest();
        gt.<String>echoV(); //this line works well
        gt.<String>echoV().getClass();//this line leads to a type cast exception                                                          
    }

    public <T> T echoV() {
        T t=(T)(new Object());                                                                    
        return t;
    }
}
charles_ma
  • 786
  • 7
  • 10
  • 1
    This answer may help http://stackoverflow.com/a/3437930/1316346 – Kevin DiTraglia Jul 31 '13 at 18:53
  • `(T)(new Object())` is an *unchecked cast* - make sure to read up on what that is and its implications, along with *type erasure*. – Paul Bellora Jul 31 '13 at 18:55
  • Yup, i know that. @PaulBellora Actually (T)(new Object()) will do nothing during run time because of type erasure. The problem is when I call gt..echoV() the automatic class casting doesn't take effect while it does take effects when i call gt.echo.getClass() – charles_ma Jul 31 '13 at 19:00
  • @PaulBellora care to explain your comment a bit further ? from the documentation: "Replace all type parameters in generic types with their bounds **or Object** if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods." http://docs.oracle.com/javase/tutorial/java/generics/erasure.html – Nir Alfasi Jul 31 '13 at 19:06
  • @charles_ma That's because the `` in of itself has nothing to do with casting - it's just the type argument to the generic method. `gt.echoV()` alone requires no cast to be inserted by the compiler, so that doesn't fail at runtime. – Paul Bellora Jul 31 '13 at 19:08
  • @PaulBellora yup i think maybe this is the reason. I think the compiler won't bother inserting code if you don't use the resulting reference. But when I execute Object o = gt.echoV(); it works well too, so the compiler must have done some extra work to tell whether it is necessary to do the casting~ – charles_ma Jul 31 '13 at 19:13
  • @alfasin I'm not sure I understand your comment. I was recommending the OP (and others) read up on the concepts of type erasure and unchecked casts. – Paul Bellora Jul 31 '13 at 19:24
  • @PaulBellora I wrote that comment cause you said that the problem lies within `(T)(new Object())` but I think that the problem is not with this casting (inside the method) - but with the cast of the returned object - the line that Charles marked in his question. But maybe I'm missing something and that's why I asked you to further explain your comment. – Nir Alfasi Jul 31 '13 at 20:34
  • The problem, of course, is that echoV is returning an Object, not a String. In the case where the return value from echoV is not used, javac does not generate a cast to String. But any attempt to reference the value generates the cast and hence the error. – Hot Licks Jul 31 '13 at 22:46
  • (Remember, ALL that generic types do is cause the compiler to insert cast operations -- nothing in the logic is changed except for the added casts, and, in particular, an Object will not magically be turned into a String.) – Hot Licks Jul 31 '13 at 22:48

3 Answers3

4

gt.<String>echoV().getClass(); produces the equivalent of the following sequence of operations:

// Inside echoV
Object t = new Object();  // Note that this is NOT a String!
Object returnValue = t;
// In main
String stackTemp = (String) returnValue;  // This is the operation that fails
stackTemp.getClass();

What you get "for free" with generics is that (String) cast. Nothing else.

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
2

This works perfect, nothing special, normal use of generics

gt.<String>echoV(); //this line works well

Here do we have something less obvious. Because generic method are defined at runtime does the jvm not know what kind of Class the generic method will return at compiletime, hence the classTypeException

gt.<String>echoV().getClass();//this line leads to a type cast exception   

you should allocated it to a variable first, because the jvm does know the type of the variable at compiletime

String s = gt.<String>echoV();
s.getClass();
Joris W
  • 517
  • 3
  • 16
  • 2
    Actually if you do `System.out.println(gt.echoV())` you got a `java.lang.ClassCastException`. – Julián Chamalé Jul 31 '13 at 18:56
  • same problem, System.out.println expects a string as parameter. If this is not the case, it will call the toString() of the object, but at compileat does the jvm not know from what object – Joris W Jul 31 '13 at 18:59
  • I believe maybe it's because in the first line, i called the function but didn't use the reference so the compiler didn't bother adding any class cast code; but in the second line, i tried to use the resulting reference from the call so the compiler had to insert class casting code there~ And same situation when you call System.out.println(gt.echoV()) @System.exit – charles_ma Jul 31 '13 at 19:06
  • 1
    @user1394628 every object has a `toString()` method hence it doesn't have anything to do with things that happened during compile-time. – Nir Alfasi Jul 31 '13 at 19:08
  • @user1394628 I believe String s = gt.echoV(); will produce a class cast exception at runtime too~ – charles_ma Jul 31 '13 at 19:19
  • But the compiler doesn't *have to* assign it to a temporary variable of type `String`. Since `.getClass()` is a method on `Object`, it *could* just assign it to a temporary variable of type `Object`, and thus not need a cast. – newacct Jul 31 '13 at 21:24
  • `String s = gt.echoV();` will fail too. – Hot Licks Jul 31 '13 at 22:49
1

change this line:

gt.<String>echoV().getClass();

to:

(gt.echoV()).getClass();

and it'll compile
(it'll return: class java.lang.Object)

The root of the ClassCastException is that the method returns t (of generic type T which is an object) and you try to downcast it to a String. You can also change your code to return:

return (T)"some-string";

in order to remove the error.

Generic are used by the compiler to check what type of object to expect, so it can catch mistakes the developer made in compile time (vs. run-time errors). So IMHO this way of using generics beats the purpose.

Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129