Как проверить коротко или неверно в Scala?
В Groovy языке очень просто проверить для null
или false
как:
groovy код:
def some = getSomething()
if(some) {
// do something with some as it is not null or emtpy
}
В Groovy, если some
есть null
или является пустой строкой или является нулевым числом и т.д., будет оцениваться как false
. Что такое сжатый метод тестирования для null
или false
в Scala?
Каков простой ответ на эту часть вопроса, предполагая, что some
- это просто тип Java String?
Также еще один лучший метод в Groovy:
def str = some?.toString()
что означает, что если some
не null
, тогда метод toString
на some
будет вызываться вместо того, чтобы бросать NPE в случае, если some
был null
. Что похоже в Scala?
Ответы
Ответ 1
Что вам может не хватать, так это то, что функция типа getSomething
в Scala, вероятно, не вернет нулевую, пустую строку или нулевое число. Функция, которая может возвращать значимое значение или, возможно, не имеет в качестве возвращаемого значения Option
- возвращает Some(meaningfulvalue)
или None
.
Затем вы можете проверить это и обработать значимое значение с помощью
val some = getSomething()
some match {
case Some(theValue) => doSomethingWith(theValue)
case None => println("Whoops, didn't get anything useful back")
}
Поэтому вместо того, чтобы пытаться закодировать значение "сбой" в возвращаемом значении, Scala имеет определенную поддержку общего случая "вернуть что-то значимое или указать отказ".
Сказав, что Scala совместим с Java, а Java возвращает значения из функций все время. Если getSomething
- это функция Java, которая возвращает null, существует объект factory, из которого будут выведены некоторые или None из возвращаемого значения.
So
val some = Option(getSomething())
some match {
case Some(theValue) => doSomethingWith(theValue)
case None => println("Whoops, didn't get anything useful back")
}
... который довольно прост, я утверждаю, и не пойду на NPE на вас.
Другие ответы делают интересные и идиоматические вещи, но это может быть больше, чем вам нужно прямо сейчас.
Ответ 2
Ну, Boolean
не может быть null
, если не передано как параметр типа. Способ обработки null
состоит в том, чтобы преобразовать его в Option
, а затем использовать все элементы Option
. Например:
Option(some) foreach { s => println(s) }
Option(some) getOrElse defaultValue
Так как Scala является статическим типом, вещь не может быть "нулевой или пустой строкой или является нулевым числом и т.д.". Вы можете передать Any
, который может быть любой из этих вещей, но тогда вам придется сопоставлять каждый тип, чтобы иметь возможность делать что-нибудь полезное с ним в любом случае. Если вы окажетесь в этой ситуации, вы, скорее всего, не будете выполнять идиоматические Scala.
Ответ 3
В Scala описанные вами выражения означают, что метод, называемый ?
, вызывается на объекте с именем some
. Регулярно объекты не имеют метода под названием ?
. Вы можете создать свое собственное неявное преобразование в объект с помощью метода ?
, который проверяет наличие null
ness.
implicit def conversion(x: AnyRef) = new {
def ? = x ne null
}
Вышеупомянутое, по сути, преобразует любой объект, на который вы вызываете метод ?
, в выражение в правой части метода conversion
(у которого есть есть ?
). Например, если вы это сделаете:
"".?
компилятор обнаружит, что объект String
не имеет метода ?
и переписывает его на:
conversion("").?
Иллюстрируется в интерпретаторе (обратите внимание, что при вызове методов на объектах вы можете опустить .
):
scala> implicit def any2hm(x: AnyRef) = new {
| def ? = x ne null
| }
any2hm: (x: AnyRef)java.lang.Object{def ?: Boolean}
scala> val x: String = "!!"
x: String = "!!"
scala> x ?
res0: Boolean = true
scala> val y: String = null
y: String = null
scala> y ?
res1: Boolean = false
Итак, вы можете написать:
if (some ?) {
// ...
}
Или вы можете создать неявное преобразование в объект с помощью метода ?
, который вызывает указанный метод для объекта, если аргумент не является null
- выполните следующее:
scala> implicit def any2hm[T <: AnyRef](x: T) = new {
| def ?(f: T => Unit) = if (x ne null) f(x)
| }
any2hm: [T <: AnyRef](x: T)java.lang.Object{def ?(f: (T) => Unit): Unit}
scala> x ? { println }
!!
scala> y ? { println }
чтобы вы могли написать:
some ? { _.toString }
Создавая (рекурсивно) ответ на soc, вы можете сопоставить соответствие по x
в приведенных выше примерах, чтобы уточнить, что ?
делает в зависимости от типа x
.: D
Ответ 4
Вы можете написать некоторую оболочку самостоятельно или использовать тип параметра.
Я действительно не проверял бы на null
. Если есть null
где-то, вы должны исправить его и не строить проверки вокруг него.
На вершине ответа axel22:
implicit def any2hm(x: Any) = new {
def ? = x match {
case null => false
case false => false
case 0 => false
case s: String if s.isEmpty => false
case _ => true
}
}
Изменить: похоже, это либо сбой компилятора, либо не работает. Я буду исследовать.
Ответ 5
Если вы используете extempore null-safe coalescing operator, вы можете написать пример str
как
val str = ?:(some)(_.toString)()
Он также позволяет вам цепочки, не беспокоясь о null
(таким образом, "коалесцируя" ):
val c = ?:(some)(_.toString)(_.length)()
Конечно, этот ответ касается только второй части вашего вопроса.
Ответ 6
То, что вы просите, есть в строке Safe Navigation Operator (?.) Groovy, andand gem от Ruby или вариант доступа экзистенциального оператора (?.) от CoffeeScript, Для таких случаев я обычно использую метод ?
моего RichOption[T]
, который определяется следующим образом
class RichOption[T](option: Option[T]) {
def ?[V](f: T => Option[V]): Option[V] = option match {
case Some(v) => f(v)
case _ => None
}
}
implicit def option2RichOption[T](option: Option[T]): RichOption[T] =
new RichOption[T](option)
и используется следующим образом
scala> val xs = None
xs: None.type = None
scala> xs.?(_ => Option("gotcha"))
res1: Option[java.lang.String] = None
scala> val ys = Some(1)
ys: Some[Int] = Some(1)
scala> ys.?(x => Some(x * 2))
res2: Option[Int] = Some(2)
Ответ 7
Использование сочетания шаблонов, предложенное в нескольких ответах, является хорошим подходом:
val some = Option(getSomething())
some match {
case Some(theValue) => doSomethingWith(theValue)
case None => println("Whoops, didn't get anything useful back")
}
Но, немного подробный.
Я предпочитаю map
an Option
следующим образом:
Option(getSomething()) map (something -> doSomethingWith(something))
Один лайнер, короткий, понятный.
Причиной этому является вариант, который можно рассматривать как некоторая коллекция - специальная снежинка коллекции, содержащая либо нулевые элементы, либо точно один элемент типа, и поскольку вы можете сопоставить List [A] с List [B], вы можете сопоставить опцию [A] с опцией [B]. Это означает, что если ваш экземпляр Option [A] определен, то есть он является некоторым [A], результатом является Some [B], иначе это None. Это действительно мощно!