Java通用方法无效-参数不受限制

时间:2019-02-11 02:29:06

标签: java generics

说我有这个代码:

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

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

据我了解,似乎我已经说过两个参数a1 a2和返回类型pick必须在相同的通用类型{{1}下}。

那为什么编译器允许我将TString传递给Integer

2 个答案:

答案 0 :(得分:5)

StringInteger都是Object的子类,以及Java中的其他所有类型。编译通用方法(或类)时,Java会尝试查找在通用类型的每个实例之间共享的最接近的超类型。这部分永远不会失败,因为Object存在。但是,如果将通用类型解析为Object,则可能不再有用。

那么,如果编译器允许使用任何类型,那么在这里使用泛型有什么意义呢?这与返回类型有关。假设您定义了pick(),那么当您尝试编译这些行中的每行时,您认为会发生什么?

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 可以正常编译,但是您丢失了所有有用的类型信息。如果您根本不使用泛型,而是只对所有内容使用Object,就会发生这种情况。在Java 5之前,您必须要做的事情,还有大量的转换和异常捕获。

2 4 无法编译:

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

由于pick()的两个参数仅共享Object作为公共超类型,因此T变成Object并返回Object,而您不能将Object分配给StringInteger

3 效果很好。两个参数具有相同的类型,因此很容易确定TString 5 的工作原理类似。

6 也可以,但是不是,因为T变成了intint是原始类型,因此不能在泛型中使用。尝试解析通用类型T时,编译器首先将原始参数autoboxes转换为“真实”类(在这种情况下为Integer)。即使在 4 5 中也是如此,即使您只是简单地分配了Integer i = 123;之类的文字。此处的区别在于,结果(Integer取消装箱返回到int,以便可以将其分配给i


pick()的示例实现中,返回值应与第二个参数具有相同的类型。如果您的API指定结果始终总是主要从该参数派生,则可以使用两种通用类型:

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

添加了此功能后, 2 仍无法编译,但是 4 可以正常工作。

答案 1 :(得分:2)

编译器将沿着a1a2的继承树查找共同的祖先,在本例中为Object。您未看到的部分原因是您正在丢弃返回值。以下两个版本无法编译:

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

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

但是,以下内容将:

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