eclipse编译器编译javac不会编写的代码 - 代码看起来是合法的

时间:2017-08-17 02:53:41

标签: java eclipse generics java-stream

以下代码在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

1 个答案:

答案 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类型给出。