Ответ 1
Вопрос 1
Вызов типа стирания "недостаток" кажется вроде как попрошайничеством, но что бы то ни было, этот абзац звучит достаточно разумно для меня, может быть, с некоторыми каламбурами о манифестах и тегах класса, а также о том, что означает "существует".:)
Вопрос 2
Не совсем. Рассмотрим следующий класс и метод case:
case class Foo[T](v: T, f: T => Int)
def doSomething(x: Any): Unit = x match {
case Foo(v, f) => println(f(v))
case _ => println("whatever")
}
Это прекрасно работает:
scala> doSomething(Foo("hello world", (_: String).size))
11
Итак, мы не просто видим Foo
как Foo[Any]
, так как (_: String).size
недействителен Any => Int
:
scala> val stringSize: Any => Int = (_: String).size
<console>:11: error: type mismatch;
found : String => Int
required: Any => Int
val stringSize: Any => Int = (_: String).size
^
Итак, компилятор знает что-то о типах членов.
Вопрос 3
Вывод T
, когда вы вызываете f(t)
, будет своего рода экзистенциальным типом, поэтому не точно Any
, но в этом случае морально эквивалентен ему. Как показывает выше приведенный выше пример Foo
, если Event
имел другие члены или методы с участием T
, компилятор знал бы, что он тот же T
.
Вопрос 4
Когда мы говорим, что JVM стирает типы, мы на самом деле просто означаем "в общих контекстах". Каждый объект (в смысле JVM) имеет связанный с ним класс:
scala> val x: Any = "foo"
x: Any = foo
scala> x.getClass
res0: Class[_] = class java.lang.String
Но...
scala> val y: Any = Seq(1, 2, 3)
y: Any = List(1, 2, 3)
scala> y.getClass
res1: Class[_] = class scala.collection.immutable.$colon$colon
Здесь есть две вещи. Во-первых, значение класса, которое мы получаем, представляет собой пару зависимостей подтипа, более специфичных, чем даже предполагаемый тип, если бы мы остановились на подписке : Any
(я немного размахиваю рукой, сравнивая классы и типы, но если вы понимаете, о чем я). Во-вторых, из-за стирания типа для дженериков мы не получаем никакой информации о типе элемента из y.getClass
, а просто в классе "верхнего уровня" значения.
Заключение
На мой взгляд, это своего рода худший из всех возможных миров в отношении стирания стилей. Конечно, вы можете отправлять типы во время выполнения в Scala!
def foo(x: Any): Unit = x match {
case s: String => println(s"I got a string: $s")
case d: Double => println("numbers suck!")
case xs: List[Int] => println(f"first int is ${ xs.head }%d")
case _ => println("something else")
}
И затем:
scala> foo("bar")
I got a string: bar
scala> foo(List(1, 2, 3))
first int is 1
Но тогда:
scala> foo(List(true, false))
java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:101)
at .foo(<console>:15)
... 31 elided
Я лично предпочел бы полное стирание типов во время выполнения (по крайней мере, насколько это может видеть программист) и вообще не подходит для типа. В качестве альтернативы мы могли бы использовать генерируемые генераторы в стиле .NET(в этом случае я, вероятно, не использовал бы Scala, но тем не менее, это разумный и последовательный вариант). Так как у нас есть частичное стирание типа и сломанный тип case.