Java generic method not working - parameter not being restricted [duplicate]

こ雲淡風輕ζ 提交于 2021-02-19 08:01:47

问题


Say I have this code:

class Demo
{
    static <T> T pick(T a1, T a2)
    {
        return a2;
    }

    public static void main(String[] args)
    {
        pick("d", 123);
    }
}

From what I learned, it seems like I have stated that the two parameters a1, a2 and the return type of pick must be under the same generic type T.

So why is the compiler allowing me to pass a String and an Integer to pick?


回答1:


Both String and Integer are subclasses of Object, along with every other type in Java. When compiling a generic method (or class), Java attempts to find the closest supertype shared between every instance of the generic type. This part never fails, because Object exists. But if a generic type is resolved to Object, it may not be useful anymore.

So what’s the point in using generics here if the compiler will let any types to be used? It’s all to do with the return type. Assuming your definition of pick(), what do you think will happen when you try to compile each of these lines?

Object o = pick("Hello", 123);       // 1

String s = pick("Hello", 123);       // 2
String s = pick("Hello", "world");   // 3

Integer i = pick("Hello", 123);      // 4
Integer i = pick(123, 456);          // 5
int i = pick(123, 456);              // 6

1 compiles just fine, but then you’ve lost any useful type information. This is what would happen if you didn’t use generics at all, and instead just used Object for everything. It’s you’d have had to do before Java 5, along with plentiful casting and exception catching.

2 and 4 won’t compile:

error: incompatible types: inferred type does not conform to upper bound(s)

Since the two arguments to pick() share only Object as a common supertype, T becomes Object and an Object is returned and you can’t assign an Object to a String or an Integer.

3 works just fine though. Both arguments have the same type, so T is easily determined to be String. 5 works for similar reasons.

6 also works, but not because T becomes int. int is a primitive type, so it can’t be used in a generic. When attempting to resolve the generic type T, the compiler first autoboxes the primitive arguments into a ‘real’ class (Integer in this case). This also happens for 4 and 5, and even when you simply assign a literal like Integer i = 123;. The difference here is that the result (an Integer) is unboxed back to an int so that it can be assigned to i.


In your example implementation of pick(), the return value should have the same type as the second parameter. If your API specifies that the result is always derived primarily from that parameter, you could use two generic types:

static <T, U> T pick(U a1, T a2) {
    return a2;
}

With this addition, 2 still fails to compile, but 4 works as you’d expect.




回答2:


The compiler will walk down the inheritance tree of a1 and a2 to find a common ancestor, in this case Object. Part of the reason you aren't seeing that is that you are discarding the return value. The following two versions won't compile:

String choice = pick("d", 123);

and

Integer choice = pick("d", 123);

The following will, however:

Object choice = pick("d", 123);


来源:https://stackoverflow.com/questions/54623214/java-generic-method-not-working-parameter-not-being-restricted

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!