考虑以下接口和类:
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的某些方法,但正如托马斯所指出的,这没有任何区别。
因此,由于必须将BaseInterface传递给Predicate.evaluate()方法,声明baseFunc2以接受<? extends BaseInterface>将无法工作。
发布于 2013-11-25 16:19:49
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 );
}
}如果不能在T中使用BaseClass,则只能使用
class DerivedClass extends BaseClass {
void derivedFunc1( DerivedInterface foo ) {
baseFunc1( foo );
}
void derivedFunc2( Collection< DerivedInterface > foo ) {
baseFunc2( foo );
}
void derivedFunc3( Collection< BaseInterface > foo ) {
baseFunc3( foo );
}
}发布于 2013-11-25 16:20:13
将方法更改为void baseFunc2( Collection<? extends BaseInterface> foo ) { }。通过这种方式,可以传递任何BaseInterface或派生类型的集合,甚至是Collection的子类,例如Set<DerivedInterface>。
注意:由于集合的类型现在可以是BaseInterface的任何子类型,编译器将不允许您向集合添加元素。
如果您需要这样做,最好的选择是以任何方式传递Collection<BaseInterface>,但是如果您确定要做什么,也可以通过只转换到Collecion或使用第三种方法来禁用类型检查,正如您所说的,从设计的角度来看,这不是最优的。
https://stackoverflow.com/questions/20197831
复制相似问题