我使用F-bounded类型以便能够返回当前类型
trait Board[T <: Board[T]] {
def updated : T
}
我正在尝试编写一个使用它的通用辅助方法。
问题是:为什么以下不编译?
object BoardOps {
def updated(board: Board[_]) = {
board.updated.updated
}
}
错误为value updated is not a member of _$1
我已经找到了这2个解决方法。它们是等价的吗?
object BoardOps {
def updated(board: Board[_<:Board[_]]) = {
board.updated.updated
}
}
object BoardOps {
def updated[T <: Board[T]](board: T) : T = {
board.updated.updated
}
}
答案 0 :(得分:5)
以下为什么不编译?
使用Board[_]
作为参数类型告诉编译器&#34;我不关心板内的参数类型&#34;。也就是说,对编译器来说,这是一种存在类型,它不知道有关该类型的任何细节。因此,board.updated
会返回&#34;不可言换的&#34;或 opaque 类型,因为我们告诉编译器&#34;扔掉&#34;那种类型的信息。
我已经找到了这2个解决方法。它们是等价的吗?
您之前的示例使用带有约束的存在类型作为Board[_]
的子类型,或者更正式地编写:
Board[T] forSome { type T <: Board[U] }
编译器实际上命名为T -> $_1
和U -> $_2
同样,我们对内部类型参数一无所知,只是它在Board[_]
上有一个上限。后一个示例使用名为T
的通用量化类型,编译器可以使用该类型将方法的返回类型推导为特定类型T
而不是Any
。< / p>
要回答你的问题,不,它们不是等同的。存在的和普遍量化的类型彼此对偶。有关存在感的更多信息,请参见What is an existential type?和https://www.drmaciver.com/2008/03/existential-types-in-scala/
答案 1 :(得分:0)
它不编译,因为只要你编写Board[_]
,编译器就不会推断出有关匿名类型参数_
的任何有用信息。
有几种解决方法(与您已经提出的方法不同):
Board[X] forSome { type X <: Board[X] }
forSome
存在量化这可以通过forSome
- 存在量化来轻松解决:
import scala.language.existentials
object BoardOps_forSome {
def updated(board: Board[X] forSome { type X <: Board[X] }) = {
board.updated.updated.updated
}
}
这允许您无限次地调用updated
。
您可以使用模式匹配实际解决问题,而无需更改签名。例如,这种不敬虔的构造允许您应用方法updated
三次(无限次地工作):
object BoardOps_patternMatch {
def updated(board: Board[_]) = {
board match {
case b: Board[x] => b.updated match {
case c: Board[y] => c.updated match {
case d: Board[z] => d.updated
}
}
}
}
}
这是因为只要将未知类型绑定到类型变量x
,y
,z
,编译器就会被迫做一些额外的推理工作,并推断它实际上必须是x <: Board[_$?]
等。不幸的是,它一次只做一步推断,因为如果它试图计算最精确的类型,那么类型计算会发散。
请注意,您的第一个解决方法只能运行两次:
object BoardOps_upperBound_once {
def updated(board: Board[_<:Board[_]]) = {
board.updated.updated // third .updated would not work
}
}
因此,它不等同于您的第二种解决方法,它也可以无限次使用,此处示例包含三个updated
的调用:
object BoardOps_genericT {
def updated[T <: Board[T]](board: T) : T = {
board.updated.updated.updated
}
}