简单示例
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(LongHolder@5761bff, StringHolder@d280526)
scala> h0: Holder[_ >: String with Long] = LongHolder@5761bff
scala> h1: Any = 1
我无法理解h1: Any = 1
中的LongHolder@5761bff
,而不是h1: Long = 1
List
类型的不会丢失
val h0 = new LongHolder(1)
val h1 = h0.value
scala> h0: LongHolder = LongHolder@7b2e3720
scala> h1: Long = 1
答案 0 :(得分:3)
之所以如此,是因为Scala没有像type T = String | Long
那样的union类型。来自T
的{{1}}应该具有某种具体类型,例如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] = LongHolder@a5e201f
,这意味着为Holder[T] forSome {type T >: String with Long}
定义了Holder
(存在<{em>} <{1}}但所有持有者都不需要),T
。简单地说编译器要求&#34;嘿!我们需要一些比Int更大的T
和更长的&#34; (想象一下类型作为放置内容的框,编译器只是一个&#34;工头&#34;)
最后>: String with Long
是更小(最接近)的类型(它适合,因为它可能大于T
),因此结果变为Any
,因为值本身不能斯卡拉存在主义。
P.S。无形HList
实际上做你想要的。
答案 1 :(得分:2)
List
的元素必须属于一种类型。如果没有明确提供,编译器将尽力确定列表的类型。您有一个包含LongHolder
和StringHolder
的列表,因此编译器将尝试使用这些类型中最不常见的类型作为列表的类型。由于这两种类型都扩展Holder
,因此它会尝试将其用作类型。为了确定用于Holder
的泛型参数的类型,编译器需要找到最小公共类型的String
和Long
,这是合成类型{{1} }。由于没有非平凡类型同时扩展_ with String with Long
和String
,因此它可以使用的唯一类型是Long
,所有类型都会扩展。
答案 2 :(得分: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] = LongHolder@6618cd9b
scala> res0: Class[?0] = class java.lang.Long