Reputation: 16039
When writing a generic method to process data for a form, I came across with the following (as I see it) unexpedted behavior. Given the following code:
public class Test {
public <T> void someGenericMethod(Integer a) {
@SuppressWarnings("unchecked")
T t = (T) a;
System.out.println(t);
System.out.println(t.getClass());
}
public static void main(String[] args) {
Test test = new Test();
test.<BigDecimal>someGenericMethod(42);
}
}
AFAIK, the code above should generate a ClassCastException
in the line T t = (T) a
because the method call in main is setting the parametrized type to BigDecimal
and casting from Integer
to BigDecimal is not allowed, conversely to what I expected, the program executed well and printed the following:
42
class java.lang.Integer
In fact, if I add another parameter to the method signature (like String b
) and make another assigment T t2 = (T) b
, the program prints
42
class java.lang.String
Why the t
variable changed it's type to Integer
(is, by any chance, making some kind of promotion on the type T to Object)?
Any explanation on this behavior is welcome
Upvotes: 4
Views: 233
Reputation: 183456
(T) a
is an unchecked cast: due to type erasure, the runtime has no way of knowing what type T
is, so it can't actually check if a
belongs to type T
.
The compiler issues a warning when you do this; in your case, you've suppressed that warning by writing @SuppressWarnings("unchecked")
.
Edited to add (in response to a further question in the comments below):
If you want to check the cast, you can write this:
public class Test {
public <T> void someGenericMethod(Class<T> clazz, Integer a) {
T t = clazz.cast(a);
System.out.println(t);
System.out.println(t.getClass());
}
public static void main(String[] args) {
Test test = new Test();
// gives a ClassCastException at runtime:
test.someGenericMethod(BigDecimal.class, 42);
}
}
by passing in clazz
, you allow the runtime to check the cast; and, what's more, you allow the compiler to infer T
from the method arguments, so you don't have to write test.<BigDecimal>someGenericMethod
anymore.
Of course, the code that calls the method can still circumvent this by using an unchecked cast:
public static void main(String[] args) {
Test test = new Test();
Class clazz = Object.class;
test.someGenericMethod((Class<BigDecimal>) clazz, 42);
}
but then that's main
's fault, not someGenericMethod
's. :-)
Upvotes: 3
Reputation: 22692
You specify a type parameter in your method signature, but never use it.
I think you want something like this:
public class Test {
public <T> void someGenericMethod(T someItem) {
System.out.println(someItem);
System.out.println(someItem.getClass());
}
}
public static void main(String[] args) {
Test test = new Test();
BigDecimal bd = new BigDecimal(42);
test.someGenericMethod(42); // Integer
test.someGenericMethod("42"); // String
test.someGenericMethod(42L); // Long
test.someGenericMethod(bd); // BigDecimal
}
Note that there's no need to cast.
The parameter type is declared in the method signature and inferred from the parameter.
In your code you're parameterizing the method call (which I've never seen) and passing in an int.
It's kinda hard to understand what you're trying to do, since your example code does nothing.
Upvotes: 1
Reputation: 122489
When compiling, your code above basically becomes the following non-generic method:
public void someGenericMethod(Integer a) {
Object t = a;
System.out.println(t);
System.out.println(t.getClass());
}
There is no cast. No exception.
Upvotes: 2