Reputation: 3431
public class Test {
public static class Nested<T> {
public T val;
Nested(T val) { this.val = val; }
}
public static void main(String[] args) {
Nested<Integer> a = new Nested<Integer>(5);
Nested<Integer> b = new Nested<Integer>(2);
Integer diff = a.val - b.val;
}
}
The above code works fine. However, if I add a method to Nested:
T diff(Nested<T> other) { return this.val - other.val; }
I get a compilation error:
operator - cannot be applied to T,T
This makes sense to me. The type of T gets erased at runtime, so Java can't apply an operator that's only defined for certain classes like Integer. But why does a.val - b.val
work?
Edit:
Lots of good answers. Thanks everyone. The gist of it, if I understand correctly, is that the compiler can add casts to Integer in a.val - b.val
because it knows a
and b
were instantiated as as Nested
. However, since <Integer
>this.val - other.val
occurs inside the body of a generic function definition (where T still could be anything), the compiler cannot add the casts that would be necessary to make "-
" work. This leads to a more interesting question, namely, if the Java compiler were capable of inlining, would it be possible for a generic function like diff to work?
Upvotes: 13
Views: 339
Reputation: 29436
You are getting a compile-time error, not a runtime one.
public static void main(String[] args) {
Nested<Integer> a = new Nested<Integer>(5);
Nested<Integer> b = new Nested<Integer>(2);
Integer diff = a.val - b.val;
}
Here, compiler knows that both T
are Integer
. You just declared <Integer>
.
T diff(Nested<T> other) { return this.val - other.val; }
Here, compiler is not certain about T
. It could be anything. And, numeric only operator -
is not allowed for just anything.
Upvotes: 8
Reputation: 13556
Because method call is at runtime and a.val - b.val
is checked at compile time.
Integer
and -
operation is allowed for integers.T
is not known to the compiler in advance, hence it is not sure whether -
operation is valid or not. Hence the compiler error.Consider we use the method as diff(Nested<Book> other)
so there is no way a book can be subtracted from other.
Upvotes: 1
Reputation: 10028
Because the code doesn't live within Nested, the type is known. The compiler can clearly see that a.val - b.val is an Integer minus an Integer, which can be auto-boxed. The compiler essentially rewrites it to
Integer diff = Integer.valueOf(((Integer) a.val).intValue() - ((Integer) b.val).intValue())
The .intValue and .valueOf calls are from the auto-boxing and auto-unboxing.
The type casts are safe for the compiler to insert because you used a parameterized type Nested.
True, technically, a could be something else, like a Calendar object, since the type is unknown at runtime. But if you are using generics, the compiler trusts that you aren't doing anything dumb to circumvent it. Therefore, if a.val or b.val were anything other than Integers, a ClassCastException would be thrown at runtime.
Upvotes: 1
Reputation: 726539
The difference between the two is whether you are inside a generic method or you are outside of it.
You got it absolutely right that inside the method T
is not known to be an Integer
, so operator minus -
cannot be applied. However, when you are in main()
, outside the generic method, the compiler knows that you've instantiated Nested
with Integer
, so it knows very well how to apply the operator. Even though the implementation of the generic has erased the type to produce the code for Nested<T>
, the compiler does not think of a
and b
in terms of Nested<T>
: it has enough knowledge to insert an appropriate cast, unbox the results, and apply the minus -
operator.
Upvotes: 9
Reputation: 17422
a.val - b.val
works because it is validated by the compiler, not in runtime. The compiler "sees" that you're using <Integer> and it compiles and runs Ok, in runtime there is no problem even with erasure because the compiler already validated that.
Upvotes: 1