想象一下,我们有以下泛型类:
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中的泛型很重要......
答案 0 :(得分:3)
本回答的第一部分将讨论问题的类型推断部分
在您的示例中唯一自动推断类型的地方是调用genericMethod
时。
genericMethod
的定义声明了两个泛型类型参数:an unbound BT
和T 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