简体   繁体   English

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

[英]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.

相关问题 Java CRTP和通配符:代码在Eclipse中编译,但不是`javac` - Java CRTP and Wildcards: Code compiles in Eclipse but not `javac` Java代码没有用&#39;javac&#39;编译,而是在Eclipse中编译 - Java code not compiling with 'javac' but compiles in Eclipse Java泛型代码使用javac编译,Eclipse Helios失败 - Java generics code compiles with javac, fails with Eclipse Helios 为什么这个代码用eclipse编译器编译但不用javac编译(maven) - why does this code compile with eclipse compiler but not with javac (maven) 使用 eclipse 但不使用 javac 编译的代码 - Code compiling with eclipse but not with javac groovy-eclipse-compiler 编译但 javac 编译失败 - groovy-eclipse-compiler compiles but javac compilation fails 项目如何在eclipse中编译,但javac会抛出编译器错误? - How is it possible that a project compiles in eclipse, but javac throws compiler errors? 此代码使用ecj但不使用javac编译。 这是ecj,javac或两者中的错误吗? - This code compiles using ecj but not javac. Is this a bug in ecj, javac or neither? 代码在Eclipse中编译,但不能在Javac中编译:带有功能子接口的咖喱lambda。 哪个是正确的? - Code compiles in Eclipse but not javac: curried lambdas with functional subinterface. Which is correct? 为什么eclipse编译shell javac或mvn产生错误的文件:代码太大? - Why eclipse compiles a file that shell javac or mvn produces an error: code too large?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM