Фабричный метод, возвращающий Builder, не компилируется с обобщениями.Что случилось? - PullRequest
1 голос
/ 22 июня 2011

Я пытаюсь создать API, где клиент может зарегистрировать слушателя, который получает события. Метод слушателя, который вызывается для события, принимает аргумент. API должен позволить клиенту решить, должен ли этот аргумент быть экземпляром конкретного класса или экземпляром интерфейса (чтобы избежать ненужных приведений в реализациях прослушивателя клиента). Для этого я поиграл с дженериками. Работает нормально, кроме одного случая!

Клиент реализует интерфейс (см. Пример в MyListener1Impl.java и MyListener2Impl.java) и может с помощью шаблонов определить тип аргумента события.

Статический метод фабрики используется для получения экземпляра ClassABuilder.java. Метод build () ClassABuilder возвращает экземпляр ClassA.java

По какой-то причине он не компилируется, когда метод Factory используется вместе с шаблоном Builder (вызывая методы в форме телескопирования) и когда конкретная реализация слушателя используется в качестве аргумента универсального типа (см., Где переменная "c4" установлено). Кто-нибудь может сказать мне, почему? Что не так?

См. Полный пример кода ниже. Скопируйте / вставьте указанные ниже классы в Eclipse (или любую другую IDE), и вы увидите, где он не компилируется.

класс Main.java

public class Main{
    public static void main(String[] args){
        // Works ok when generic type argument is set to interface "AnyFile".
        // MyListener2Impl has also declared "AnyFile"
        ClassABuilder<AnyFile> builder0 = Factory.newClassABuilder();
        builder0.set(new MyListener2Impl());
        ClassA<AnyFile> c0 = builder0.build();

        // Also Works ok when methods are called in telescope form 
        // (not sure "telescope" is the correct term?) 
        ClassA<AnyFile> c1 = Factory.newClassABuilder()
        .set(new MyListener2Impl())
        .build();

        // Works ok when generic type argument is set to concrete "MyFileImpl".
        // MyListener1Impl has also declared "MyFileImple"
        ClassABuilder<MyFileImpl> builder2 = Factory.newClassABuilder();
        builder2.set(new MyListener1Impl());
        ClassA<MyFileImpl> c2 = builder2.build();
        // also works ok with telescop form, but Factory class is NOT used.
        ClassA<MyFileImpl> c3 = new ClassABuilder<MyFileImpl>().set(new MyListener1Impl()).build();

        // But with static factory method AND telescope style, it does not compile! Why? What is wrong?
        ClassA<MyFileImpl> c4 = Factory.newClassABuilder()
        .set(new MyListener1Impl())
        .build();
    }
}

класс AnyFile.java

import java.util.List;
public interface AnyFile {
    List<AnyFile> listFiles();
}

класс ClassA.java

public class ClassA <T extends AnyFile>{
}

класс ClassABuilder.java

public class ClassABuilder <T extends AnyFile>{
    public ClassABuilder<T> set(Listener<T> a){
        return this;
    }

    public ClassA<T> build(){
        return new ClassA<T>();
    }
}

класс Factory.java

public class Factory {
    public static <T extends AnyFile> ClassABuilder<T> newClassABuilder(){
        return new ClassABuilder<T>();
    }
}

класс Listener.java

public interface Listener <T extends AnyFile>{
    void event(T file);
}

класс MyFileImpl.java

import java.util.List;
public class MyFileImpl implements AnyFile{
    @Override
    public List<AnyFile> listFiles() {
        // do something...
        return null;
    }
}

класс MyListener1Impl.java

public class MyListener1Impl implements Listener<MyFileImpl>{
    @Override
    public void event(MyFileImpl file) {
        // do something
    }
}

класс MyListener1Impl.java

public class MyListener2Impl implements Listener<AnyFile>{
    @Override
    public void event(AnyFile file) {
        // do something
    }
}

1 Ответ

1 голос
/ 22 июня 2011

Короткий ответ - вывод типа Java недостаточно силен для вывода слева направо. То есть тип <T> из Factory.newClassABuilder() (который будет использоваться для параметризации ClassA) должен быть известен до вызова метода .set(), и Java не может определить тип возврата Factory.newClassABuilder() из аргумента метода, вызываемого для этого возвращаемого типа.

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

...