请考虑case class Foo[A, B <: List[A]](l: B) { ... }
或类似的事情。特别是A
以及B
需要在Foo
的正文中提供。
编译器是否可以自动推断A
?例如,Foo(List(1,2,3))
失败,因为类型检查器会将A
推断为Nothing
。也许有一种方法可以使用类型成员来解决这个问题?
我有一种感觉,我在这里忽略了一些非常简单的东西;)
编辑:我刚刚发现使用其他类型参数X
的工作正常,但是我不明白为什么会这样:
scala> case class Bar[A, B[X] <: List[X]](l: B[A])
defined class Bar
scala> Bar(List(1,2,3))
res11: Bar[Int,List] = Bar(List(1, 2, 3))
有人可以向我解释一下吗? 这是一个统一问题吗?
编辑2:使用[A, B[X] <: List[X]](l: B[A])
会对某些层次结构产生不良影响(尽管这并不是什么大问题)。更有趣的是,我偶然发现了Josh Suereth的blog post,隐含地表明[A, B <: List[A]](l: B with List[A])
同样有效......不需要暗示等等。
答案 0 :(得分:5)
它并没有真正回答“为什么”部分,对不起,但这里有一些你可以玩的技巧。首先,由于您未在示例中使用X
,因此可以写:
case class Bar[A,B[_] <: Seq[_]](l : B[A])
然后:
scala> Bar(List(1,2,3))
resN: Bar[Int,List] = Bar(List(1, 2, 3))
(我使用协变Seq
代替List
来表明它也适用于子类型。另请注意,这不等于使用额外的X
,请参阅评论。)不幸的是,每次你想要使用序列类型时,你需要编写B[A]
,即手动实例化它。解决这个问题的一种方法是改为编写:
case class Bar[A,B](l : B)(implicit ev : B <:< Seq[A])
行动中:
scala> Bar(List(1,2,3))
resN: Bar[Int,List[Int]] = Bar(List(1, 2, 3))
...你得到的类型参数A
和B
实例化就像你一直都知道的那样。
答案 1 :(得分:5)
编译器无法自动推断A.但是,如果有可能,则必须说A应该是Int的超类型,所以Int或Any,而不仅仅是Int!。因为List [Int]&lt ;: List [Any]。因此,如果编译器会推断Int为A,那么限制性太强。
换句话说:
case class Foo[A, B <: List[A]](l: B) { ... }
然后将其称为Foo(List(1,2,3))
,你说 A必须是一个类型,其中包含它的列表应该是Int 列表的超类型。
所以有效A必须是Int的超类型,因为List是协变的。
case class Bar[A, B[X] <: List[X]](l: B[A])
然后将其称为Foo(List(1,2,3))
,您说 A必须是Int 。
案例(2)没有留下任何除了Int之外的空间。
然而,情况(1)为A留下了除Int以外的空间,例如它可能是任何。
你可以看到这个,因为你可以通过Foo[Any, List[Int]](List(1,2,3))
来调用它。在案例(2)中你将无法做到这一点。
所以这两个案例并不相同。
答案 2 :(得分:2)
根据Alexey Romanov,原则上可以自动推断出类型,但目前还不是。
因此,至少目前,所有应推断的类型参数都必须在参数中明确显示。