Если следующий код компилируется под Java 1.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
        }
    }
}

При создании класса со следующим 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>

в maven 3.4 он производит следующую ошибку компиляции:

[ERROR] Не удалось выполнить цель org.apache.maven.plugins: maven-compiler-plugin: 3.3: компиляция (компиляция по умолчанию) в проекте java-compiler-test: сбой компиляции [ERROR] C:\Users\abrieg\workcopy\java-compiler-test\src\main\java\FooTest.java: [25] Служба метода (FooTest.Base) в типе FooTest.Service неприменима для аргументы (FooTest.Base)

При настройке источника и целевого уровня на 1.7 для компилятора eclipse или при использовании javac в качестве компилятора не сообщается об ошибке компиляции.

Вопрос заключается в том, что JLS 1.8 более специфичен в отношении вывода типа, так что этот код действительно не допускается, как предполагалось компилятором eclipse для java 1.8, или если это регрессия в компиляторе eclipse.

Основываясь на тексте ошибки компилятора, я склонен сказать, что это регрессия, но я не уверен.

Я определил следующие две ошибки, уже сообщаемые jdt, но я думаю, что они не применяются точно:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=432603 https://bugs.eclipse.org/bugs/show_bug.cgi?id=430987

Если это регрессия, это уже сообщалось в jdt?

Ответы

Ответ 1

Насколько я понимаю, этот код должен компилироваться, но, конечно, не без непроверенного предупреждения.

Вы указали переменную service необработанного типа AbstractService, которая является подтипом необработанного типа service, который имеет метод void service(Base), который является стиранием void service(T).

Таким образом, вызов service.service(base) может вызывать этот метод void service(Base), объявленный в service, конечно, с непроверенным предупреждением, поскольку метод является общим и не проверяет параметр типа T.

Это может быть противоречивым, поскольку тип AbstractService переопределяет этот метод с методом, стирание которого void service(Derived), но этот метод может переопределять другой метод в общем контексте, а не в отношении отношения типа исходного типа.

Или, другими словами, тип can not переопределяет метод таким образом, что он более ограничивает типы параметров, чем переопределенный метод супертипа.

Это также относится к наследованию общего типа, но к другому результату. Если ваша переменная имела тип AbstractService<X>, то X должен быть назначен Derived из-за ограничения параметра типа. Этот тип AbstractService<X> является подтипом Service<X>, который имеет метод void service(X) (as T: = X), который переопределяется (реализуется) AbstractService<X> с помощью метода void service(X), который принимает тот же типы аргументов.


Так как на вашем сайте существует некоторая путаница, я хочу подчеркнуть, что это не имеет никакого отношения к вашему оператору if(… instanceof Derived). Как объяснялось выше, это поведение связано с использованием типа raw, что означает, что вы используете AbstractService без фактического аргумента типа и в основном отключите проверку типа Generics. Это будет даже работать, если вы написали

public void bar(Base base) {
    service.service(base); // UNCHECKED invocation
}

Если вы изменили объявление переменной на

private AbstractService<Derived> service;

он больше не будет сырым типом, и проверка типа произойдет, а service.service(base) создаст ошибку компилятора, независимо от того, заключите ли вы ее с помощью if(base instanceof Derived) { … } или нет.

Существуют только типы сырья для совместимости только с кодом Pre-Generics, и вы должны избегать их использования и не игнорировать предупреждения, вызванные использованием типа raw.

Ответ 2

Это, конечно, о типах

Как говорили другие, вам нужно отбросить базу до Derived, чтобы она удовлетворяла требованиям к сервису (значение T), поскольку компилятор знает, что этот аргумент для AbstractService.service должен расширять Derived, и поэтому, если он не является производным, он не может соответствовать.

К сожалению, это избавляет от ошибки компилятора, но это не устраняет проблему. поэтому вы получаете предупреждение о безопасности типа.

Из-за определения AbstractService, service на самом деле AbstractService<? extends Derived>, и если вы устраните это упущение, вы просто получите сообщение об ошибке.

Это связано с тем, что AbstractService<? extends Derived> не расширяет AbstractService<Derived>, а база casting до Derived исправляет проблему для AbstractService<Derived>.

Посмотрите на классы ниже, чтобы увидеть пример этого. ConcreteService не расширяется AbstractService<Derived>, он расширяет AbstractService<RealDerived>. Вы не можете передать только Derived в ConcreteService.service(RealDerived base), например, вы не можете передать объект FalseDerived, потому что это не удовлетворяет типу аргумента.

Истина заключается в том, что где-то что-то должно действительно определять, что такое T, чтобы мы могли исправить проблему безопасности типа.

Выполнение этого в общем случае требует некоторого обмана, такого как класс, который связывает фактический тип аргумента с фактическим типом службы, так что кастинг может выполняться с помощью вида typeafe.

Как следует

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 static class AbstractService<T extends Derived> implements  Service<T> {

        public void service(T value) {
        }

    }

    // The following class defines an argument B that and ties together the type of the service with the type of the referenced stored class which enables casting using the class object 
    public abstract static class Barrer<B extends Derived>{

        private AbstractService<B> service;

        private Class<B> handledClass;


        protected Barrer(Class<B> handledClass, AbstractService<B> service){
            this.handledClass = handledClass;
            this.service = service;
        }

        public  void bar(Base base) {
            if(handledClass.isAssignableFrom(base.getClass())) {
                service.service(handledClass.cast(base)); // compile error at this line
            }
        }

    }

// the following classes provide concrete implementations and the concrete class to perform the casting.   

    public static class RealDerived extends Derived{}

    public static class FalseDerived extends Derived{}

    public static class ConcreteService extends AbstractService<RealDerived>{
    }


    public static class ConcreteBarrer extends Barrer<RealDerived> {
        protected ConcreteBarrer() {
            super(RealDerived.class,new ConcreteService());
        }

    }
}