我正在尝试构建一个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
}
}
答案 0 :(得分:1)
简短的回答是Java的类型推断不足以推断从左到右。也就是说,<T>
的类型Factory.newClassABuilder()
(将用于参数化ClassA
)必须在调用.set()
方法之前就已知,并且Java无法推断{{ 1}}从返回类型调用的方法的参数返回类型。
这种推理需要整个程序分析,只有少数语言可以做,Java肯定不支持。