Сгруппировать, считая в Java 8 stream API

Самый простой способ понять, почему это недопустимо, - это следующий пример:

abstract class Fruit
{
}

class Apple : Fruit
{
}

class Banana : Fruit
{
}

// This should intuitively compile right? Cause an Apple is Fruit.
List<Fruit> fruits = new List<Apple>();

// But what if I do this? Adding a Banana to a list of Apples
fruits.Add(new Banana());

Последнее утверждение разрушило бы безопасность типов .NET.

Массивы, однако, разрешите это:

Fruit[] fruits = new Apple[10]; // This is perfectly fine

Однако, помещая Banana в fruits, все равно будет нарушена безопасность типа, поэтому .NET должен выполнить проверку типа для каждой вставки массива и выдать исключение, если это на самом деле не Apple. Это потенциально (небольшой) удар производительности, но это можно обойти, создав обертку struct вокруг любого типа, поскольку эта проверка не выполняется для типов значений (потому что они не могут наследовать ни от чего). Сначала я не понял, почему это решение было принято, но вы часто сталкиваетесь с тем, почему это может быть полезно. Наиболее распространенным является String.Format, который принимает params object[], и любой массив может быть передан в это.

В .NET 4, однако, существует безопасная ковариация / контравариантность типа, которая позволяет вам выполнять некоторые присвоения, подобные этим, но только в том случае, если они достаточно безопасны. Что доказуемо безопасно?

IEnumerable<Fruit> fruits = new List<Apple>();

Вышеупомянутое работает в .NET 4, потому что IEnumerable<T> стал IEnumerable<out T>. out означает, что T может когда-либо выходить из из fruits и что не существует никакого метода на IEnumerable<out T>, который когда-либо принимает T как параметр, так что вы никогда не сможете неправильно передать Banana в IEnumerable<Fruit>.

Контравариантность - это то же самое, но я всегда забываю точные детали. Неудивительно, что для этого есть ключевое слово in для параметров типа.

141
задан Lii 2 March 2017 в 09:46
поделиться