Java类型系统仅支持不变类型。因此List<String>
不是List<Object>
。 List<String>
不是List<Object>
,因为将Integer
插入List<String>
无效。但是,有些类型的协变类型转换是有效的。
鉴于A,B和Producer类:
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类型系统在用户代码中没有警告的情况下执行这种有效的类型转换?
答案 0 :(得分:8)
您尚未发布Producer的代码,但是根据名称和断言它应该是协变的,也许您现在所说的位置:
Producer<Foo>
你应该说:
Producer<? extends Foo>
如果Java会自动意识到通用接口等同于其通配符表单(例如Iterator
和Iterable
也是安全协变的),那将是很好的,但至少现在,它没有。
答案 1 :(得分:2)
好吧,你可以通过原始类型进行操作
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>>>
,则必须为此添加其他方法,依此类推。由于严格来说这是类型系统的错误使用,因此以这种方式工作不方便并不奇怪。
答案 2 :(得分:1)
Java没有提供任何方法来指定泛型类型应该是协变的(或逆变的)。在您的情况下,如果我可以从Producer&lt; Producer&lt; A&gt;&gt;转换为Producer&lt; Producer&lt; A&gt;&gt;,那么我可以这样做:
Producer<Producer<Object>> objProducerProducer = cast(Producer<Producer<A>>);
Producer<Object> objProducer = objProducerProducer.produce()
但是,当然,objProducerProducer实际上正在制作Producer&lt; A&gt;对象。 Java无法将这些转换为Producer&lt; Object&gt;自动,就是这样。您的显式强制转换静态方法有效,因为您需要&lt;?延伸T>。泛型不会相互扩展。我想你想要的是与泛型系统集成的隐式转换,这是我们所拥有的一个很长的飞跃。
编辑:澄清,是的,在您的情况下,处理制作人&lt; A&gt;是安全的。作为生产者&lt; Object&gt;,但这是泛型系统无法获得的知识。
答案 3 :(得分:1)
你在你的实现中类型类不是强制转换,它只是一种隐藏编译器警告的方便方法(以及一种欺骗同事的方便方法)。因此,我宁愿建议将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);
注意:它可能对您的确切问题没有帮助,但可能会指向正确的方向。
答案 4 :(得分:1)
这应该是在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();
}
};
}