Java 泛型:有关使用泛型方法进行类型捕获和生成推理的问题
这是我上一个问题的后续问题,但由于上一个线程很长,我决定开始另一个与几乎相同主题相关的线程。
public class GenericMethodInference {
static <T> void test1(T t1, T t2) {}
static <T> void test3(T t1, List <T> t2) {}
static <T> void test4(List <T> t1, List <T> t2) {}
public static void main(String [] args) {
List <Object> c = new LinkedList<Object>();
List <? extends Object> d = new ArrayList<Integer>();
List e = new ArrayList<Integer>();
test1("Hello", new Integer(1)); // ok clause (1)
GenericMethodInference.<Object>test1("Hello", new Integer(1)); // ok clause (2)
test3("Hello", c); // ok clause (3)
test4(d,d) // clause (4) Error due to different type capture generated
}
注意:如果将光标移到每个子句上,您将看到正在生成并显示在 Eclipse 上的推理
:子句 (1) 将产生 测试1
b.子句 (2) 将准确生成实际类型参数中定义的内容
c.子句(3)将产生
问题:
- 为什么子句 (1) 没有产生
- 为什么子句 (3) 产生
- 既然第(4)条使用了相同的变量,为什么即使使用的参数是相同的变量d,也会生成2个不同类型的捕获?
This is a follow-up to my previous question but since the previous thread was a long one, i decided to start another thread pertaining to the almost same topic.
public class GenericMethodInference {
static <T> void test1(T t1, T t2) {}
static <T> void test3(T t1, List <T> t2) {}
static <T> void test4(List <T> t1, List <T> t2) {}
public static void main(String [] args) {
List <Object> c = new LinkedList<Object>();
List <? extends Object> d = new ArrayList<Integer>();
List e = new ArrayList<Integer>();
test1("Hello", new Integer(1)); // ok clause (1)
GenericMethodInference.<Object>test1("Hello", new Integer(1)); // ok clause (2)
test3("Hello", c); // ok clause (3)
test4(d,d) // clause (4) Error due to different type capture generated
}
Note: If you move your cursor over each clause, you will see the inference being generated and displayed on Eclipse:
a. Clause (1) will produce <? extends Object> test1 <? extends Object, ? extends Object>
b. Clause (2) will produce exactly what's defined in the actual type parameter
c. Clause (3) will produce <Object> test3 <Object, List <Object>>
Questions:
- Why clause (1) didn't produce <Object>? Since <Object> works as shown in clause (2), why <? extends Object> being produce instead?
- why clause (3) produce <Object> instead of <? extends Object>?
- Since clause (4) uses the same variable, why 2 different type capture generated eventhough the parameter used is of the same variable d?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是三个问题中最好的一个。我的想法是,编译器/Eclipse 不想假设
Object
必然是在String
和之间推断出的
,所以它是安全的。正如 @bringer128 指出的,T
类型>整数String
和Integer
也都实现了Serialized
和Comparable
- 因此这些类型也是该方法的推断类型的候选类型。值得注意的是,以下代码给出了编译器错误“非法类型开始”:
这是因为将通配符指定为方法的类型参数是无效的。因此,您在工具提示中看到的事实与编译器/Eclipse 报告此信息的功能的微妙有关 - 它仅确定
T
在其范围内,而不是它是什么。请记住,Java 的泛型实现只是为了程序员的方便/理智。一旦编译成字节码,类型擦除将被消除
T
的任何概念。因此,在检查时,编译器只需要确保可以推断出a有效的T
,但不一定推断出它是什么。因为在这种情况下,在需要
List
的地方传入List
编译器假设 d 实际上引用同一个对象是不安全的,即使在评估参数之间也是如此。例如:
在本例中,
List
将传递到第一个参数,List
将传递到第二个参数 - 均来自d
。由于这种情况是可能的,因此编译器更容易安全行事。This is the best question out of the three. My thinking is that the compiler/Eclipse doesn't want to assume that
Object
is necessarily the typeT
that is inferred betweenString
andInteger
, so it plays it safe. As @bringer128 pointed out,String
andInteger
also both implementSerializable
andComparable
- so these types are also candidates for the inferred type of the method.It's worth noting that the following code gives the compiler error "illegal start of type":
This is because it's invalid to specify a wildcard as a method's type parameter. So the fact you're seeing that in the tooltip has to do with a subtlety of the compiler's/Eclipse's facility to report this information - it has determined only that
T
is within its bounds, not what it is.Remember that Java's implementation of generics is solely for the convenience/sanity of programmers. Once compiled into bytecode, type erasure will have gotten rid of any notion of
T
. So in its checking, the compiler only needs to ensure that a validT
can be inferred, but not necessarily what it is.Because in this case, the fact that a
List<Object>
is passed in where aList<T>
is expected tells the compiler thatT
is exactlyObject
.It isn't safe for the compiler to assume that
d
actually refers to the same object, even between evaluating parameters. For example:In this case, a
List<Integer>
would be passed into the first parameter, and anList<String>
into the second - both fromd
. Since this scenario is possible, it's easier for the compiler to play it safe.test1() 的情况实际上是相当险恶的。参见 JLS3 15.12.2.7。
我们不应该知道类型推断的细节——在大多数情况下,直觉与算法是一致的。可惜情况并非总是如此,就像看似微不足道的 test1() 示例一样。
我们的约束是 T :> String 和
T :> Integer
(“:>
”表示超类型)这导致
T=lub(String,Integer)
,lub
表示“最小上限”。由于
String <: Comparable
和Integer <: Comparable
,这导致lci({Comparable, Comparable;})
,这会产生Comparable
,即Compable
最后,我们有
T = Serialized &兼容
,一个自引用定义! Spec 称之为“无限类型”:让我们看看 javac 是如何表示它的: (javac 7)
这似乎不对;它并不是真正的递归;看来 javac 检测到
lub()
中的递归并放弃,导致不太具体的类型Comparable
The test1() case is actually quite sinister. see JLS3 15.12.2.7.
We are not supposed to know the details of type inference - in most cases intuition coincides with the algorithm. Alas that's not always the case, as in the seemingly trivial test1() example.
The constraints we have is
T :> String
andT :> Integer
( ":>
" means super type)This leads to
T=lub(String,Integer)
,lub
means "least upper bound".Since
String <: Comparable<String>
andInteger <: Comparable<Integer>
, this leads tolci({Comparable<String>, Comparable<Integer>})
, which yieldsComparable<? extends lub(String,Integer)>
, i.e.Compable<? extends T>
In the end, we have
T = Serializable & Compable<? extends T>
, a self referenced definition! Spec calls it "infinite type":Let's find out how javac represents it: (javac 7)
That doesn't seems right; it's not really recursive; it appears that javac detects recursion in
lub()
and gives up, resulting a less specific typeComparable<?>