How to write a generic method that takes two arguments of the same types in java?

I was very surprised when I noticed that the following code compiles without warnings and fingerprints of 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 was expecting a compilation error.

Is there a reason why this code compiles?

What is the correct way to ensure that arguments are of the same type?

Edit: What about parameters of a limited type? The best I can come up with 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 does not allow circular constraints. <T extends U, U extends T> does not compile. Is this a dead end?

+7
source share
3 answers

The reason this compiler is because Java will output the most specific supertype of passed arguments, in this case Object Serializable & Comparable<? extends Serializable & Comparable<? extends Comparable<?>>> Serializable & Comparable<? extends Serializable & Comparable<? extends Comparable<?>>> Serializable & Comparable<? extends Serializable & Comparable<? extends Comparable<?>>> , after 1 is put into a square Integer and "1" is passed as a String .

No generics:

 private static void method(Number arg1, Number arg2) { 

Even without generics, you can pass Integer and 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 generic edge case that I can come up with to make sure this is the exact type. If you have a final class and you place an upper bound, you can limit it to the same class.

 public <T extends MyFinalClass> void method(T arg1, T arg2) { // Yes, they're both MyFinalClasses } 

But then you can do the same without generics.

 public void method(MyFinalClass arg1, MyFinalClass arg2) { // Yes, they're both MyFinalClasses } 
+7
source

You can add a class as an additional parameter.

 private static <T> void method(T arg1, T arg2, Class<T> type) { // ... } 

Now you need to specify the generic type.

You can still call method(1, "1", Object.class); , but at least you explicitly point to a generic type.

+4
source

This is impossible to do. Or look at it differently, the two supporting arguments are always of the "same type" - Object - any arguments of the reference type are always instances of Object .

T can always be Object and take any two supporting arguments. Even with <T, U extends T> void method(T arg1, U arg2) both T and U can be Object and, therefore, accept any two arguments again.

The main reason for this is that there is no type safety reason for such a restriction. One of the main points of inheritance is that it should be possible to safely handle instances of a subclass, such as a superclass. The superclass reference type can point to an instance of this class or subclass freely. Therefore, two reference variables that have the same type of compilation time can always accurately indicate runtime on instances of different types of subclasses. Therefore, at compile time, you can never make any statement about the relationship between the actual runtime classes of two instances, except that they are subclasses of the compile time type. Since it is safe for two arguments to be instances of different classes at runtime, it is equally safe to pass two arguments of different types of compilation time.

+1
source

Source: https://habr.com/ru/post/977162/


All Articles