I have this class:
class MyClass<N extends Number> { N n = (N) (new Integer(8)); }
And I want to get these outputs:
System.out.println(new MyClass<Long>().n); System.out.println(new MyClass<Long>().n.getClass());
Output of first System.out.println()
statement:
8
Output of second System.out.println()
statement:
java.lang.ClassCastException: java.lang.Integer (in module: java.base) cannot be cast to java.lang.Long (in module: java.base)
Why do I get the first output? Isn't there a cast as well? Why do I get the exception in the second output?
PS: I use Java 9; I tried it with the JShell and I got an exception on both outputs. Then I tried it with IntelliJ IDE and got the first output but the exception at the second.
Java Generics helps the programmer to reuse the code for whatever type he/she wishes. For instance, a programmer writes a generic method for sorting an array of objects. Generics allow the programmer to use the same method for Integer arrays, Double arrays, and even String arrays.
Generics allow us to pass type information, in the form of <type>, to the compiler, so that the compiler can perform all the necessary type-check during compilation to ensure type-safety at runtime. Let's take a look at the declaration of interface java. util.
To overcome the above problems of collections(type-safety, type casting) generics introduced in java 1.5v . Main objectives of generics are: 1) To provide type safety to the collections. 2) To resolve type casting problems. To hold only string type of objects we can create a generic version of ArrayList as follows.
The behavior that IntelliJ shows is clear to me:
You have an unchecked cast in MyClass
. This means new Integer(8)
is not immediately cast to Long
but to the erasure Number
(which works), when this line is executed: N n =(N)(new Integer(8));
Now let's look at the output statements:
System.out.println(new MyClass<Long>().n);
boils down to String.valueOf(new MyClass<Long>().n)
-> ((Object)new MyClass<Long>().n).toString()
which works fine, because n is accessed through Object
and also the toString()
method is accessed through static type Object
-> no cast to Long
occurs. new MyClass<Long>().n.toString()
would fail with an exception, because toString()
is tried to be accessed via static type Long
. Therefore a cast of n to type Long
occurs which is not possible(Integer
can't be cast to Long
).
The same thing occurs when executing the 2nd statement:
System.out.println(new MyClass<Long>().n.getClass());
The getClass
method (declared in Object
) of type Long
is tried to be accessed through static type Long
. Therefore a cast of n to type Long
occurs which yields a cast exception.
JShell behavior:
I tried to reproduce the resulting exception for the first output statement on JShell - Java 9 early access Build 151:
jshell> class MyClass<N extends Number> { ...> N n = (N) (new Integer(8)); ...> } | Warning: | unchecked cast | required: N | found: java.lang.Integer | N n = (N) (new Integer(8)); | ^--------------^ | created class MyClass jshell> System.out.println(new MyClass<Long>().n); 8 jshell> System.out.println(new MyClass<Long>().n.getClass()); | java.lang.ClassCastException thrown: java.base/java.lang.Integer cannot be cast to java.base/java.lang.Long | at (#4:1)
But it seems that JShell gives the exact same results as IntelliJ. System.out.println(new MyClass<Long>().n);
outputs 8 - no exception.
This happen because of Java erasure.
Since Integer
extends Number
, the compiler accepts the cast to N
. At runtime, since N
is replaced by Number
(due to the erasure), there is no problem to store an Integer
inside n
.
The argument of method System.out.println
is of type Object
so there is no problem to print the value of n
.
However, when calling a method on n
, a type check is added by the compiler to ensure the right method will be called. Hence resulting in a ClassCastException
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With