简单的答案是否定的,类型系统无法告诉你一个类是否有默认构造函数。请记住,案例类通常没有默认构造函数,因为不推荐使用无参数案例类。默认构造函数的概念对于不可变对象不是那么有用。 AFAIK 原则上没有理由不应该存在(Scala 确实支持结构类型,其中类型必须具有特定名称的方法),但它需要更改语言。您可以在运行时使用反射进行检查,但这不是您想要的。
但是,您可以使用类型类模式强制默认值在范围内。这在概念上与 OP 中建议的添加额外的默认争论非常相似,但使用隐式来隐藏它们。它在收藏库中大量使用。使用scalaz.Zero 的missingfaktor 的答案是一个特例,但在普通Scala 中很容易做到,并且对于一些不一定为零的任意默认值。
case class Default[T](default: T)
case class Foo(value: String)
case class Bar(value: Int)
implicit val fooDefault = Default(Foo("I'm a default Foo")) // note 1
现在让我们看一个示例用法:
def firstItem[T](lst: List[T]) (implicit ev: Default[T]) = // note 2
if (lst.isEmpty) ev.default else lst.head
val fooList = List(Foo("cogito"), Foo("ergo"), Foo("sum"))
val emptyFooList = List[Foo]()
val barList = List(Bar(101), Bar(102))
val emptyBarList = List[Bar]()
firstItem(fooList) // Foo("cogito")
firstItem(emptyFooList) // Foo("I'm a default Foo")
firstItem(barList) // ** error: missing implicit **
所以我们看到它使用List[Foo] 编译,但不接受List[Bar],因为没有隐式Default[Bar](注3)。
注 1:这个隐式可以在 object Foo 上定义 - 如果您在其他地方导入该类,这将确保它在范围内。但不一定非得如此:您也可以为任意类定义类似的隐式,Int、String 等等(试试看)。
注 2:这相当于加糖版本 def firstItem[T: Default](lst: List[T]) =...,您可以在其中召唤 ev 和 implicitly[Default[T]]。任君挑选。
注意 3:我们可以通过简单地提供一个来使其工作:
firstItem(barList)(Default(Bar(42))) // Bar(101)
firstItem(emptyBarList)(Default(Bar(42))) // Bar(42)