Java类型推断在编译时失败,但Eclipse编译并运行良好
我很难理解为什么以下代码没有在Java 8/9中编译,但如果我在Eclipse IDE中“运行”,它就可以正常工作:
package de.playground.test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class InferenceTest {
class InferenceObject<T> implements Comparable<InferenceObject<T>> {
@Override
public int compareTo(InferenceObject<T> o) {
return 0;
}
}
public static void main(String[] args) {
InferenceTest inferenceTest = new InferenceTest();
List<InferenceObject<?>> result = inferenceTest.read();
System.out.println(String.format("Array contains %d element(s)", result.size()));
}
private List<InferenceObject<?>> read() {
List<InferenceObject<?>> simpleList = new ArrayList<>();
InferenceObject<String> infObj = new InferenceObject<>();
simpleList.add(infObj);
// Collections.<InferenceObject<?>>sort(simpleList);
Collections.sort(simpleList);
return simpleList;
}
}
javac InferenceTest.java
产生以下错误:
InferenceTest.java:26: error: no suitable method found for sort(List<InferenceTest.InferenceObject<?>>)
Collections.sort(simpleList);
^
method Collections.<T#1>sort(List<T#1>) is not applicable
(inference variable T#1 has incompatible bounds
equality constraints: InferenceTest.InferenceObject<?>
upper bounds: InferenceTest.InferenceObject<CAP#1>,Comparable<? super T#1>)
method Collections.<T#2>sort(List<T#2>,Comparator<? super T#2>) is not applicable
(cannot infer type-variable(s) T#2
(actual and formal argument lists differ in length))
where T#1,T#2 are type-variables:
T#1 extends Comparable<? super T#1> declared in method <T#1>sort(List<T#1>)
T#2 extends Object declared in method <T#2>sort(List<T#2>,Comparator<? super T#2>)
where CAP#1 is a fresh type-variable:
CAP#1 extends Object from capture of ?
1 error
问题是,由于使用了通配符泛型,类型推断失败了——我不明白为什么这段代码在Eclipse中编译,而Oracle JDK 8/9拒绝编译这段代码——这可能是Bug 6468354的回归吗
使用环境:Kubuntu 17.10 x86_64、javac 1.8.0、javac 1.8.0_131、javac 1.8.0_151和javac 9.0.1
解决方法:
class InferenceObject<T> implements Comparable<InferenceObject<?>> {
@Override
public int compareTo(InferenceObject<?> o) {
return 0;
}
}
在不显式地声明类型而不是通配符泛型并且不使用稍微不同的解决方法的情况下,编写此代码的正确方法是什么
# 1 楼答案
该代码不会编译(使用1.8.0_161),因为您要求的是集合。sort()对未正确实现可比接口的类型的对象进行排序。从Javadoc for Collections.sort()开始:
根据元素的自然顺序,将指定列表按升序排序列表中的所有元素都必须实现可比较的接口
在初始代码中,要排序的列表成员的类型为
InferenceObject<?>
,但compareTo()方法的参数的类型为InferenceObject<T>
,因此出现编译器错误是正确的如果compareTo()方法的参数是(比如)
InferenceObject<Float>
,并且要排序的列表由(比如)InferenceObject<LocalDate>
个对象组成,那么编译器错误的原因就会更清楚。同样的原则也适用于初始代码清单,尽管原因不太明显如果您的列表包含实现了可比较的任何特定类型的推断对象,那么您最初的推断对象类定义就可以了。例如:
List<InferenceObject<String>> simpleList = new ArrayList<>();
List<InferenceObject<BigInteger>> simpleList = new ArrayList<>();
List<InferenceObject<Year>> simpleList = new ArrayList<>();
我也是。它看起来像是Eclipse编译器的一个bug。你可以raise a bug report
在修改后的代码中,您已经以正确的方式进行了操作。您可以创建
InferenceObject<?>
(即类型未知)类型的对象列表,也可以创建InferenceObject<T>
类型的对象列表,其中“T”是某种特定类型。这是一个二元选择(事实上,这有点微妙,因为你可以有一个
List<InferenceObject<? extends Number>>
,但我不认为你在问这个。)不要认为这是一个“变通办法”。将其视为正确编写代码的先决条件
但对代码的另一个有价值的更改是像这样声明simpleList,因为您知道要对其元素进行排序:
如果您试图向列表中添加无效的推断对象,编译器将标记错误: