Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics: why is this output possible?

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()); 
  1. Output of first System.out.println() statement:

    8 
  2. 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.

like image 809
Socke Avatar asked Jan 06 '17 11:01

Socke


People also ask

What is the point of using generics Java?

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.

Why do we use the Java generics capability in data structures?

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.

Why generics are introduced in Java What problems do they overcome?

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.


2 Answers

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.

like image 122
Calculator Avatar answered Sep 19 '22 20:09

Calculator


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.

like image 30
Raphaël Avatar answered Sep 20 '22 20:09

Raphaël