考虑以下接口和类:
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 );
}
}