Простой ответ - нет, система типов не может сказать вам, есть ли у класса конструктор по умолчанию. Помните, что у классов case обычно нет конструктора по умолчанию, так как классы case без аргументов устарели Концепция конструкторов по умолчанию не очень полезна для неизменных объектов. AFAIK, нет никаких причин, почему не должно быть в принципе (Scala поддерживает структурные типы, где у типа должен быть метод с определенным именем), но это потребовало бы изменения языка. Вы можете проверить во время выполнения с отражением, но это не то, что вы хотите.
Однако вы можете использовать шаблон класса типов, чтобы значение по умолчанию находилось в области видимости. Концептуально это очень похоже на добавление дополнительного аргумента по умолчанию, как предлагается в ОП, но с использованием имплицитов, чтобы скрыть их. Он активно используется в библиотеке коллекций. Ответ об отсутствующем факторе с использованием scalaz.Zero
является особым случаем этого, но его очень легко сделать в ванильном 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)