I was very surprised when I noticed that following code compiles without warnings and prints Integer / String
:
public final class GenericsTest {
private static <T> void method(T arg1, T arg2) {
System.out.println(arg1.getClass().getSimpleName());
System.out.println(arg2.getClass().getSimpleName());
}
public static void main(String[] args) {
method(1, "1");
}
}
I expected a compilation error.
Is there a reason why this code compiles?
What is the correct way to ensure that arguments have the same type?
Edit: What about bounded type parameters? The best I can think of is this:
private static <T, U extends T> void method(T arg1, U arg2) {
System.out.println(arg1.getClass().getSimpleName());
System.out.println(arg2.getClass().getSimpleName());
}
Unfortunately, java doesn't allow cyclic constraints. <T extends U, U extends T>
doesn't compile. Is this a dead end?
The reason that this compiles is because Java will infer the most specific supertype of the arguments passed in, in this case, Object
Serializable & Comparable<? extends Serializable & Comparable<? extends Comparable<?>>>
, after 1
is boxed to Integer
and "1"
is passed as a String
.
Without generics:
private static void method(Number arg1, Number arg2) {
Even without generics, you can pass in an Integer
and a Double
.
Only if the type in question is final
can you do this, without generics:
private static void method(String arg1, String arg2) {
// Yes, they're both Strings, guaranteed.
There is one edge case with generics that I can think of to ensure that they are the exact type. If you have a final
class, and you place an upper bound, then you can restrict it to that same class.
public <T extends MyFinalClass> void method(T arg1, T arg2) {
// Yes, they're both MyFinalClasses
}
But then you could do the same thing without generics.
public void method(MyFinalClass arg1, MyFinalClass arg2) {
// Yes, they're both MyFinalClasses
}
You could add the class as an additional parameter.
private static <T> void method(T arg1, T arg2, Class<T> type) {
// ...
}
Now you have to specify the common type.
You can still call method(1, "1", Object.class);
but at least you are explicit about the common type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With