8

以下问题使我感到困惑:

trait Foo[A]
class Bar[A](set: Set[Foo[A]] = Set.empty)

这产生

<console>:8: error: polymorphic expression cannot be instantiated to expected type;
 found   : [A]scala.collection.immutable.Set[A]
 required: Set[Foo[?]]
       class Bar[A](set: Set[Foo[A]] = Set.empty)
                                           ^

不得不在Set.empty. 为什么使用此默认参数进行类型推断会失败?以下作品:

class Bar[A](set: Set[Foo[A]] = { Set.empty: Set[Foo[A]] })

请注意,这与以下内容无关Set

case class Hallo[A]()
class Bar[A](hallo: Hallo[A] = Hallo.apply)  // nope

奇怪的是,这不仅有效:

class Bar[A](hallo: Hallo[A] = Hallo.apply[A])

......还有这个:

class Bar[A](hallo: Hallo[A] = Hallo())      // ???
4

1 回答 1

6

您可以直接在empty方法上指定类型,而不必添加额外的一组括号/大括号和类型注释:

class Bar[A]( set: Set[Foo[A]] = Set.empty[Foo[A]] )

至于为什么类型推断失败,请看这些问题:

更新:

我很抱歉,我仓促的回答太离谱了。上面帖子中的问题与此问题无关。@TravisBrown 在上面的评论中提出了一个非常好的观点。起初这似乎有效:

class Bar[A]( set: Set[A] = Set.empty )

但是,如果您实际上尝试调用构造函数,它会在使用站点失败:

new Bar[Int]
//  <console>:9: error: type mismatch;
//   found   : scala.collection.immutable.Set[Nothing]
//   required: Set[Int]
//  Note: Nothing <: Int, but trait Set is invariant in type A.
//  You may wish to investigate a wildcard type such as `_ <: Int`. (SLS 3.2.10)
//  Error occurred in an application involving default arguments.
//                new Bar[Int]

这表明编译器不会强制默认参数对所有人有效A,只是对某些人有效A。他们可能做出了这样的选择,因此您可以执行以下操作:

scala> case class MyClass[T](set: Set[T] = Set(0))
defined class MyClass

scala> MyClass() // defaults to MyClass[Int]
res0: MyClass[Int] = MyClass(Set(0))

scala> MyClass(Set('x)) // but I can still use other types manually
res1: MyClass[Symbol] = MyClass(Set('x))

但是,任何类型的参数化类型的嵌套都无法在构造函数的声明站点进行类型检查:

class Bar[A]( set: Set[Option[A]] = Set.empty )
// <console>:7: error: polymorphic expression cannot be instantiated to expected type;
//  found   : [A]scala.collection.immutable.Set[A]
//  required: Set[Option[?]]
//        class Bar[A]( set: Set[Option[A]] = Set.empty )

如果类型参数处于协变位置,则推理不会失败:

class Bar[ A ]( set: List[Foo[A]] = List.empty ) // OK

class Bar[ A ]( set: Map[Int,Foo[A]] = Map.empty ) // OK (unless you use it)

class Bar[ A ]( set: Map[Foo[A],Int] = Map.empty ) // BAD
// <console>:8: error: polymorphic expression cannot be instantiated to expected type;
//  found   : [A, B]scala.collection.immutable.Map[A,B]
//  required: Map[Foo[?],Int]
//            class Bar[ A ]( set: Map[Foo[A],Int] = Map.empty ) // BAD
//                                                       ^

这些是有效的,因为编译器Nothing默认选择协变类型。这适用于List,但如果您实际尝试调用它,则上面的第二个示例不起作用。

大多数这种怪异的原因可能是 Scala 处理默认参数的方式。编译器会自动向伴生对象添加一个额外的方法,然后无论您遗漏一个参数,编译器都会自动向伴生对象中的新方法添加一个方法调用,以生成缺少的参数。看起来将默认参数抽象为一个方法会破坏类型推断中的一些东西,这些东西可以用于正常的赋值。

我认为这些发现中的大多数都非常令人困惑。我从中得到的是,实际测试您的默认参数以确保它们在您尝试使用它们时不会破坏类型正确性非常重要!

于 2012-10-28T17:48:56.817 回答