2015-04-11 2 views
1

В простом примереScala. Почему класс владельца теряет тип стоимости?

trait Holder[T] {def value: T} 

class LongHolder(val value: Long) extends Holder[Long]  
class StringHolder(val value: String) extends Holder[String] 

val r1 = List(new LongHolder(1), new StringHolder("a"))  
val h0 = r1(0)  
val h1 = h0.value 


scala> r1: List[Holder[_ >: String with Long]] = List([email protected], [email protected]) 
scala> h0: Holder[_ >: String with Long] = [email protected] 
scala> h1: Any = 1 

Я не могу понять, почему h1: Any = 1 в [email protected], но не h1: Long = 1

без List типа не утрачивается

val h0 = new LongHolder(1) 
val h1 = h0.value 

scala> h0: LongHolder = [email protected] 
scala> h1: Long = 1 

ответ

2

Элементы List должны быть один type. Компилятор сделает все возможное, чтобы определить тип списка, если он явно не указан. У вас есть список, который содержит LongHolder и StringHolder, поэтому компилятор попытается использовать наименее распространенный тип этих типов в качестве типа списка. Поскольку оба эти типа расширяют Holder, он попытается использовать это как тип. Чтобы определить тип, используемый для общего параметра Holder, компилятору необходимо найти наименее распространенный тип String и Long, который является синтетическим типом _ with String with Long. Поскольку нет нетривиальных типов, распространяется как String, так и Long, единственным типом, который он может использовать, является Any, который распространяется на все типы.

1

Объяснение dk14 верное. Но если вы хотите сохранить информацию о типах во время выполнения, вы можете использовать отражение Scala. Я изменил пример docs немного, чтобы продемонстрировать, что:

def mkList[T : ClassTag](elems: T*) = List[T](elems: _*) 

val r1 = mkList(new LongHolder(1L), new StringHolder("a")) 
val h0 = r1(0) 
h0.value.getClass 

scala> h0: Holder[_1] = [email protected] 
scala> res0: Class[?0] = class java.lang.Long 
+1

Тип стирания не имеет ничего общего с этим, поскольку тип становится «потерянным» во время компиляции - не во время выполнения – dk14

+1

Вы правы, и ваше объяснение довольно ясно об этом. Я отредактировал свой ответ и надеюсь, что он по-прежнему содержит полезную информацию. – ipoteka

3

Это происходит потому, что Scala не имеет union типов, как type T = String | Long. Ваш T от List[T] должен иметь некоторый конкретный тип, например String, Long или что-то, и вы не можете создать список из нескольких типов. Каждый элемент в списке должен иметь тот же тип T, что делает много операций (например, flatMap или filter), чтобы можно:

scala> val a: (Int, Int, String, String, Int) = parseSomething() // tuple may be seen as List with heterogenous elements 
a: (Int, Int, String, String, Int) = (5, 6, "a", "b", 7) 

scala> val b = a.filter(_ != 6) //how compiler would know that `a._2 == 6` to infer (Int, String, String, Int) ? 
b: (???) = (5, "a", "b", 7) 

Так T должно быть одинаковым для всех элементов. Когда компилятор видит несколько параметров, которые имеют несколько типов, он пытается найти ближайший общий тип:

def ff[T](a: T, b: T): T = a 

trait A 
case class A1() extends A 
case class A2() extends A 

scala> ff(A1(), A2()) 
res20: A = A1() //A is the nearest common type 

Дженерик немного интереснее. Компилятор может вывести экзистенциальный тип для них M[_ >: A1 with A2]:

scala> ff(new LongHolder(1), new StringHolder("a")) 
res23: Holder[_ >: String with Long] = [email protected] 

Это означает Holder[T] forSome {type T >: String with Long}, что означает, что Holder определяется для некоторого T (например T должен существовать но не обязательно для всех держателей), которые >: String with Long. Просто говоря, компилятор спрашивает: «Эй! Нам нужны некоторые T, что больше, чем Int с Long здесь» (представьте себе типы, как коробки, чтобы положить что-то в, и компилятор просто «Бригадир»)

Наконец Any меньше (ближе) (он подходит, как он может быть больше String with Long), поэтому результат становится Any, так как само значение не может быть экзистенциальным в Scala.

P.S. Shapeless HList на самом деле делает то, что вы хотите.