为什么它是用Java7编译的,而不是用Java8编译的?

为什么它是用Java7编译的,而不是用Java8编译的?,java,generics,compiler-errors,java-7,java-8,Java,Generics,Compiler Errors,Java 7,Java 8,仿制药很棘手。 看起来它们在不同版本的Java中受到不同的对待 这段代码在Java7中编译成功,但在Java8中编译失败 import java.util.EnumSet; public class Main { public static void main(String[] args) { Enum foo = null; tryCompile(EnumSet.of(foo)); } static <C extends Enum<C> &am

仿制药很棘手。 看起来它们在不同版本的Java中受到不同的对待

这段代码在Java7中编译成功,但在Java8中编译失败

import java.util.EnumSet;

public class Main {
  public static void main(String[] args) {
    Enum foo = null;
    tryCompile(EnumSet.of(foo));
  }

  static <C extends Enum<C> & Another> void tryCompile(Iterable<C> i) {}

  static interface Another {}
}
import java.util.EnumSet;
公共班机{
公共静态void main(字符串[]args){
枚举foo=null;
tryCompile(枚举集(foo));
}
静态void tryCompile(Iterable i){}
静态接口另一个{}
}
下面是来自Java8的错误消息。我用这个来编译它:

/tmp/java_A7GNRg/Main.java:6:错误:类Main中的方法trycomfile无法应用于给定类型;
tryCompile(枚举集(foo));
^
必填项:Iterable
找到:枚举集
原因:推断类型不符合上限
推断:枚举
上限:枚举,另一个
其中C是一个类型变量:
C扩展Enum,方法tryCompile(Iterable)中声明的另一个Enum
/tmp/java_A7GNRg/Main.java:6:警告:[未选中]未选中的方法调用:类内枚举集的方法应用于给定类型
tryCompile(枚举集(foo));
^
所需:E
找到:枚举
其中E是一个类型变量:
E扩展了(E)方法中声明的枚举
1错误
1警告

问题在于Java编译器版本之间的差异。

在我看来,这是一个正确的错误:

原因:推断类型不符合上限
推断:枚举
上限:枚举,另一个

EnumSet.of(foo)
将具有类型
EnumSet
,该类型与
C扩展Enum和另一个
不兼容,原因与
Set
Set不兼容Java 8中的类型推断引擎已改进,并且(我假设)现在可以确定
C
类型没有扩展另一个
类型

在Java7中,类型推断系统无法或不费事确定
另一个
类型是否丢失,这给了程序员怀疑的好处(在编译时)

在Java7中,如果在运行时调用
另一个
接口上的方法,您仍将在运行时为违规行为付费

例如,此代码:

import java.util.EnumSet;

public class Main {

  static enum Foo {
    BAR
  }

  public static void main(String[] args) {
    Enum foo = Foo.BAR;
    tryCompile(EnumSet.of(foo));
  }

  static <C extends Enum<C> & Another> void tryCompile(Iterable<C> i) {
    i.iterator().next().doSomething();
  }

  static interface Another {
    void doSomething();
  }
}
尽管Java 7编译器将编译代码,但它仍然会对原始类型和未检查的调用发出警告,这应该会提醒您某些错误


下面是一个非常简单的示例,它不使用枚举,而是以显示相同问题的
enum
的定义为模型。在Java 7中编译时带有警告,但在Java 8中没有:

import java.util.Collections;
import java.util.List;

public class Main {

  static class Foo<T extends Foo<T>> {
  }

  static class FooA extends Foo<FooA> {
  }

  public static <T extends Foo<T>> List<T> fooList(T e) {
    return Collections.singletonList(e);
  }


  public static void main(String[] args) {
    Foo foo = new FooA();
    tryCompile(fooList(foo));
  }

  static <C extends Enum<C> & Another> void tryCompile(Iterable<C> i) {
    i.iterator().next().doSomething();
  }

  static interface Another {
    void doSomething();
  }
}
import java.util.Collections;
导入java.util.List;
公共班机{
静态类Foo{
}
静态类FooA扩展了Foo{
}
公共静态列表傻瓜(TE){
返回集合。单音列表(e);
}
公共静态void main(字符串[]args){
Foo-Foo=新的FooA();
tryCompile(傻瓜(foo));
}
静态孔隙tryCompile(Iterable i){
i、 迭代器().next().doSomething();
}
另一个静态接口{
无效剂量();
}
}

因此,这不是一个
Enum
特定的问题,但可能是因为涉及到递归类型。

这对我来说在Eclipse标准/SDK版本中编译得很好:Luna发行版(4.4.0)构建id:20140612-0600,带有Eclipse JDT(Java开发工具)补丁和Java 8支持(针对开普勒SR2)1.0.0.v20140317-1956 org.eclipse.jdt.java8patch.feature.group eclipse.org已安装


我收到一些警告(foo上的原始类型和tryCompile上的未检查调用。

Java 7和Java 8之间的主要区别在于目标类型推断。虽然Java 7只考虑方法调用的参数来确定类型参数,但Java 8将使用表达式的目标类型,即嵌套方法invoca的参数类型初始化或赋值给的变量类型,或者在
返回
语句的情况下方法的返回类型

例如,在编写时,
List List=Arrays.asList(1,2,3,4);
,Java 7将通过查看方法的参数来推断右侧的类型
List
,并生成一个错误,而Java 8将使用目标类型
List
来推断方法参数必须是
Number
的实例这一约束,因此在Java 8中是合法的

如果你对正式的细节感兴趣,你可以研究一下,尤其是,这不是一个容易的阅读

那么当您说
enumfoo=null;trycomfile(EnumSet.of(foo));
时会发生什么呢

在Java7中,表达式
EnumSet.of(foo)的类型
将通过查看参数的类型来推断,
foo
是原始类型
Enum
,因此将执行未选中的操作,结果类型是原始类型
EnumSet
。此类型实现原始类型
Iterable
,因此可以传递到
trycomfile
,形成一个r未检查的操作

在Java8中,
EnumSet.of(foo)的目标类型
trycomfile
的第一个参数的类型,它是
Iterable
,因此在Java 7
EnumSet中不需要太多细节。of
将被视为原始类型调用,因为它有一个原始类型参数;在Java 8中,它将被视为泛型调用,因为它有一个泛型目标类型将其作为泛型调用,编译器将得出以下结论:找到了类型(
Enum
)与所需的类型
C extends Enum&other
不兼容。虽然您可以将原始类型
Enum
分配给
C extends Enum
,并带有未选中的警告,但它将被视为与另一个
不兼容(没有类型转换)

您确实可以插入这样的类型转换:

Enum foo = null;
tryCompile(EnumSet.of((Enum&Another)foo));
这当然不是没有u
Exception in thread "main" java.lang.ClassCastException: Main$Foo cannot be cast to Main$Another
    at Main.tryCompile(Main.java:16)
    at Main.main(Main.java:12)
import java.util.Collections;
import java.util.List;

public class Main {

  static class Foo<T extends Foo<T>> {
  }

  static class FooA extends Foo<FooA> {
  }

  public static <T extends Foo<T>> List<T> fooList(T e) {
    return Collections.singletonList(e);
  }


  public static void main(String[] args) {
    Foo foo = new FooA();
    tryCompile(fooList(foo));
  }

  static <C extends Enum<C> & Another> void tryCompile(Iterable<C> i) {
    i.iterator().next().doSomething();
  }

  static interface Another {
    void doSomething();
  }
}
Enum foo = null;
tryCompile(EnumSet.of((Enum&Another)foo));
Enum foo = null;
EnumSet set = EnumSet.of(foo);
tryCompile(set);