Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
158 views
in Technique[技术] by (71.8m points)

java - Why does this compile in Java7 and does not in Java8?

Generics are tricky. And looks like they are treated differently in different versions of Java.

This code successfully compiles in Java 7 and fails to compile with Java 8.

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 {}
}

Here is an error message from Java 8. I used this one to compile it: http://www.compilejava.net/

/tmp/java_A7GNRg/Main.java:6: error: method tryCompile in class Main cannot be applied to given types;
    tryCompile(EnumSet.of(foo));
    ^
  required: Iterable<C>
  found: EnumSet
  reason: inferred type does not conform to upper bound(s)
    inferred: Enum
    upper bound(s): Enum<Enum>,Another
  where C is a type-variable:
    C extends Enum<C>,Another declared in method <C>tryCompile(Iterable<C>)
/tmp/java_A7GNRg/Main.java:6: warning: [unchecked] unchecked method invocation: method of in class EnumSet is applied to given types
    tryCompile(EnumSet.of(foo));
                         ^
  required: E
  found: Enum
  where E is a type-variable:
    E extends Enum<E> declared in method <E>of(E)
1 error
1 warning

The question is about the difference between versions of Java compiler.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The main difference between Java?7 and Java?8 is the target type inference. While Java?7 only considers the parameters of a method invocation to determine the type arguments, Java?8 will use the target type of an expression, i.e. the parameter type in case of a nested method invocation, the type of the variable that is initialized or assigned to, or the method’s return type in case of a return statement.

E.g. when writing, List<Number> list=Arrays.asList(1, 2, 3, 4);, Java?7 will infer the type List<Integer> for the right hand side by looking at the method’s arguments and generate an error while Java?8 will use the target type List<Number> to infer the constraint that the method arguments must be instances of Number which is the case. Therefore, it is legal in Java?8.

If you are interested in the formal details, you may study the “Java Language Specification, Chapter 18. Type Inference”, especially §18.5.2. Invocation Type Inference, however, that’s not easy reading…

So what happens when you say Enum foo = null; tryCompile(EnumSet.of(foo));?

In Java?7 the type of the expression EnumSet.of(foo) will be inferred by looking at the type of the argument, foo which is the raw type Enum, hence an unchecked operation will be performed and the result type is the raw type EnumSet. This type implements the raw type Iterable and hence can be passed to tryCompile forming another unchecked operation.

In Java?8 the target type of EnumSet.of(foo) is the type of the first parameter of tryCompile which is Iterable<C extends Enum<C> & Another>, so without going too much into details, in Java?7 EnumSet.of will be treated as raw type invocation because it has a raw type argument, in Java?8 it will be treated as generic invocation because it has a generic target type. By treating it as as a generic invocation, the compiler will conclude that the type found (Enum) is not compatible to the required type C extends Enum<C> & Another. While you could get away with assigning the raw type Enum to C extends Enum<C> with an unchecked warning, it will considered to be incompatible with Another (without a type-cast).

You can indeed insert such a cast:

Enum foo = null;
tryCompile(EnumSet.of((Enum&Another)foo));

This compiles, of course not without an unchecked warning due to the assignment of Enum to C?extends?Enum<C>.

You can also dissolve the target type relationship so that the same steps as in Java?7 are performed:

Enum foo = null;
EnumSet set = EnumSet.of(foo);
tryCompile(set);

Here, raw types are used throughout the three lines so this compiles with unchecked warnings and the same ignorance about the implements Another constraint as in Java?7.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...