[英]eclipse compiler compiles code that javac will not - code looks to be legal
The following code compiles in Eclipse Version: Mars.2 Release (4.5.2) Build id: 20160218-0600 on Windows 10 Version 10.0.14393. 以下代码在Eclipse Version中编译:Mars.2 Release(4.5.2)在Windows 10版本10.0.14393上构建id:20160218-0600。 It does not compile on a range of oracle javac compilers.
它不能在一系列oracle javac编译器上编译。 There are other questions concerning pieces of code that compile with the Eclipse JDT compiler, but not javac.
关于使用Eclipse JDT编译器编译的代码片段还有其他问题,但不是javac。 I could not find one similar to this example.
我找不到类似于这个例子的一个。 This is a piece of toy code, and it's only purpose is to demonstrate this curiosity.
这是一个玩具代码,它的唯一目的是展示这种好奇心。
Is the eclipse compiler correct to compile this? eclipse编译器是否正确编译?
Note: If the eclipse compiler generated .class is de-compiled it produces source which can be compiled with javac. 注意:如果eclipse编译器生成的.class被解编译,它会生成可以用javac编译的源代码。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Puzzle {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) {
List<List> outer = Arrays.asList(Arrays.asList(new Object(), new Object()),
Arrays.asList(),
Arrays.asList(new Object(), new Object()));
Stream<Boolean> bs1 = outer.stream().flatMap(inner -> inner.stream()).map(obj -> obj.hashCode() % 2 == 0);
boolean b0 = bs1.filter(b -> !b).findAny().isPresent();
boolean b2 = outer.stream().flatMap(inner->inner.stream())
.map(obj -> obj.hashCode() % 2 == 0)
.filter(b -> !b).findAny().isPresent();
System.out.printf("%s %s %s", outer, b0, b2);
}
}
Here is the compiler error, in several versions of the compiler: 这是编译器错误,在几个版本的编译器中:
C:\Users\tofti>C:\jdk1.8.0_121\bin\javac -version & C:\jdk1.8.0_121\bin\javac Puzzle.java
javac 1.8.0_121
Puzzle.java:23: error: bad operand type Object for unary operator '!'
.filter(b -> !b).findAny().isPresent();
^
1 error
C:\Users\tofti>C:\jdk1.8.0_112\bin\javac -version & C:\jdk1.8.0_112\bin\javac Puzzle.java
javac 1.8.0_112
Puzzle.java:23: error: bad operand type Object for unary operator '!'
.filter(b -> !b).findAny().isPresent();
^
1 error
C:\Users\tofti>C:\jdk1.8.0_141\bin\javac -version & C:\jdk1.8.0_141\bin\javac Puzzle.java
javac 1.8.0_141
Puzzle.java:23: error: bad operand type Object for unary operator '!'
.filter(b -> !b).findAny().isPresent();
^
1 error
And here is the code compiling, and executing in Eclipse: Code compiling and running in eclipse 这里是代码编译,并在Eclipse中执行: 代码编译和在eclipse中运行
One of the suppressed "unchecked" warnings reveals the problem, in Eclipse 4.6.3: 在Eclipse 4.6.3中,一个被抑制的“未经检查”的警告揭示了这个问题:
Type safety: The expression of type Stream needs unchecked conversion to conform to Stream<Boolean>
And in javac 1.8.0_131: 在javac 1.8.0_131中:
Puzzle.java:12: warning: [unchecked] unchecked conversion
Stream<Boolean> bs1 = outer.stream().flatMap(inner -> inner.stre
am()).map(obj -> obj.hashCode() % 2 == 0);
^
required: Stream<Boolean>
found: Stream
Without any casting, here are the actual return types from each method call in the chain: 没有任何转换,这里是链中每个方法调用的实际返回类型:
Stream<List> s1 = outer.stream();
Stream s2 = s1.flatMap(inner -> inner.stream());
Stream s3 = s2.map(obj -> obj.hashCode() % 2 == 0);
Stream s4 = s3.filter(b -> !b); // compiler error
Optional opt = s4.findAny();
boolean b = opt.isPresent();
If you add a wildcard to make it List<List<?>> outer
, this is what you get instead: 如果你添加一个通配符使它成为
List<List<?>> outer
,那么你得到的就是:
Stream<List<?>> s1 = outer.stream();
Stream<?> s2 = s1.flatMap(inner -> inner.stream());
Stream<Boolean> s3 = s2.map(obj -> obj.hashCode() % 2 == 0);
Stream<Boolean> s4 = s3.filter(b -> !b); // compiles OK
Optional<Boolean> opt = s4.findAny();
boolean b = opt.isPresent();
So the issue is that the .map
call on the raw Stream
doesn't actually return Stream<Boolean>
, you're just implicitly casting it when you assign bs1
, and that fixes the raw types in the rest of the chain. 所以问题是原始
Stream
上的.map
调用实际上并没有返回Stream<Boolean>
,你只是在分配bs1
时隐式地转换它,并且修复了链中其余部分的原始类型。 In fact, you can add this cast to the one-line version and it compiles: 实际上,您可以将此强制转换添加到单行版本并编译:
boolean b2 = ((Stream<Boolean>) outer.stream().flatMap(inner -> inner.stream())
.map(obj -> obj.hashCode() % 2 == 0))
.filter(b -> !b).findAny().isPresent();
Now, the raw .map
call is missing the class type parameter <T>
, but <R>
is a method type parameter, so why doesn't it return Stream<R>
? 现在,原始
.map
调用缺少类类型参数<T>
,但<R>
是方法类型参数,那么为什么不返回Stream<R>
? The raw types section in the Java language specification states that "a non-static type member of a raw type is considered raw". Java语言规范中的原始类型部分声明“原始类型的非静态类型成员被视为原始”。 The example given is a generic inner class, but assuming it applies to generic methods as well, this should be the reason for the
<R>
parameter of .map
getting dropped when it's called on a raw Stream
, causing it to return another raw Stream
. 给出的示例是一个通用内部类,但假设它也适用于泛型方法,这应该是
.map
的<R>
参数在原始Stream
上调用时被丢弃的原因,导致它返回另一个原始Stream
。
EDIT: Found this in an Eclipse bug report : 编辑:在Eclipse错误报告中找到:
It looks like javac infers the return type of the method invocation to be the raw type I when passed in arguments are raw types.
看起来javac将方法调用的返回类型推断为参数中传递的原始类型I是原始类型。 This may be motivated by the almost last bit of §18.5.2 after bounds set 4 has been generated
在生成边界集4之后,这可能是由§18.5.2的最后几位推动的
[...]
[...]
If unchecked conversion was necessary for the method to be applicable during constraint set reduction in §18.5.1, then the parameter types of the invocation type of m are obtained by applying θ' to the parameter types of m's type, and the return type and thrown types of the invocation type of m are given by the erasure of the return type and thrown types of m's type.
如果在第18.5.1节中约束集减少期间方法需要未经检查的转换 ,那么m的调用类型的参数类型是通过将θ'应用于m的类型的参数类型,以及返回类型和m的调用类型的抛出类型由返回类型的擦除和m类型的抛出类型给出。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.