以下代码在Eclipse Version中编译:Mars.2 Release(4.5.2)在Windows 10版本10.0.14393上构建id:20160218-0600。它不能在一系列oracle javac编译器上编译。关于使用Eclipse JDT编译器编译的代码片段还有其他问题,但不是javac。我找不到类似于这个例子的一个。这是一个玩具代码,它的唯一目的是展示这种好奇心。
eclipse编译器是否正确编译?
注意:如果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);
}
}
以下是编译器错误,在几个版本的编译器中:
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
以下是代码编译,并在Eclipse中执行: Code compiling and running in eclipse
答案 0 :(得分:4)
一个被抑制的“未经检查”的警告揭示了Eclipse 4.6.3中的问题:
Type safety: The expression of type Stream needs unchecked conversion to conform to Stream<Boolean>
在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
没有任何转换,以下是链中每个方法调用的实际返回类型:
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();
如果您添加通配符以使其成为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();
问题是,原始.map
上的Stream
调用实际上并未返回Stream<Boolean>
,您只是在分配bs1
时隐式投射它,并修复了链中其余部分的原始类型。实际上,您可以将此强制转换添加到单行版本并编译:
boolean b2 = ((Stream<Boolean>) outer.stream().flatMap(inner -> inner.stream())
.map(obj -> obj.hashCode() % 2 == 0))
.filter(b -> !b).findAny().isPresent();
现在,原始.map
调用缺少类类型参数<T>
,但<R>
是方法类型参数,为什么不返回Stream<R>
? raw types中的Java language specification部分声明“原始类型的非静态类型成员被视为原始”。给出的示例是一个通用内部类,但假设它也适用于泛型方法,这应该是<R>
.map
参数在原始Stream
上调用时被删除的原因},导致它返回另一个原始Stream
。
编辑:在an Eclipse bug report中找到了这个:
看起来javac将方法调用的返回类型推断为参数中传递的原始类型I是原始类型。在生成边界集4之后,这可能是由§18.5.2的几乎最后一位激发的
[...]
如果对于在§18.5.1中的约束集减少期间适用的方法需要未经检查的转换,则通过将θ'应用于参数来获得m的调用类型的参数类型m的类型类型,以及返回类型和m的调用类型的抛出类型是由删除返回类型和抛出类型的m类型给出。