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 Longoccurs 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.