Ответ 1
При работе с чистыми функциями всегда лучше придерживаться полных реализаций и вместо использования null
, require
или броска исключений для кодирования всех сбоев данных. Такие типы, как Option
и Either
, существуют именно для этого. Оба они являются монадами, поэтому вы можете использовать для них "for" -syntax.
Следующая модификация вашего кода показывает полную функцию apply
, которая дает только содержательное значение, когда входной Java URI
не предоставляет null
s. Мы кодируем понятие "значимого", завершая результат в Option
.
object Url {
def apply(uri: java.net.URI): Option[Url] =
for {
protocol <- Option(uri.getScheme)
host <- Option(uri.getHost)
path <- Option(uri.getRawPath)
}
yield Url(protocol, host, path)
}
case class Url(protocol: String, host: String, path: String)
Функция Option
- это стандартная функция проверки нуль, которая отображает null
в None
и переносит значения в Some
.
Что касается ваших "настроек", вы можете реализовать null
-checking в них аналогичным образом, используя Option
. Например:.
case class Url(protocol: String, host: String, path: String) {
def protocol(value: String): Option[Url] =
Option(value).map(v => copy(protocol = v))
// so on...
}
Однако я бы советовался с этим. В Scala традиционно никогда не использовать null
s, поэтому принято также применять только null
-обслуживание для "мостовых" API, когда вы получаете значения из Java libs. То, что null
-checking имеет смысл при преобразовании из java.net.URI
, но после этого вы находитесь в мире Scala, где не должно быть null
s, и, следовательно, null
-checking просто становится избыточным.