2011-06-21 47 views
1

我想建立一个API,客户端可以注册一个监听器,它接收事件。在事件上调用的侦听器方法需要一个参数。 API应该让客户决定这个参数应该是具体类的实例还是接口的实例(以避免客户端监听器实现中不必要的转换)。要做到这一点,我玩过泛型。它工作正常,除了一个案例!返回的工厂方法Builder不能用泛型编译。哪里不对?

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

静态工厂方法用于检索ClassABuilder.java的实例。 ClassBuilder的build()方法返回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 
    } 
} 
+0

对于一个问题,这是非常多的代码。你能把它降到最低吗? –

+0

这似乎是[为什么隐式类型推断仅适用于赋值?]的副本?(http://stackoverflow.com/questions/2055352/why-implicit-type-in​​ference-only-works-in-an-assignment ),[由编译器自动绑定(类型推断)的泛型类型](http://stackoverflow.com/questions/4143796/auto-binding-type-in​​ference-of-generic-types-by-the-compiler),或者[泛型类型推断失败?](http://stackoverflow.com/questions/4149825/generics-type-in​​ference-fails)。 –

回答

1

简短的回答是,Java的类型推断是没有强大到足以推断左到右。也就是说,在调用.set()方法之前必须知道类型<T>Factory.newClassABuilder()(将用于参数化ClassA),并且Java无法从在该返回类型上调用的方法的参数推断出Factory.newClassABuilder()的返回类型。

这种推理需要整个程序的分析,少数语言做,Java绝对不支持。

+0

感谢您的答案!我将删除使用静态工厂类比()。 – Alexander

+0

对不起,我无法做出一个简短的例子。 – Alexander