调用泛型方法时未指定的类型参数

时间:2016-09-14 13:21:24

标签: java generics

想象一下,我们有以下泛型类:

public class GenericClass<U> {
    private U value;

    public GenericClass(U value) {
        this.value = value;
    }
}

以及其他一些类MyClass中的以下泛型方法:

public <T extends BT, BT> void genericMethod(T arg) {
    Object genericClass = new GenericClass<BT>(arg);
}

如果我们调用

,BT类型参数将获得哪个值
genericMethod("text");

有些说明:

上面的代码编译没有错误或警告,这对我来说似乎很奇怪。 反编译(通过IntelliJ IDEA 2016)显示以下代码:

public <T extends BT, BT> void genericMethod(T arg) {
    new MyClass.GenericClass(arg);
}

请注意,新GenericClass<BT>(arg)new GenericClass(arg)不同,因为后者相当于new GenericClass<T>(arg)(类型扣除),虽然T extends BT,但这些 不同的类型,GenericClass可能具有内部逻辑,其中确切类型名称扮演重要角色(例如,在某些地图中用作字符串键等)。所以对我来说奇怪的是,为什么编译器默默地使用类型推导而不是产生一些警告(或者甚至错误),指出没有指定BT类型参数。也许我错过了什么。然而,关于Java中的泛型很重要......

2 个答案:

答案 0 :(得分:3)

本回答的第一部分将讨论问题的类型推断部分

在您的示例中唯一自动推断类型的地方是调用genericMethod时。

genericMethod的定义声明了两个泛型类型参数:an unbound BTT which should be a subclass of BT

类型T将根据Java language specification section 18.2.4中指定的规则推断:

  

形式的约束公式,其中S和T是类型,减少如下:

     

...

     

否则,如果T是推理变量,α和S不是基本类型,则约束减少到边界S =α。

在您的示例中,您提供String类型的对象作为类型T的参数。 根据上述规则,T将被推断为等于String

现在T已修复,推理将继续BT。这种类型是通过跟随section 18.3.1来推断的,它表示类型为

的表达式

T extends BT, T == String

意味着

String extends BT

此关系允许编译器将BT绑定到String,因此您最终会得到以下隐含的调用:

genericMethod<String,String>("text")

希望这可以解决一些问题。

回答问题的第二部分:

  

虽然T扩展了BT,但它们是不同的类型和GenericClass   可能有内部逻辑,其中确切的类型名称起重要作用   角色(例如在某些地图中用作字符串键等)

如果不添加更多约束,编译器会将T视为等同于BT,并且只允许您调用BT类型中定义的方法。

除非你使用反射做一些非常奇怪的事情,否则代码不应该依赖于类型参数的运行时值。 当前的GenericClass不是很有用,因为U没有任何约束,因此编译器只允许您对所有对象执行有效的操作。 您可以在该上下文中将U视为与Object相同。

答案 1 :(得分:1)

根据你问题的这一部分

  

GenericClass可能有内部逻辑,其中确切的类型名称起着重要作用(例如在某些地图中用作字符串键等)。

我认为可能会有一些混淆,关于如何推断这些类型。重要的是要意识到,GenericClass将包含的类型是你传入的任何内容的运行时类型。任何依赖于确切类型的东西,你可能在里面做的事情(虽然通常你真的不应该)会工作很好。

总的来说,在编译时完成的类型检查可能看起来有点松散(特别是如果你来自C ++)。在这个问题的答案中有一些有趣的讨论。 Java generics - type erasure - when and what happens