是否应在Java 1.8下编译以下代码

Spa*_*ker 14 java eclipse jls language-lawyer java-8

给出以下课程:

public class FooTest {

    public static class Base {
    }

    public static class Derived extends Base {
    }

    public interface Service<T extends Base> {
        void service(T value);
    }

    public abstract class AbstractService<T extends Derived> implements  Service<T> {
        public void service(T value) {
        }
    }

    private AbstractService service;

    public void bar(Base base) {
        if(base instanceof Derived) {
            service.service(base); // compile error at this line
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用以下内容构建类时pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mgm-tp</groupId>
    <artifactId>java-compiler-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.3</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <compilerId>eclipse</compilerId>
                    </configuration>
                    <dependencies>
                        <dependency>
                            <groupId>org.codehaus.plexus</groupId>
                            <artifactId>plexus-compiler-eclipse</artifactId>
                            <version>2.5</version>
                        </dependency>
                    </dependencies>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>
Run Code Online (Sandbox Code Playgroud)

在maven 3.4中,它产生以下编译错误:

[错误]无法执行目标org.apache.maven.plugins:maven-compiler-plugin:3.3:在项目java-compiler-test上编译(default-compile):编译失败[错误] C:\ Users\abrieg\workingcopy\java-compiler-test\src\main\java\FooTest.java:[25] FooTest.Service类型中的方法服务(FooTest.Base)不适用于参数(FooTest.Base)

当为eclipse编译器将源和目标级别设置为1.7或者当使用javac编译器时,没有报告编译错误.

问题是JLS 1.8对类型推断更具体,因此这个代码实际上不被eclipse编译器为java 1.8所假设,或者这是eclipse编译器中的回归.

根据编译器错误的文本,我倾向于说它是回归,但我不确定.

我已经确定已向jdt报告了以下两个错误,但我认为它们并不完全适用:
https : //bugs.eclipse.org/bugs/show_bug.cgi?id = 432603 https://bugs.eclipse.org/错误/ show_bug.cgi?ID = 430987

如果这是一个回归,这已经报告给jdt?

Hol*_*ger 2

据我了解,这段代码应该可以编译,但当然不是没有未经检查的警告。

\n\n

您已经声明了一个原始类型service的变量,它是原始类型的子类型,它有一个擦除 的方法。 AbstractService Servicevoid service(Base)void\xc2\xa0service(T)

\n\n

因此,调用可能会调用在 中声明service.service(base)的方法,当然,会出现未经检查的警告,因为该方法是通用的,并且没有发生类型参数的验证。void service(Base)ServiceT

\n\n

这可能是违反直觉的,因为类型使用AbstractService擦除的方法覆盖该方法void service(Derived),但该方法只能在通用上下文中覆盖其他方法,而不能在原始类型中覆盖继承关系中覆盖。

\n\n

或者,换句话说,类型可以\xe2\x80\x99t 重写方法,其方式是在参数类型方面比重写的超类型方法更具限制性。

\n\n

这也适用于泛型类型继承,但结果不同。如果您的变量具有类型AbstractService<X>,则由于类型参数的约束,X必须可分配给。Derived该类型AbstractService<X>是其子类型,Service<X>它有一个方法void\xc2\xa0service(X)(如T:= ),该方法被接受相同参数类型的方法X覆盖(实现) 。AbstractService<X>void\xc2\xa0service(X)

\n\n
\n\n

由于您的网站上似乎存在一些混乱,我想强调这与您的陈述无关if(\xe2\x80\xa6 instanceof Derived)。如上所述,此行为是由于原始类型使用造成的,这意味着您正在使用AbstractService,并且基本上关闭了泛型类型检查。如果你写了,这甚至可以工作

\n\n
public void bar(Base base) {\n    service.service(base); // UNCHECKED invocation\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您将变量的声明更改为

\n\n
private AbstractService<Derived> service;\n
Run Code Online (Sandbox Code Playgroud)\n\n

它不再是原始类型,并且类型检查将会发生,并且service.service(base)会生成编译器错误,无论您是否将其括起来if(base instanceof Derived) { \xe2\x80\xa6 }是否将其括起来。

\n\n

原始类型的存在只是为了与泛型之前的代码兼容,您应该避免使用它们,并且不要忽略原始类型引发的警告使用引起的警告。

\n

  • @SpaceTrucker:请仔细阅读我答案的第一部分并尝试理解。并不是任何编译器都知道“base”包含“Derived”的实例。相反,“Service.service(Base)”方法将被调用,因为由于“原始类型”的使用,“这不应该是可能的”信息“已被删除”。 (2认同)