Java中的差异问题



考虑以下接口和类:

interface BaseInterface { }
interface DerivedInterface extends BaseInterface { }
class BaseClass
{
    void baseFunc1( BaseInterface foo ) { }
    void baseFunc2( Collection<BaseInterface> foo ) { }
    void baseFunc3( Collection<? super DerivedInterface> foo ) { }
}
class DerivedClass extends BaseClass
{
    void derivedFunc1( DerivedInterface foo )
    {
        baseFunc1( foo ); //no problem here.
    }
    void derivedFunc2( Collection<DerivedInterface> foo )
    {
        baseFunc2( foo ); //error!
    }
    void derivedFunc3( Collection<DerivedInterface> foo )
    {
        baseFunc3( foo ); //fixed it, but the fix is unreasonable.
    }
}

derivedFunc1()调用baseFunc1()时没有问题,因为DerivedInterface可从BaseInterface分配。

但当derivedFunc2()调用baseFunc2()时,会出现问题,因为Collection<DerivedInterface>显然不能从Collection<BaseInterface>中赋值。

鉴于我对java中的协方差和反方差的理解(诚然不是很清楚),我能想到的解决这个问题的唯一方法是将baseFunc3()声明为接受Collection<? super DerivedInterface>,它可以从Collection<DerivedInterface>赋值。

当然,这是不合理的,因为BaseClass的设计不可能对某些DerivedInterface有任何了解,更不用说为BaseInterface的推导链加上一个上限了。

在我编写的代码中,这是一个经常发生的问题,目前每当我遇到它时,处理它的方法是向运行时添加转换逻辑。

我的问题是:有没有什么好而简单的方法可以让derivedFunc2将其Collection<DerivedInterface>传递给baseFunc2,而不会对BaseClass进行任何不合理的更改(例如添加baseFunc3()),也不会花费运行时转换的费用?

编辑:

我使用的接口实际上不是Collection接口,(当然,我不希望能够将DerivedInterface的集合视为BaseInterface的集合,因为有人可能会向该集合添加一个实现BaseInterface而不是DerivedInterface的对象。)

我使用的是Predicate接口,该接口包含一个接受BaseInterface作为参数的方法。实现该接口的对象永远不需要存储传递给它们的BaseInterface的实例,它们只需要调用该BaseInterface的一些方法,但正如Thomas所指出的,这没有任何区别。

因此,由于必须将BaseInterface传递给Predicate.eevaluate()方法,因此声明baseFunc2接受<? extends BaseInterface>将不起作用。

将方法更改为void baseFunc2( Collection<? extends BaseInterface> foo ) { }。这样,就允许传递BaseInterface或派生类型的任何集合,甚至是Collection的子类,例如Set<DerivedInterface>

请注意:因为集合的类型现在可以是BaseInterface的任何子类型,所以编译器不允许您向集合添加元素。

如果你需要这样做,最好的选择是无论如何传递Collection<BaseInterface>,但如果你确定你在做什么,你也可以通过只转换到Collecion来禁用类型检查,或者使用第三种方法,正如你所说,从设计角度来看,这不是最佳的。

BaseClass.baseFunc2应接受Collection< ? extends BaseInterface >作为其参数。在Java中,泛型实例的协变或逆变是在其使用点声明的,而不是在类定义本身声明的。

为什么T不是您设计中类层次结构的一部分?

class BaseClass< T > {
    void baseFunc1( T foo ) { }
    void baseFunc2( Collection< ? extends T > foo ) {
        // use foo in covariant fashion,
        // e.g., foo.contains( t ) 
        // can accept Collection< T >, Collection< S > (where S <: T)
    }
    void baseFunc3( Collection< ? super T > foo ) {
        // use foo in contravariant fashion,
        // e.g., foo.add( t )
        // can accept Collection< T >, Collection< S > (where S >: T)
    }
    void baseFunc4( Collection< T > foo ) {
        // use foo in invariant fashion,
        // e.g., foo.add( foo.iterator().next() )
        // can only accept Collection< T >
    }
}

现在你可以做了

class DerivedClass extends BaseClass< DerivedInterface > {
    void derivedFunc1( DerivedInterface foo ) {
        baseFunc1( foo );
    }
    void derivedFunc2( Collection< DerivedInterface > foo ) {
        baseFunc2( foo );
    }
    void derivedFunc3( Collection< DerivedInterface > foo ) {
        baseFunc3( foo );
    }
}

如果不能在BaseClass中使用T,则只能进行

class DerivedClass extends BaseClass {
    void derivedFunc1( DerivedInterface foo ) {
        baseFunc1( foo );
    }
    void derivedFunc2( Collection< DerivedInterface > foo ) {
        baseFunc2( foo );
    }
    void derivedFunc3( Collection< BaseInterface > foo ) {
        baseFunc3( foo );
    }
}

相关内容

  • 没有找到相关文章

最新更新