我在scala论坛上遇到了这个post。
这是一个回顾:
我刚刚意识到Scala编译器似乎没有在通配符上“延续”类型边界:
scala> class Foo { def foo = 42 }
defined class Foo
scala> class Bar[A <: Foo](val a: A)
defined class Bar
scala> def bar(x: Bar[_]) = x.a.foo
:7: error: value foo is not a member of Any
我希望方法栏的参数仍然是Foo的上限,即使它的确切类型参数在方法中不重要。 这种行为有特定原因吗?
然后讨论进入规范解释争议:)
最后海报提出了这样的解释:
但是,如果编译器为Bar [_]执行此操作,出于一致性原因,它也必须为Bar [A]执行此操作。然而,后者会产生一些奇怪的后果。 def bar [A](x:Bar [A],y:Bob [A]) 例如,突然必须携带Bob的隐式类型绑定。如果鲍勃有自己的类型限制,那将是非常混乱的。
我没有得到,因为
def bar[A](x: Bar[A])
单独不会编译,因为Bar类型参数是有界的。
无论如何,我相信以下内容是完全合理的:
def bar(x: Bar[_], y : Bob[_])
作为一种解决方法,他们建议:
def bar(x: Bar[_ <: Foo]) = x.a.foo
除了干燥之外,它还会让事情变得困难:
让我们考虑一个树
abstract class Tree[T <: Tree[T]] { val subTree : List[T] }
现在,您将如何定义递归遍历树的函数(显然在Tree类之外定义):
def size( tree : Tree[_] ) = tree.subTree.size + tree.subTree.map(size(_)).sum
显然不起作用,因为subTree将变成List [Any],所以我们需要一个类型参数:
def size[T <: Tree[T]]( tree : T ) = ...
甚至丑陋:
class OwnATree( val myTree : Tree[_] ){}
应该成为
class OwnATree[T <: Tree[T]]( val myTree : T ){}
等等...
我相信某处出了点问题:)
答案 0 :(得分:5)
使用size
和OwnATree
完成所需内容的最简单方法是使用存在类型:
def size(tree: Tree[T] forSome { type T <: Tree[T] }): Int =
tree.subTree.size + tree.subTree.map(size(_)).sum
和
class OwnATree(val myTree: Tree[T] forSome { type T <: Tree[T] })
你的Tree[_]
版本实际上也在使用存在类型 - 它们只是包含在一些语法糖中。换句话说,
def size(tree: Tree[_]): Int = ...
只是语法糖:
def size(tree: Tree[T] forSome { type T }): Int = ...
你不能将你需要的约束添加到下划线版本,但你可以添加到desberared版本。