返回生成器的工厂方法不能使用泛型进行编译。怎么了?

时间:2011-06-21 23:40:30

标签: java generics static factory builder

我正在尝试构建一个API,客户端可以在其中注册一个接收事件的侦听器。在事件上调用的侦听器方法接受参数。 API应该让客户端决定这个参数应该是具体类的实例,还是接口的实例(以避免客户端监听器实现中不必要的转换)。要做到这一点,我已经玩弄了泛型。它的工作正常,除了一个案例!

客户端实现了一个接口(参见MyListener1Impl.java和MyListener2Impl.java中的示例),并且可以使用泛型来决定事件的参数类型。

静态工厂方法用于检索ClassABuilder.java的实例。 ClassABuilder的build()方法返回ClassA.java的实例

由于某种原因,当Factory方法与Builder Pattern一起使用(以telescoping形式调用方法)并且具体的侦听器实现用作泛型类型参数时,它不会编译(请参阅变量“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();
}

class ClassA.java

public class ClassA <T extends AnyFile>{
}

class 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;
    }
}

class MyListener1Impl.java

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

class MyListener1Impl.java

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

1 个答案:

答案 0 :(得分:1)

简短的回答是Java的类型推断不足以推断从左到右。也就是说,<T>的类型Factory.newClassABuilder()(将用于参数化ClassA)必须在调用.set()方法之前就已知,并且Java无法推断{{ 1}}从返回类型调用的方法的参数返回类型。

这种推理需要整个程序分析,只有少数语言可以做,Java肯定不支持。