Я хотел бы использовать классы классов для более ясного описания типов моих данных, чтобы получить более высокую статическую корректность. Цель состоит в том, чтобы иметь 100% статическую уверенность в том, что любое существующее значение Age
всегда содержит действительный возраст человека (не считая того, что правила инкапсуляции можно обойти с помощью отражения).Алгебраические типы, которые могут быть созданы только с помощью функции дозорного устройства
Например, вместо того, чтобы использовать Int
хранить возраст лиц, у меня есть:
case class Age(x: Int) extends AnyVal
def mkAge(x: Int) = if (0 <= x && x <= 150) Some(Age(x)) else None
def unwrapAge(age: Age) = age.x
однако, эта реализация страдает от того, что Age
все еще могут быть обработаны без прохождения через mkAge
и unwrapAge
.
Далее я попытался сделать конструктор приватным:
case class Age private(x: Int) extends AnyVal
object Age {
def make(x: Int) = if (0 <= x && x <= 150) Some(Age(x)) else None
def unwrap(age: Age) = age.x
}
однако, в то время как это делает предотвратить Age
от того экземпляра, используя новый (например, new Age(3)
), в object Age
автоматически сгенерированный apply(x: Int)
все еще легко доступен.
Итак, вот вопрос: как скрыть как конструктор, а также по умолчанию метод apply
в объекте компаньона из ничего, кроме Age.make
или mkAge
?
Хотелось бы избежать использования обычного (не case
) класса и правильно реплицировать автогенерированные методы в class Age
и object Age
вручную.
Это не скомпилировано в 2.10.3: «error: value class должен иметь общедоступный параметр val». И даже если бы это было так, это не помогло бы. –
@AlexeyRomanov Извините, тогда это невозможно с 2.10. Я скомпилировал его с помощью 2.11. Он достигает вашей цели: ни метод-конструктор, ни метод применения сопутствующего объекта не могут быть вызваны извне, а х также не является общедоступным. Каким образом это не помогает? – Madoc
"или метод приложения сопутствующего объекта можно вызвать извне". Тогда это ошибка Scala, поскольку видимость 'Age.apply' не должна зависеть от' x'. Согласно http://www.scala-lang.org/files/archive/spec/2.11/05-classes-and-objects.html#case-classes, он всегда открыт для публики. –