Пожалуйста, объясните использование метода Option или Null - PullRequest
16 голосов
/ 17 декабря 2010

Класс Option Scala имеет метод orNull, подпись которого показана ниже.

orNull [A1 >: A](implicit ev : <:<[Null, A1]) : A1

Я сбит с толку неявной вещью. Кто-нибудь, пожалуйста, объясните, как это можно использовать, в идеале, на примере?

Ответы [ 5 ]

31 голосов
/ 17 декабря 2010
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 | null (что, конечно, плохо).Теперь только значения AnyRef (экземпляры классов) могут принимать нулевое значение.

Итак, нам бы хотелось, чтобы def orNull[A >: Null] = .....Но A уже установлен, и мы не хотим ограничивать его в определении черты.Следовательно, orNull ожидает доказательства того, что A является обнуляемым типом.Это свидетельство в форме неявной переменной (отсюда и имя 'ev')

<:<[Null, A1] можно записать как Null <:< A1, если смотреть так, оно похоже на 'Null <: A1'.<: <определяется в Predef, а также в методе, который предоставляет неявное значение с именем <code>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
5 голосов
/ 17 декабря 2010
Цель

orNull заключается прежде всего в обеспечении совместимости Option с Java.Хотя использование null в Scala не рекомендуется, некоторые интерфейсы могут ожидать получения пустых ссылок.

orNull имеет простую реализацию:

def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null

В соответствии с этим, null будет возвращено не только для значений в штучной упаковке (Some(null)), но и для None(например, если вы вызовете None.get, будет сгенерировано исключение).

Неявные проверки параметров, если значение в штучной упаковке обнуляется.

Хороший пример использования можно найти прямо в комментариях к orNull:

val initialText: Option[String] = getInitialText
val textField = new JComponent(initialText.orNull,20)
4 голосов
/ 17 декабря 2010

Помните, что в Scala примитивные типы и ссылочные типы унифицированы - но только ссылочные типы допускают обнуление Неявное просто позволяет компилятору подтвердить, что A1 является ссылочным типом.

2 голосов
/ 20 декабря 2010

Чтобы понять, почему это полезно, 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 класса <:< определен как контравариантный).

Как уже упоминалось, наиболее интуитивный способ думать об ограничениях типов - это читать их как границы типов (т.е. читать как Null <: Int).<code>Null не соответствует Int и не существует неявного значения для <: <[Null, Int].С другой стороны, <code>Null соответствует String, и компилятор найдет неявный параметр.

Кстати, вот еще один связанный ответ .

0 голосов
/ 22 мая 2014

Re: «как» это используется - единственное место, где мы находим это полезным, - это когда я работаю с отображениями Java API, где null является обычным явлением, например, на jdbc подготовил операторы для обнуляемых столбцов sql. Option Все внутренние поля модели могут быть сопоставлены:

stmt.setDate("field", myModel.myDateField.orNull)

Вместо более многословного:

stmt.setDate("field", myModel.myDateField.getOrElse(null))
...