2014-02-10 33 views
3

在我正在使用的一些代码中,我有一个现有的第三方API实现了从A扩展的事物(也许不是直接的,而是通过X,也许还实现了一堆其他接口)。Java:是否有可能说变量类型必须满足多重继承/接口要求

现在为我正在处理的代码,我有一个接口IB,它提供了A提供的附加功能。因为我的很多代码实际上都要求传递给它的对象扩展了A,并且还实现了IB,但是没有办法为我的成员变量声明我能想到的。但选择A或IB会导致很多演员阵容。

我猜如果A有/有一个接口IA会解决这个问题,但是我没有办法改变A,或者我的IB实现不需要扩展A(第三方代码使用A,并且需要通过它来关心很多管理,持久性,网络,用户接口等)。

class Z { 
    private List<?what here?> items; 
    /**The implementer of IB should know how to find the Z instance and call this.*/ 
    private void subscribe(? item) { 
     items.add(item); 
    } 
    public void doSomethingWithItems() { 
     ...code thats requires facilities from A and IB... 
    } 

回答

2

使用通用助手类,其类型参数使您可以使用一个类型路口:

class Z { 

    private static final class Wrapper<T extends A & IB> { 

     private final T item; 

     Wrapper(final T item) { 
      this.item = item; 
     } 

     void doWork() { 
      // code thats requires facilities from A and IB 
     } 
    } 

    private List<Wrapper<?>> wrappers; 

    private <T extends A & IB> void subscribe(T item) { 
     wrappers.add(new Wrapper<T>(item)); 
    } 
    public void doSomethingWithItems() { 
     for (final Wrapper<?> wrapper : wrappers) { 
      wrapper.doWork(); 
     } 
    } 

我介绍了这个帖子了类似的回答:Java generics type mismatch in method signature

+0

“列表>”正在“列表>”我认为?去尝试这个,并没有真正考虑使这个方法是通用的。 –

+0

@FireLancer是的,纠正。我回过头来看看它的名字:) –

+1

这是一个非常简洁的技巧,可以从Java提供的类型参数的交集类型中引导第一类交集类型。我将添加到我的工具箱丑陋的泛型问题:-) – meriton

3

您可以指定一个类型路口

<T extends A & IB> 

的规则,如果类型之一是一类,它必须首先列出。

如果可以的话我会键入类:

class Z<T extends A & IB> { 
    private List<T> items; 

    private void subscribe(T item) { 
     items.add(item); 
    } 
    public void doSomethingWithItems() { 
     // the items are both A and IB 
    } 
} 

如果您不能键入Z,去类型的方法:

class Z { 
    private List<A>items; 

    private <T extends A & IB> void subscribe(T item) { 
     items.add(item); 
    } 
    public void doSomethingWithItems() { 
     // items are A, but if you want IB functionality you must cast. 
     // the cast is safe if items are only added via subscribe() 
    } 
} 
+1

T是什么类型? Z实例用于满足要求的任何事物,而不是特定类型的事物,因此同一个z实例可以被赋予Abc或Xyz对象,除了扩展A和实现IB之外,它们是不相关的。 –

+0

http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4。9“不能直接将交集类型作为程序的一部分来编写,没有语法支持这一点。”我想这是什么? –

+0

@fire huh?这编译。有什么问题? – Bohemian

1

你可以做一个新的抽象类,这两个用抽象方法实现IB,并扩展A.然后,可以让所有需要使用的类扩展新的抽象类。

+0

IC做什么?一个抽象类就没有问题,只要在整个系统中没有其他任何东西,任何形式的重叠都会这样做。 –

+0

对不起,我不了解您的评论。你是否建议有两个不同的接口'IB'和'IC',我需要一些扩展'A'并实现'IB'的类。一些扩展'A'并实现'IC'的类;并且还扩展'A'并实现'IB'和'IC'?这是你的问题,还是我误解了你? –

+0

是的,潜在的,它在没有多重继承问题。 AExtended表示,IB的某些实现可能已经有了不同的基础。 IB是一个以 –

2

这将是最ideomatic如果IB是一个IA的子类型,但是如果你不能这样做...

由于Java不支持头等交集类型,所以很难在代码中表达它。也就是说,Java只支持类型参数边界中的交集类型,并且我们不能在字段声明中使用类型参数,而不要求列表中的所有元素都是IB的相同子类型。

因此,我能想到的最好的近似值是强制约束只能在公共API,并且使用内部转换:

class Z { 
    private List<A> items; 

    private <B extends A & IB> void subscribe(B item) { 
     items.add(item); 
    } 

    public void doSomethingWithItems() { 
     for (A a : items) { 
      IB b = (IB) a; // safe because we checked it on subscription 
      // use features of A and IB 
     } 
    } 
} 

是的,这是丑陋的。这就是为什么像Ceylon这样的新型JVM语言具有第一类交叉类型的原因。也就是说,在锡兰,人们可以简单地写:

class Z() { 

    List<A & IB> items; 

    void subscribe(A & IB item) { 
     items.add(item); 
    } 

    void doSomethingWithItems() { 
     for (item in items) { 
      // use features of A and IB 
     } 
    } 
} 
相关问题