Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

generics

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?

like image 933
alkedr Avatar asked Oct 23 '14 23:10

alkedr


2 Answers

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
}
like image 125
rgettman Avatar answered Oct 14 '22 16:10

rgettman


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.

like image 21
Max Fichtelmann Avatar answered Oct 14 '22 15:10

Max Fichtelmann