繁体   English   中英

支持Java中的协变类型转换

[英]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会自动意识到通用接口等同于其通配形式(例如IteratorIterable也是安全协变的),那将是很好的,但至少现在,它不会。

好吧,你可以通过原始类型去做

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM