简体   繁体   English

Java 8类型推断导致在调用时省略泛型类型

[英]Java 8 type inference cause to omit generic type on invocation

I have a Problem with a generic method after upgrading to Java 1.8, which was fine with Java 1.6 and 1.7 Consider the following code: 升级到Java 1.8之后,我遇到了一个泛型方法的问题,这对于Java 1.6和1.7来说很好。请考虑以下代码:

public class ExtraSortList<E> extends ArrayList<E> {
    ExtraSortList(E... elements) {
        super(Arrays.asList(elements));
    }

    public List<E> sortedCopy(Comparator<? super E> c) {
        List<E> sorted = new ArrayList<E>(this);
        Collections.sort(sorted, c);
        return sorted;
    }

    public static void main(String[] args) {
        ExtraSortList<String> stringList = new ExtraSortList<>("foo", "bar");

        Comparator<? super String> compGen = null;
        String firstGen = stringList.sortedCopy(compGen).get(0); // works fine

        Comparator compRaw = null;
        String firstRaw = stringList.sortedCopy(compRaw).get(0); // compiler ERROR: Type mismatch: cannot convert from Object to String
    }
}

I tried this with the Oracle javac (1.8.0_92) and with Eclipse JDT (4.6.1) compiler. 我尝试使用Oracle javac(1.8.0_92)和Eclipse JDT(4.6.1)编译器。 It is the same result for both. 两者都是一样的结果。 (the error message is a bit different, but essentially the same) (错误消息有点不同,但基本相同)

Beside the fact, that it is possible to prevent the error by avoiding raw types, it puzzles me, because i don't understand the reason. 除了事实,可以通过避免原始类型来防止错误,它让我困惑,因为我不明白原因。

Why does the raw method parameter of the sortedCopy-Method have any effect on the generic type of the return value? 为什么sortedCopy-Method的raw方法参数对返回值的泛型类型有任何影响? The generic type is already defined at class level. 泛型类型已在类级别定义。 The method does not define a seperate generic type. 该方法未定义单独的泛型类型。 The reference list is typed to <String> , so should the returned List. 引用list键入<String> ,返回的List也应该键入。

Why does Java 8 discard the generic type from the class on the return value? 为什么Java 8会在返回值上丢弃类中的泛型类型?

EDIT: If the method signature of sortedCopy is changed (as pointed out by biziclop) to 编辑:如果sortedCopy的方法签名被更改(由biziclop指出)

public List<E> sortedCopy(Comparator c) {

then the compiler does consider the generic type E from the type ExtraSortList<E> and no error appears. 然后编译器会从ExtraSortList<E>类型中考虑泛型类型E ,并且不会出现错误。 But now the parameter c is a raw type and thus the compiler cannot validate the generic type of the provided Comparator. 但是现在参数c是原始类型,因此编译器无法验证提供的Comparator的泛型类型。

EDIT: I did some review of the Java Language Specification and now i think about, whether i have a lack of understanding or this is a flaw in the compiler. 编辑:我做了一些Java语言规范的评论,现在我想一想,我是否缺乏理解,或者这是编译器的一个缺陷。 Because: 因为:

  • Scope of a Declaration of the generic type E is the class ExtraSortList , this includes the method sortedCopy . 泛型类型E 的声明范围ExtraSortList类,其中包括sortedCopy方法。
  • The method sortedCopy itself does not declare a generic type variable, it just refers to the type variable E from the class scope. 方法sortedCopy本身不声明泛型类型变量,它只是从类作用域引用类型变量E see Generic Methods in the JLS 请参阅JLS中的通用方法
  • The JLS also states in the same section JLS也在同一部分中说明

    Type arguments may not need to be provided explicitly when a generic method is invoked, as they can often be inferred (§18 (Type Inference)). 调用泛型方法时,可能不需要显式提供类型参数,因为它们通常可以被推断出来(§18(类型推断))。

  • The reference stringList is defined with String , thus the compiler does not need to infer a type for E on the invocation of sortedCopy because it is already defined. 引用stringList是使用String定义的,因此编译器不需要在调用sortedCopy推断E的类型,因为它已经定义。
  • Because stringList already has a reified type for E , the parameter c should be Comparator<? super String> 因为stringList已经具有E的具体类型,所以参数c应该是Comparator<? super String> Comparator<? super String> for the given invocation. 给定调用的Comparator<? super String>
  • The return type should also use the already reified type E , thus it should be List<String> . 返回类型也应该使用已经提到的类型E ,因此它应该是List<String>

This is my current understanding of how i think the Java compiler should evaluate the invocation. 这是我目前对我认为Java编译器应如何评估调用的理解。 If i am wrong, an explanation why my assumptions are wrong would be nice. 如果我错了,解释为什么我的假设是错误的将是很好的。

To bring an final answer to why this happens: 为了解决这个问题的最终答案:

Like @Jesper mentioned already, you're using raw types when you shouldn't (Especially when using the Generic as type in multiple cases). 就像@Jesper已经提到的那样,你不应该使用原始类型(特别是在多种情况下使用Generic作为类型)。 Since you pass an Comparator without an Generic-Type, there will actually be none. 由于您传递的是没有Generic-Type的Comparator,实际上没有。 You could think of the E-Generic as null to make it easier. 您可以将E-Generic视为null以使其更容易。 Therefore your code becomes to this: 因此,您的代码变为:

public List sortedCopy(Comparator c) {
    List sorted = new ArrayList(this);
    Collections.sort(sorted, c);
    return sorted;
}

Now you're attemptig/assuming you get an String from an List without Generics and therefore an Object (hence it's the super-class of everything ). 现在你正在尝试/假设你从没有泛型的List中得到一个String,因此得到一个Object(因此它是所有东西的超类)。

To the question why the raw-type parameter has no effect on the return type, since you don't specify an certain Level of abstraction. 关于为什么raw-type参数对返回类型没有影响的问题,因为你没有指定某个抽象级别。 You'd have to define an Type that the Generic has to extend/implement at least to make that happen (compilation errors), for example. 例如,您必须定义Generic必须扩展/实现的类型,以至少实现这一点(编译错误)。

public class ExtraSortList<E extends String> extends ArrayList<E> {

will now only allow Strings or Classes which extend it (not possible here since string is final). 现在只允许扩展它的字符串或类(这里不可能,因为字符串是最终的)。 With that, your fallback Type would be String. 有了它,你的后备类型将是String。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM