[英]Supporting covariant type conversions in Java
Java类型系统仅支持不变类型。 因此List<String>
不是List<Object>
。 List<String>
不是List<Object>
因为将Integer
插入List<String>
无效。 但是,存在这种协变类型转换有效的类型。
鉴于A,B和生产者类别:
class A{}
class B{}
interface Producer<T> {
T next();
}
可以定义协变类型Producer的强制转换:
class Types{
@SuppressWarnings("unchecked")
public static <T> Producer<T> cast(Producer<? extends T> producer){
return (Producer<T>) producer;
}
}
此方法支持从Producer<A>
为Producer<Object>
并防止像Producer<A>
到Producer<B>
无效转换:
Producer<Object> valid = Types.<Object> cast(new Producer<A>());
Producer<A> invalid = Types.<A> cast(new Producer<B>()); //does not compile
我的问题是我无法从Producer<Producer<A>>
到Producer<Producer<Object>>
。
Producer<Producer<A>> producerOfA = new Producer<Producer<A>>();
Producer<Producer<Object>> producerOfObjects =
Types.<Producer<Object>> cast(producerOfA); //does not compile
有没有办法说服Java类型系统在用户代码中没有警告的情况下执行这种有效的类型转换?
您还没有发布Producer的代码,但是根据名称和断言它应该是协变的,也许你现在说的任何地方:
Producer<Foo>
你应该说:
Producer<? extends Foo>
如果Java会自动意识到通用接口等同于其通配形式(例如Iterator
和Iterable
也是安全协变的),那将是很好的,但至少现在,它不会。
好吧,你可以通过原始类型去做
Producer<Producer<String>> spp = ...;
Producer<Producer<Object>> opp = (Producer<Producer<Object>>)(Producer) spp;
但它很脏,理论上不正确。 您应该使用Producer<Producer<?>>
(或Producer<? extends Producer<?>>
),但如果您真的不能 ,我建议您改为使用包装器。
class CovariantProducer<T> implements Producer<T> {
private final Producer<? extends T> backing;
public CovariantProducer(Producer<? extends T> backing) {
this.backing = backing;
}
@Override
public T next(){ return backing.next(); }
}
// Usage:
Producer<String> sp = ...;
Producer<Object> op = new CovariantProducer<Object>(sp);
final Producer<Producer<String>> spp = ...;
Producer<Producer<Object>> opp = new Producer<Producer<Object>>() {
@Override
public Producer<Object> next() {
return new CovariantProducer<Object>(spp.next());
}
};
更多的开销,但这样你不必强奸类型系统,并且真正不起作用的东西也看起来不像它的工作原理。
编辑:你也可以做一个特殊情况的cast
方法:
@SuppressWarnings("unchecked")
public static <T> Producer<Producer<T>> castMetaProducer(Producer<? extends Producer<? extends T>> producer){
return (Producer<Producer<T>>) producer;
}
但是,如果您要将Producer<Producer<Producer<String>>>
转换为Producer<Producer<Producer<Object>>>
,则必须为此添加另一种方法,依此类推。 由于严格来说这是类型系统的错误使用,因此以这种方式工作不方便并不奇怪。
Java没有提供任何方法来指定泛型类型应该是协变的(或逆变的)。 在你的情况下,如果我可以从Producer <Producer <A >>转换为Producer <Producer <A >>,那么我可以这样做:
Producer<Producer<Object>> objProducerProducer = cast(Producer<Producer<A>>);
Producer<Object> objProducer = objProducerProducer.produce()
但是当然,objProducerProducer实际上是在生成Producer <A>对象。 Java无法自动将这些转换为Producer <Object>,这就是它的运作方式。 您的显式强制转换静态方法是否有效,因为您需要<? 延伸T>。 泛型不会相互扩展。 我想你想要的是与泛型系统集成的隐式转换,这是我们所拥有的一个长期的飞跃。
编辑:澄清,是的,在您的情况下,将生产者<A>视为生产者<对象>是安全的,但这是仿制药系统无法获得的知识。
你在你的实现中类型类不是一个演员,它只是隐藏编译器警告的一种方便的方法(以及一种欺骗同事的方便方法)。 因此,我宁愿建议将Producer的实现改为这样的(如果可能的话),而不是“生成”你的生产者:
class Producer {
<T> T produce(Class<T> cls) {
Object o;
// do something, e.g.
// o = cls.newInstance();
// perfectly type safe cast
return cls.cast(o);
}
}
Producer p = new Producer();
A a = p.produce(A.class);
注意:它可能无法帮助您解决确切的问题,但它可能会指向正确的方向。
这是应该使用Java中的使用站点处的通配符来处理的。
但是,您可以使用Java中的代理显式执行此操作。
public static <T> Iterator<T> clean(final Iterator<? extends T> orig) {
return new Iterator<T>() {
public boolean hasNext() {
return orig.hasNext();
}
public T next() {
return orig.next();
}
public void remove() {
return orig.remove();
}
};
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.