Scala Класс option имеет метод orNull
, подпись которого показана ниже.
orNull [A1 >: A](implicit ev : <:<[Null, A1]) : A1
Я смущен неявной вещью. Кто-нибудь, пожалуйста, объясните, как это можно использовать, в идеале с примером?
Scala Класс option имеет метод orNull
, подпись которого показана ниже.
orNull [A1 >: A](implicit ev : <:<[Null, A1]) : A1
Я смущен неявной вещью. Кто-нибудь, пожалуйста, объясните, как это можно использовать, в идеале с примером?
scala> Some(1).orNull
<console>:10: error: could not find implicit value for parameter ev: <:<[Null,Int]
Some(1).orNull
^
scala> (None : Option[Int]).orNull
<console>:10: error: could not find implicit value for parameter ev: <:<[Null,Int]
(None : Option[Int]).orNull
scala> Some("hi").orNull
res21: java.lang.String = hi
scala> Some(null : String).orNull
res22: String = null
scala> (None : Option[String]).orNull
res23: String = null
Объяснение неявной вещи: orNull - это способ вернуться из значения Some | None для Java value | null idiom (что, конечно, плохо). Теперь только значения AnyRef (экземпляры классов) могут принимать нулевое значение.
Итак, нам бы понравилось def orNull[A >: Null] = ....
. Но А уже установлен, и мы не хотим ограничивать его в определении признака. Поэтому orNull ожидает доказательства того, что A является типом с нулевым значением. Это доказательство находится в форме неявной переменной (отсюда и имя "ev" )
<:<[Null, A1]
можно записать как Null <:< A1
, видя его так, что он похож на "Null <: A1". & Л;: < определяется в Predef, а также метод, который предоставляет неявное значение с именем conforms
.
Я думаю, что использование A1 здесь строго не требуется и потому, что orNull использует getOrElse (где заданное по умолчанию может быть супер-типом A)
scala> class Wrapper[A](option: Option[A]) {
| def orNull(implicit ev: Null <:< A): A = if(option.isEmpty) null else option.get
| }
defined class Wrapper
scala> new Wrapper(Some("hi")).orNull
res18: java.lang.String = hi
orNull
Целью является, в первую очередь, обеспечение совместимости Option
с Java. Хотя использование null
не рекомендуется в Scala, некоторые интерфейсы могут ожидать получения ссылок с нулевым значением.
orNull
имеет прямую реализацию:
def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null
В соответствии с этим, null
будет возвращен не только для вложенных нулей (Some(null)
), но и для None
(например, если вы вызываете None.get
, будет выбрано исключение).
Неявные проверки параметров, если значение в ящике равно NULL.
Хороший пример использования можно найти прямо в комментариях к orNull
:
val initialText: Option[String] = getInitialText
val textField = new JComponent(initialText.orNull,20)
Помните, что в Scala примитивные типы и ссылочные типы унифицированы, но только ссылочные типы имеют значение NULL. Неявный просто позволяет компилятору подтвердить, что A1 является ссылочным типом.
Чтобы понять, почему это полезно, IttayD предоставил приятное объяснение:
Так что нам бы понравилось
orNull[A >: Null] = .....
Но А уже установлены, и мы не хотим ограничить его в определении черта. Следовательно, orNull ожидает что A - тип с нулевым значением. Это свидетельство имеет форму неявная переменная (отсюда и название 'Эв')
Таким образом, ограничения типа полезны, когда вы хотите иметь методы (например, orNull
) для общего класса (например, Option
) с более конкретными ограничениями (например, Null <: A <: Any
), чем для самого класса (например, A <: Any
).
Это еще одна "функция", которая не встроена в язык, но предоставляется бесплатно благодаря неявным параметрам и аннотациям параметров типа. Чтобы понять это, посмотрите на определение <:<
:
// from Predef
sealed abstract class <:<[-From, +To] extends (From => To)
implicit def conforms[A]: A <:< A = new (A <:< A) {def apply(x: A) = x}
Для
scala> Some(1).orNull
<console>:10: error: could not find implicit value for parameter ev: <:<[Null,Int]
Some(1).orNull
компилятор ищет неявное значение типа <:<[Null, Int]
и найдет метод def conforms[A]: A <:< A
. Таким образом, должен быть A
, для которого <:<[A, A]
соответствует <:<[Null, Int]
. Нет A
, для которого это выполняется, и в результате компилятор будет жаловаться на недостающее неявное значение.
Однако для
scala> Some("hi").orNull
res21: java.lang.String = hi
нам повезло. Теперь компилятор пытается найти A
, для которого <:<[A, A]
соответствует <:<[Null, String]
. Это работает для A = String
, потому что Null
является подтипом String
, а параметр типа From
класса <:<
определяется как контравариантный).
Как уже упоминалось, наиболее интуитивным способом думать о ограничениях типа является чтение его как привязка типа (т.е. чтение его как Null <: Int). Null
не соответствует Int
, и нет никакого неявного значения для <: < [Null, Int]. С другой стороны, Null
соответствует String
, а компилятор найдет неявный параметр.
Кстати, вот еще один связанный ответ.
Re: "как" это используется - одно место, которое мы находим полезным, - это когда мы имеем дело с java-api-сопоставлениями, где null
является обычным явлением, например. на jdbc подготовленные операторы для нулевых столбцов sql. Внутренние поля модели Option
al могут быть отображены:
stmt.setDate("field", myModel.myDateField.orNull)
Вместо более подробного:
stmt.setDate("field", myModel.myDateField.getOrElse(null))