我一直在努力学习更多的Scala,偶然发现了这篇讨论f边界类型的有趣文章。我对最后关于使用这类类型的值集合的讨论特别感兴趣。
下面是一个简单的例子:abstract class AbsClass[A <: AbsClass[_]] {
this: A =>
def me: A = this
}
def me2[A <: AbsClass[A]](a: A): A = a
case class C1(v: Int) extends AbsClass[C1]
case class C2(v: Int) extends AbsClass[C2]
如博客文章中所示,我们可以使用存在量词来允许我们在C1
和C2
的列表上调用me2
scala> List[A forSome {type A <: AbsClass[A] }](C1(1), C2(2)).map(me2(_))
res2: List[A forSome { type A <: AbsClass[A] }] = List(C1(1), C2(2))
现在,我一直在尝试列表类型的几个变化,我希望有更有经验的人能指出其中的微妙之处
如下所示
scala> val l2 = List[A forSome {type A <: AbsClass[A] }](C1(1), C2(2))
l2: List[A forSome { type A <: AbsClass[A] }] = List(C1(1), C2(2))
scala> l2.map(_.me)
res4: List[A forSome { type A <: AbsClass[A] }] = List(C1(1), C2(2))
我也可以调用_.me
并将列表表示为包含AbsClass[A] forSome {type A}
的实例,如
scala> val l: List[AbsClass[_]] = List(C1(1), C2(2)).map(_.me)
l: List[AbsClass[_]] = List(C1(1), C2(2))
但是当我再次尝试将相同的映射应用到l
时,我丢失了类型信息和我被一个List[Any]
卡住了。有办法保持这里的类型吗?
请记住,下划线是类型约束。
val l: List[AbsClass[_]] = List(C1(1), C2(2)).map(_.me)
val ll: List[AbsClass[_ >: Nothing <: Any]] = List(C1(1), C2(2)).map(_.me)
这两件事是一样的。下划线将压缩到Nothing的下界或Any的上界。
有关存在类型如何工作的更多信息,请参见:https://youtu.be/NFnsFda82Yo