5

我在 scala 论坛上看到了这篇文章。

这是一个回顾:

我刚刚意识到 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

我希望方法 bar 的参数仍然是 Foo 的上限,即使它的确切类型参数在方法中并不重要。这种行为有特定的原因吗?

然后讨论进入规范解释争议:)

解释?

最终海报提出了这个解释:

但是,如果编译器为 Bar[_] 执行此操作,出于一致性原因,它也必须为 Bar[A] 执行此操作。然而,后者会产生一些奇怪的后果。例如 def bar[A](x: Bar[A], y: Bob[A]) 突然必须为 Bob 携带一个隐式类型绑定。如果 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 ){}

等等等等……

我相信某处有问题:)

4

1 回答 1

5

完成你想要的最简单的方法sizeOwnATree使用存在类型:

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 = ...

您不能将所需的约束添加到下划线版本,但可以添加到去糖版本。

于 2012-08-17T20:17:12.217 回答