Can the generic type of a generic Java method be used to enforce the type of arguments?

JavaGenericsTypes

Java Problem Overview


I would like to use a generic type to ensure, that the arguments of a method are of the same type, like this:

public static <T> void x(T a, T b)

I would assume that the two arguments (a and b), that are passed to this method, would always have to be of the same type. But to my surprise I was able to pass arguments of any type (even primitives) to method x, as if T is erased to Object, no matter what arguments are passed.

The only work-around I found so far, was to use 'extends' like this:

public static <T, U extends T> void x(T a, U b)

But although I can live with it, it is not what I wanted.

Is there a way to use a generic type to force the type of all arguments of a method?

Java Solutions


Solution 1 - Java

If I understand correctly, one way to do it is to explicitly specify the type of T instead of letting the compiler infer its type to be of the most direct superclass in the case of two objects of different types being passed in as arguments. Take something like this, for example:

public class Test {
    public static void main(String[] args) {
        Test.x(5.0, 5);          // This works since type is inferred to be Number
        Test.<Integer>x(5, 5);   // This works since type is stated to be Integer
        Test.<Integer>x(5.0, 5); // This doesn't; type is stated to be Integer and Double is passed in
    }
    
    public static <T> void x(T a, T b) {
    }
}

Solution 2 - Java

If I understand your question correctly, you want this:

x(10, "x");

to fail at compile time. Now consider doing this:

Integer i = 10;
String s = "x";
Object o1 = i;
Object o2 = s;
x(o1, o2);

In this case they are both objects - the same type. I don't think there is any way to really enforce what you want - when you cast your argument to Object it is always possible to call it with two different types without any warnings/errors.

You can specify the type you want to use by using it like this:

ClassName.<Type>x(obj1, obj2);

And it's proably the only way to do it.

Solution 3 - Java

Why this should be problem in the first place is kind of nebulous to me. I suspect you've instead misunderstood something about the ways in which the type system is useful.

What can we do with a <T> void x(T a, T b)? Well, not a whole lot. Inside the body of x, T is the same as Object, so we could only do something like call toString on a and b to print them.

There's really no practical reason a and b must have the same type. Just that they have some type in common, and that type is Object or a subtype of it. In fact, there's no clear reason why <T> void x(T a, T b) actually needs to be generic at all.

  • The method body doesn't care what the actual types of a and b are because it couldn't use them anyway.
  • The call site doesn't care what the actual types of a and b are because x is a void method so it's a black hole.

It's more typical for a method to have a result, like <T> List<T> Arrays.asList(T...):

// This will cause a compile error because
// the type inferred must be compatible
// with the return assignment.
List<Integer> r = Arrays.asList(1, 1.0);

Or a bound:

// We don't care what the actual types of
// a and b are, just that we can call bar()
// on them.
// Note: this method does not need to be generic.
<T extends Foo> void x(T a, T b) {
    a.bar();
    a.bar();
}

Or a bound which asserts some kind of relation:

// We don't care what the actual types of
// a and b are, just that we can compare
// them to each other.
<T extends Comparable<T>> T max(T a, T b) {
    return (a.compareTo(b) < 0) ? b : a;
}

Solution 4 - Java

You can explicitly specify the type parameter, when calling the method. For example:

 <String>x("hello", "world");

However, if you don't specify the type-parameter explicitly and rely only on the type inference feature of Java, then I don't think you can, not only in Generics, but in general.

The method parameter's type is not a concrete type, but it's rather something that denotes a set of applicable types (even this set can comprise of just one type, in the case of final classes, for example).

For example, this method:

public void x(Something a) { }

denotes a method, which parameter should be of a type from the set of types, which are compatible with Something (i.e. Something and all its subtypes).

The same applies for Generics.

Solution 5 - Java

Presumably, you are not calling your generic method in a generic fashion, so it's treated like a call to x(Object a, Object b). In this example:

public class Test {

  static <T> void x(T a, T b) {
  }

  public static void main(String[] args) {
    x(1, 2); // compiles
    Test.<String>x(1, 2); // does not compile
    Test.<String>x("a", "b"); // compiles
  }
}

The first call to x is not made generically so it compiles. The second call equates T to String, so it fails because 1 and 2 are not Strings. The third call compiles because it properly passes in Strings.

Solution 6 - Java

This worked for me

public static <T> void x(T a, T b, Class<T> cls) {
}

now this compiles

public static void main(String[] args) throws Exception {
	x(1, 2, Integer.class);
}

and this does not

public static void main(String[] args) throws Exception {
	x(1, "", Integer.class);
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionsys64738View Question on Stackoverflow
Solution 1 - JavaTNTView Answer on Stackoverflow
Solution 2 - Javabarteks2xView Answer on Stackoverflow
Solution 3 - JavaRadiodefView Answer on Stackoverflow
Solution 4 - JavaKonstantin YovkovView Answer on Stackoverflow
Solution 5 - JavaGriffeyDogView Answer on Stackoverflow
Solution 6 - JavaEvgeniy DorofeevView Answer on Stackoverflow