我正在尝试创建一个泛型类型并将它们放入列表中。这是我的代码。
class A
class B extends A
class C extends B
class G[T <: B] {
def x(t: T) = {}
}
val g1 = new G[B]
val g2 = new G[C]
//val list1: List[G[B]] = List(g1, g2) -- this would not compile.
val list: List[G[_ <: B]] = List(g1, g2)
list.head.x()//now what ?
我期待x的类型是B,但编译器抱怨
_$1 where type _$1 <: com.example.cake.modules.robotics.B
list.head.x(new B)
快速搜索我指向外卡的问题(_&lt;:B)。但不知道如何解决。
答案 0 :(得分:3)
由于以下部分中描述的原因,当您将元素放入列表时,您将丢失有关g1
和g2
的确切类型的信息。 shapeless HLists 不是这种情况:
> import shapeless._
> val list = g1 :: g2 :: HNil
list: G[B] :: G[C] :: HNil = ::(cmd7$G@3a94d716, ::(cmd7$G@64992713, HNil))
> list.head.x(new B)
// successful
编译器所说的实际上是完全合情合理的(尽管有点模糊):请注意g2
的类型为G[C]
且其x
成员仅接受C
1}}(或任何子类型)作为参数。
事实上:
scala> :type g2
G[C]
scala> g2.x(new C)
scala> g2.x(new B)
<console>:16: error: type mismatch;
found : B
required: C
g2.x(new B)
^
当你同时添加g1
和g2
时,你会失去两者的精确类型,并且对于元素类型,最终需要安全的东西,无论你得到什么元素。 List[G[_ <: B]]
表示对于列表中的每个元素,T
中的G[T]
需要T <: B
,并且这是元素的唯一保证你拿出了清单。因此,最后,您使用list.head
获得的是G[T <: B]
,因此T
可以是B
的任何子类型(这是一个无限集),因此,例如,如果是T = C
,那么x
只会接受C
的子类型作为其t
参数的值。这使得有效地无法为t
提供值,因为T
可能只是Nothing
(没有价值的底层居住类型)。
事实上,如果你让scalac推断list
的类型,你得到:
scala> val list = List(g1, g2)
list: List[G[_ >: C <: B]] = List(G@379619aa, G@cac736f)
表示T
中的G[T]
必须是B
的子类型和C
的超类型,这会将下限设置为T
,从而允许通话list.head.x(new C)
但不允许list.head.x(new B)
:
scala> list.head.x(new B)
<console>:18: error: type mismatch;
found : B
required: _2 where type _2 >: C <: B
list.head.x(new B)
重要的是要注意,如果不是使用head
而是抓住列表list(1)
的第二个元素,那么就会发生完全相同的事情。
为了弄清楚应该在这里做些什么,我们可能需要了解更多你想要实现的目标,因为没有那么简单的“修复”。
答案 1 :(得分:1)
班级G
在逆变位置使用类型T
(作为x
的参数)。
根据您的用例,您可以为类层次结构建模,以便将T
的子类传递给x应该可以工作。这意味着您的示例代码可能类似于:
class A
class B extends A
class C extends B
class G[-T <: B] {
def x(t: T) = {}
}
val g1 = new G[B]
val g2 = new G[C]
val list: List[G[C]] = List(g1, g2) //compiles.
list.head.x(new C)
如前所述,班级G
在逆变位置使用类型T
。
所以,如果你想(如你的示例代码中所示)是协变的,而不是被迫使用矛盾,那么你所要做的就是确保你只在&#34; covariant places&中使用类型T
#34;,例如:
class A
class B extends A
class C extends B
class G[+T <: B] {
def x[R >: T <: B](r: R) = {}
}
val g1 = new G[B]
val g2 = new G[C]
val list: List[G[B]] = List(g1, g2) //compiles.
list.head.x(new B)
请注意方法x上的奇怪类型约束:R >: T <: B
只能写为R >: T
,这意味着T
的任何超类型,即你也可以得到:
list.head.x(new A)
进行编译,这就是为什么您需要R
上的约束也是B
的子类型