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

时间:2018-02-13 10:34:21

标签: 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;
    }
}

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

1 个答案:

答案 0 :(得分:1)

  

我很难理解为什么以下代码没有   在Java 8/9中编译

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

根据元素的自然顺序,将指定列表按升序排序。 列表中的所有元素都必须实现Comparable接口

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

如果 compareTo()方法的参数是(例如)InferenceObject<Float>,并且要排序的List由(例如)组成,则编译器错误的原因会更清楚InferenceObject<LocalDate>个对象。同样的原则适用于初始代码清单,尽管原因不太明显。

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

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

  

我很困惑这个代码在Eclipse中编译的原因

我也是。它看起来像Eclipse编译器的一个错误。你可以Javadoc for Collections.sort()

  

在没有明确说明的情况下编写此代码的正确方法是什么   一个类型而不是通配符通用

您已经在修改后的代码中使用了正确的方法。您要么创建类型为InferenceObject<?>的对象列表(即类型未知),要么您要创建类型为InferenceObject<T>的对象列表,其中&#39; T&#39;是一些特定的类型。这是一个二元选择。

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

  

不使用略有不同的解决方法

不要将其视为&#34;解决方法&#34;。将其视为正确编写代码的先决条件。

但是对你的代码另一个值得改变的是声明 simpleList ,因为你知道你想要对它的元素进行排序:

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

如果您尝试向List添加无效的InferenceObject,编译器将标记错误:

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