Почему эти контравариантные типы аргументов считаются безопасными?

Я только что узнал в своем классе языков программирования, что «контравариантные типы аргументов на самом деле были бы безопасными, но они не были сочтены полезными и, следовательно, не поддерживаются в практических языках». Несмотря на то, что они не поддерживаются, я не понимаю, почему что-то вроде этого примера, который нам дали, теоретически было бы «безопасным»:

class Animal {
  ...
  public bool compare(Panda) { ... }
} 

class Panda extends Animal {
  ... 
  public bool compare(Animal) { ... }
}

Насколько я понимаю, проблемы с подтипами возникают, когда делается что-то, что может привести к потере специфичности. Так что, если я сделал это? :

Panda p = new Panda(); 
Animal a = new Animal
...
p.compare(a); 

Когда я смотрю на это, мне кажется, что у панды могут быть (и, вероятно, есть) дополнительные поля, о которых обычное животное не знает. Таким образом, даже если все их элементы данных, относящиеся к животным, одинаковы, у панды могут быть другие вещи, которые отличаются. Как это позволит сравнивать его с обычным животным? Будет ли он просто рассматривать вещи, относящиеся только к животным, и игнорировать все остальное?


person norman    schedule 17.11.2012    source источник


Ответы (1)


В вашем примере вы не используете какие-либо универсальные типы. У вас есть Panda расширение Animal, и это пример наследования, который приводит к полиморфизм, который более или менее соответствует тому, что вы описываете. Проверьте ссылки.

Чтобы получить контравариантность, вам нужно рассмотреть некоторый общий тип. В качестве примера я буду использовать тип .NET IComparer`1[T]. С помощью синтаксиса C# (который я буду использовать, а не Java) мы указываем, что IComparer является контравариантным в T, записывая in в определении:

public interface IComparer<in T>
{
  ...
}

Предположим, у меня есть метод, который возвращает IComparer`1[Animal] (или IComaparer<Animal>), например:

static IComparer<Animal> CreateAnimalComparer()
{
  // code that returns something here
}

Теперь в С# можно сказать:

IComparer<Panda> myPandaComparer = CreateAnimalComparer();

Теперь это из-за контравариантности. Обратите внимание, что тип IComparer<Animal> не является производным от (или «расширением») типа IComparer<Panda>. Вместо этого Panda происходит от Animal, и это приводит к тому, что IComparer<Xxxx> можно присваивать друг другу (в противоположном порядке, следовательно, «контравариантность» (а не «ковариантность»)).

Причина, по которой имеет смысл объявить Comparer<> контравариант, заключается в том, что если у вас есть компаратор, который может сравнивать двух произвольных животных и возвращать число со знаком, указывающее, какое из них больше, то тот же компаратор также может принимать двух панд и сравнивать их. Ибо панды - животные.

Таким образом, отношение

любое Panda является Animal

(из наследования) приводит к отношению

любое IComparer<Animal> является IComparer<Panda>

(по контравариантности).

В примере с ковариацией то же отношение

любое Panda является Animal

приводит к

любое IEnumerable<Panda> является IEnumerable<Animal>

по ковариации (IEnumerable<out T>).

person Jeppe Stig Nielsen    schedule 25.11.2012