Java 如何确保仅以协变方式使用类型参数?

Java 如何确保仅以协变方式使用类型参数?,java,unit-testing,generics,static-analysis,covariance,Java,Unit Testing,Generics,Static Analysis,Covariance,假设我有一个通用接口Source,它是T对象的纯生产者。作为一个纯粹的生产者是界面契约的一部分。因此,如果您有源代码,那么您可以对源代码执行任何操作。这不是答案,但太长,无法放入注释中 因此,合理的预期是,无论你能用源代码做什么,如果你有源代码也应该可以做。如果你不想在你的接口中有任何put()方法,你所要做的就是不写任何东西,并且在代码的某个地方留下这样的注释;最好周围有很多星号 老实说,我一点也不理解有一个特性会给语言带来阻止类或接口的一些逆变方法的可能性的有用性。这是程序员的责任,而不是编

假设我有一个通用接口
Source
,它是
T
对象的纯生产者。作为一个纯粹的生产者是界面契约的一部分。因此,如果您有
源代码,那么您可以对
源代码执行任何操作。这不是答案,但太长,无法放入注释中


因此,合理的预期是,无论你能用
源代码做什么,如果你有
源代码也应该可以做。如果你不想在你的接口中有任何put()方法,你所要做的就是不写任何东西,并且在代码的某个地方留下这样的注释;最好周围有很多星号

老实说,我一点也不理解有一个特性会给语言带来阻止类或接口的一些逆变方法的可能性的有用性。这是程序员的责任,而不是编译器的责任


这与您要求提供一个特性是一样的,该特性可以阻止任何函数的加法,该函数将接受整数作为其参数之一或返回值。在我看来,在一种语言中拥有这样一个功能是完全没有用的。

也许不是您想要的答案,但我想您可以在接口声明中强制执行它

强制逆变使用
公共接口使用者{
公共物品;
}
公共静态类ConsumerImpl实现使用者{

私有列表考虑到你的链接和语法,我想你说的是Java?在标记中明确表示是值得的。使用类似
FooSource extends Source
的东西怎么样?你是对的,它很模糊。我希望现在更好。我认为这不是有效的批评。几乎任何形式的静态分析都可以这样说好吧,接口的契约是关于它的方法的:你定义抽象方法a(),b(),c()…然后你要求任何人将这个接口扩展到一个类中来实现这些抽象方法。你要求的是阻止任何方法添加到这个接口中,这些方法会遵循一些特定的规则(和完全任意的)签名。老实说,这有什么意义呢?对于其余的,如果你想坚持,你应该写一个明确的例子,说明你的意思完全符合你“对源代码和源代码的合理期望,但是你也可以把
接口弄糊涂{public void consumer(s item);public T product();}
。我遗漏了什么吗?@Saintali当然可以!Java泛型的问题是它们基于使用站点定义的概念,而不是像C#或Scala中那样基于声明站点注释。
public void copyTo(List<T> destination);
public void copyTo(List<? super T> destination);
public interface Consumer<T, S extends T> {
   public void consume(S item);
}

public static class ConsumerImpl<T, S extends T> implements Consumer<T, S> {

   private List<? super S> items = new ArrayList<T>();

   public void consume(S item) {
      this.items.add(item);
   }

}
Consumer<Object, String> consumer = new ConsumerImpl<Object, String>();
consumer.consume("Whatever");
consumer.consume("Another");
public interface Producer<T, S extends T> {
   public T produce();
}

public static class ProducerImpl<T, S extends T> implements Producer<T,S> {

   private Deque<? extends T> items = new ArrayDeque<S>();

   public ProducerImpl(Deque<S> items) {
      this.items = items;
   }

   public T produce() {
      return items.pop();
   }

}
Deque<Integer> myInts = new ArrayDeque<Integer>();
myInts.push(1);
myInts.push(2);

Producer<Number, Integer> producer = new ProducerImpl<Number, Integer>(myInts);
Number n1 = producer.produce();
Number n2 = producer.produce();