2

I have the following Java code that I tried out in JShell.

class X<A> {
  A id(A a) {
    return a;
  }
}

Case 1

X<Integer> w = new X<Integer>();
w.id(5)

In this case, JShell prints just 5, as I had expected. I expect the identity function in w to be parameterized with type Integer and thus expects nothing but a Integer. Supplying variables that are not subtypes of Integers causes this function to error.

Case 2

X x = new X<Integer>();
x.id(5)

JShell does output 5, but along with 5, it also outputs this error message:

|  Warning:
|  unchecked call to id(A) as a member of the raw type X
|  x.id(5)
|  ^-----^

What does it mean by an unchecked call to id(A)? It doesn't seem to infer the type of x to be X<Integer> as I'm also able to run x.id("5") with just a warning which is isn't possible in case 1. Does this mean the identity function in x is polymorphic (with respect to the type of variable supplied)?

Case 3

X y = new X<>()
y.id(5)
X z = new X()
z.id(5)

This situation is identical to case 2. However, I am unable to wrap my mind around the code. What is the parameterized type of y? Are objects y and z identical apart from the fact that they are two separate objects?

I'm guessing the notion of type erasure is playing a part in this but am unable to truly understand it and explain the phenomenon above.

  • 2
    You just discovered [raw types](https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html). – Turing85 May 04 '18 at 13:05

2 Answers2

2

In Java generics you have to specify the generic type on the variable type, not its implementation.

This means that you have to have

X<Integer> x = new X<>();

or

X<Integer> x = new X<Integer>();

for the type to register. Since Java 7 you have been allowed to use the diamond operator, which means that the compiler is able to infer the implementation type from the data type. If you have

X x = new X<Integer>();

then java assumes that the generic type is simply an Object. This is the reason you get an unchecked assignment, because as far as the compiler is concerned, X contains Objects, not Integers.

Case 3 is almost identical to case 2, the implementation is a runtime feature, so the compiler always assumes it is an object. Case 3 is basically what you had in Java 5.

vandench
  • 1,973
  • 3
  • 19
  • 28
  • "*then java assumes that the generic type is simply an Object.*" - Not really. Generics are not covariant, this is why `X x = new X()` does not compile. Raw types are worse. – Turing85 May 04 '18 at 13:14
  • @Turing85 I'm not sure of an easily understandable way of putting it, but if I remember correctly specifying `Object` in the data type strongly defines it's type, while not specifying a generic type at all "loosely" defines it's type as an Object (or basically allows for any type). – vandench May 04 '18 at 14:30
  • the "*allowing for any type*" is the critical part: `X x = new X(); X s = x; System.out.println(s.id("Hello"));` This will compile. It will even execute. And it won't throw an `Exception`. That is why raw types are bad. – Turing85 May 04 '18 at 14:33
-1

First of all.

X<Integer> x = new X<Integer>();

is not the same as

X x = new X<Integer>();

Well. tecnically it is, but by removing the type on the left side, you are telling the compiler that the variable has no specific type. I give you an example.

List<Integer> l = new ArrayList<>();
l.add(1); //Everything fine
List l = new ArrayList<Integer>();
l.add(1); //Compiler or Runtimeerror

In Java the left part of a variable is the most important (no valid in Java10 anymore), that's why if you are declaring a variable in this manner you gotta tell him his type on the left side(in Java 10 this is no concern anymore there you use "var" on the left side), on the right side you can just use diamond type <> .

Levent Dag
  • 162
  • 8
  • No, `l.add(1); //Compiler or Runtimeerror` is wrong, neither of those. And "no valid in Java10 anymore" that is wrong, as even `var` is statically typed at compile time. – Dorian Gray May 04 '18 at 16:04