斯卡拉。为什么持有者类失去了价值的类型?

时间:2015-04-11 08:19:16

标签: scala types

简单示例

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

3 个答案:

答案 0 :(得分:3)

之所以如此,是因为Scala没有像type T = String | Long那样的union类型。来自T的{​​{1}}应该具有某种具体类型,例如List[T]String或其他类型,并且您无法创建多种类型的列表。列表中的每个元素都必须具有相同的Long类型,这样才能实现许多操作(如TflatMap):

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的元素必须属于一种类型。如果没有明确提供,编译器将尽力确定列表的类型。您有一个包含LongHolderStringHolder的列表,因此编译器将尝试使用这些类型中最不常见的类型作为列表的类型。由于这两种类型都扩展Holder,因此它会尝试将其用作类型。为了确定用于Holder的泛型参数的类型,编译器需要找到最小公共类型的StringLong,这是合成类型{{1} }。由于没有非平凡类型同时扩展_ with String with LongString,因此它可以使用的唯一类型是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