Пожалуйста, объясните использование метода orNull Option
класс опций Scala имеет orNull
метод, подпись которого показана ниже.
orNull [A1 >: A](implicit ev : <:<[Null, A1]) : A1
я озадачен неявной вещью. Кто-нибудь объяснит, как его можно использовать, в идеале, на примере?
5 ответов
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 к идиоме value|null Java (что, конечно, плохо). Теперь только значения AnyRef (экземпляры классов) могут принимать значение null.
так что мы хотели бы def orNull[A >: Null] = ....
. Но уже установлено, и мы не хотим ограничивать его в определении признака. Таким образом, orNull ожидает доказательства того, что A является нулевым типом. Это доказательство в форме неявной переменной (отсюда и название "ev")
<:<[Null, A1]
можно записать как Null <:< A1
видя это так, он похож на "Null <:>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
, будет выброшено исключение).
неявный параметр проверяет, является ли значение в коробке nullable.
хороший пример использования можно найти в комментарии orNull
:
val initialText: Option[String] = getInitialText
val textField = new JComponent(initialText.orNull,20)
помните, что в Scala примитивные типы и ссылочные типы унифицированы, но только ссылочные типы могут быть обнулены. Неявное просто позволяет компилятору подтвердить, что A1 является ссылочным типом.
чтобы понять, почему это полезно, IttayD предоставил хорошее объяснение:
так что мы хотели бы def
orNull[A >: Null] = .....
но A все уже готово, а мы не хотим. ограничьте его в определении черта характера. Поэтому, orNull ожидает доказательство того, что A-тип, подлежащий аннулированию. Это доказательство в форме неявная переменная (отсюда и название 'ev')
в общем, ограничения типа полезны, когда вы хотите есть методы (например,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
параметр типа класс!--8--> определяется как контравариантный).
как уже упоминалось, самый интуитивный способ думать об ограничениях типа-читать его как привязку типа (т. е. читать его как Null <:>Null не соответствует Int
и нет неявного значения для<:> отвечает String
и компилятор найдет неявный параметр.
кстати, вот еще один обзоры ответа.
Re: "Как" это используется-одно место, где мы находим это полезным, - это при работе с сопоставлениями Java api, где null
является обычным явлением, например, в JDBC подготовленные операторы для столбцов sql nullable. The Option
Al внутренние поля модели могут быть сопоставлены:
stmt.setDate("field", myModel.myDateField.orNull)
вместо более многословен:
stmt.setDate("field", myModel.myDateField.getOrElse(null))