Ответ 1
Я надеюсь, что это не слишком долго, но я серьезно сомневаюсь в этом, поэтому я сначала попытаюсь дать быстрый ответ: "Когда вы называете (абстрактным) что-то, основное использование имеет в виду это позже". Ну, это было не полезно сейчас, не так ли?
Рассмотрим эту простую функцию Scala:
val sum = (a: Int, b: Int) => a + b
Компилятору не нужно знать, что a
является a
и b
является b
. Все, что нужно знать, что a
, а также b
имеют тип Int
и что a
предшествует b
(что в данном случае не имеет значения, так как добавление является коммутативным, но компилятор все равно заботится!). Scala предлагает (не поймите меня неправильно, я также его люблю) компилятор, дружественный синтаксис заполнителя, который выступает в качестве доказательства этой "гипотезы".
val sum: (Int, Int) => Int = _ + _ // where the 1st _ differs from the 2nd _
Теперь взгляните на это:
case x: SomeTypeParameterizedWith[AnotherType] // AnotherType is erased anyway
case x: SomeParameterizedType[_] // Existential type
case x: SomeParameterizedType[kind] // Existential type which you can reference
Если вам не нужен аргумент типа, используйте синтаксис placeholder. Когда вы делаете (по какой-либо причине) заботу, вы должны называть аргумент типа нижним регистром, поэтому компилятор знает, что вы хотите рассматривать его как идентификатор.
Вернуться к вашему вопросу.
Основное использование экзистенциальных типов работает с шаблонами подстановок Java. Это взято из Программирование в Scala - Экзистенциальные типы и было слегка изменено вашим.
// This is a Java class with wildcards
public class Wild {
public java.util.Collection<?> contents() {
java.util.Collection<String> stuff = new Vector<String>();
stuff.add("a");
stuff.add("b");
stuff.add("see");
return stuff;
}
}
// This is the problem
import scala.collection.mutable.Set
val iter = (new Wild).contents.iterator
val set = Set.empty[???] // what type goes here?
while (iter.hasMore)
set += iter.next()
// This is the solution
def javaSet2ScalaSet[T](jset: java.util.Collection[T]): Set[T] = {
val sset = Set.empty[T] // now T can be named!
val iter = jset.iterator
while (iter.hasNext)
sset += iter.next()
sset
}
Хорошо, так что же произошло? Простые дженерики, нет волшебства?! Если вы имеете дело с дженериками на повседневной основе, это выглядит нормально для вас, но вы забываете, что ультра-супер-концепция ввода аргументов типа в область видимости работает только по классам и методам. Что делать, если вы находитесь вне класса или метода, просто в какой-то случайной области в середине нигде (например, REPL)? Или что, если вы находитесь в классе или методе, но аргументы типа не были введены в их области? Вот где ваш вопрос и этот ответ приходят в игру.
val set = new Wild().contents match {
case jset: java.util.Collection[kind] => {
val sset = Set.empty[kind]
val iter = jset.iterator
while (iter.hasNext)
sset += iter.next()
sset
}
}
Идентификатор kind
требуется, чтобы компилятор мог проверить, что вы ссылаетесь на одно и то же.
Обратите внимание, что вы не можете просто добавлять строки в set
, так как тип set
равен Set[_]
.