Java 类型推断在编译时失败,但 Eclipse 编译并运行得很好

标签 java eclipse generics type-inference

我很难理解为什么以下代码不能在 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;
    }
}

在不明确声明类型而不是通配符泛型且不使用稍微不同的解决方法的情况下编写此代码的正确方法是什么?

最佳答案

I'm having a hard time to understand why the following code does not compile in Java 8 / 9

该代码无法编译(使用 1.8.0_161),因为您要求 Collections.sort() 对未正确实现 Comparable 接口(interface)的类型的对象进行排序。来自 Javadoc for Collections.sort() :

根据指定列表元素的自然顺序对指定列表进行升序排序。 列表中的所有元素都必须实现 Comparable 接口(interface)

在您的初始代码中,要排序的列表成员的类型为 InferenceObject<?>compareTo() 方法的参数类型为 InferenceObject<T> ,因此存在编译器错误是正确的。

如果 compareTo() 方法的参数是(例如)InferenceObject<Float>,则编译器错误的原因会更清楚。 ,要排序的列表包括(例如) InferenceObject<LocalDate>对象。尽管原因不太明显,但同样的原理也适用于初始代码 list 。

如果您的列表包含实现了Comparable的任何特定类型的InferenceObject,那么您最初的InferenceObject类定义就可以了。例如:

List<InferenceObject<String>> simpleList = new ArrayList<>(); List<InferenceObject<BigInteger>> simpleList = new ArrayList<>(); List<InferenceObject<Year>> simpleList = new ArrayList<>();

I'm confused as to why this code compiles in Eclipse

我也是。看起来像是 Eclipse 编译器的一个错误。你可以raise a bug report .

What is the correct way to write this code without explicitly stating a type instead of the wildcard generic

您已经在修改后的代码中以正确的方式进行了操作。您想要创建 InferenceObject<?> 类型的对象列表(即类型未知)或者您想要创建 InferenceObject<T> 类型的对象列表,其中“T”是某种特定类型。这是一个二元选择。

(实际上比这更微妙,因为你可以有 List<InferenceObject<? extends Number>> 但我不认为你在问这个。)

without using the slightly different workaround?

不要将其视为“解决方法”。将其视为正确编写代码的先决条件。

但是对代码的另一个有值(value)的更改是像这样声明 simpleList ,因为您知道要对其元素进行排序:

List<InferenceObject<? extends Comparable>> simpleList = new ArrayList<>();

如果您尝试将无效的 InferenceObject 添加到列表中,编译器将标记错误:

simpleList.add(new InferenceObject<String>()); // OK, since String implements Comparable
simpleList.add(new InferenceObject<StringBuffer>()); // Error, since StringBuffer does not implement Comparable

关于Java 类型推断在编译时失败,但 Eclipse 编译并运行得很好,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48764663/

相关文章:

java - 以 AM/PM 格式显示时间

java - Eclipse 光标变为十字准线

java - 在 Eclipse 中隐藏/限制/保护代码

java - hibernate c3p0 ThreadPoolExecutor 连接池,我做对了吗?

java - 为什么ant认为包不存在?

java - 找不到 PropertySource : PKIX path building failed for spring cloud config URL

java - 为什么 Eclipse 不能导出类​​路径中包含的 Maven 库?

c# - C# Expression<TDelegate> 的通用约束 - 无法隐式转换类型

Java:获取通用供应商返回对象的类类型

java - 将对象动态转换为特定类以使用函数