Правильный способ построения объекта Option: Option(значение) или Some(значение)
Каковы плюсы и минусы 2 способов инициации Option
объекты:
1.
def getAmount: Option[Int] = {
val a: Int = 1
Option(a)
}
2.
def getAmount: Option[Int] = {
val a: Int = 1
Some(a)
}
Какой я должен использовать?
2 ответа
Есть два важных различия. Первый, Option
вернет None
если его аргумент нулевой:
scala> val x: Option[String] = Some(null)
x: Option[String] = Some(null)
scala> val y: Option[String] = Option(null)
y: Option[String] = None
Это может быть полезно, но это не всегда то, что вы хотите, и (что не менее важно) предполагает, что есть разумный шанс, что в некоторых случаях аргумент может быть нулевым, что может вводить в заблуждение.
Some
больше подходит для случаев, когда вы хотите произвести Option
вокруг значения, которое вы знаете, не равно нулю. К сожалению, второе отличие заключается в том, что тип возвращаемого значения Some(foo)
является Some[Whatever]
не Option[Whatever]
что может быть очень неудобно в некоторых ситуациях, когда Some
вывод означает, что вы получите ошибки типа при попытке использовать None
или же Option
в определенных позициях позже. В этих случаях вы должны использовать Some(foo): Option[Whatever]
, что довольно неприятно.
Например, предположим, что у нас есть список строк, представляющих (мы надеемся) целые числа, и мы хотим проанализировать и суммировать их. Мы хотим None
если есть ошибка разбора и Some(total)
иначе. Ниже приведен довольно разумный способ сделать это за один проход с использованием стандартной библиотеки:
List("1", "2", "3").foldLeft(Some(0)) {
case (acc, item) => for {
t <- acc
n <- util.Try(item.toInt).toOption
} yield t + n
}
За исключением того, что это не работает - мы получаем ошибку типа:
<console>:10: error: type mismatch;
found : Option[Int]
required: Some[Int]
t <- acc
^
Мы можем исправить это, написав .foldLeft(Some(0): Option[Int])
, но тьфу.
Это не проблема в вашем конкретном примере, потому что тип возвращаемого значения явно Option[Int]
так что вам не нужно беспокоиться о выводе типа. В таком случае Some(a)
это правильный выбор.
Как примечание, Скалаз предоставляет some
а также none
конструкторы, которые помогут вам избежать проблемы вывода типа и шумных решений, таких как Some(foo): Option[Whatever]
:
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> some(10)
res0: Option[Int] = Some(10)
scala> none[Int]
res1: Option[Int] = None
Оба возвращаемых типа Option
, что делает вывод типа намного проще. Вы можете тривиально определить их самостоятельно, если не хотите использовать Scalaz:
scala> def some[A](a: A): Option[A] = Some(a)
some: [A](a: A)Option[A]
scala> def none[A]: Option[A] = None
none: [A]=> Option[A]
Если вы используете их вместо Some
а также None
Вам никогда не придется беспокоиться о неадекватно определенном типе, который выводится.
Подводя итог: используйте Option(foo)
только в ситуациях, когда аргумент может быть нулевым (что в идеале должно быть только для таких вещей, как совместимость с Java). использование Some(foo)
в случаях, когда значение было явно введено как Option
, Если предполагаемый тип будет Some[Whatever]
, добавить : Option[Whatever]
введите аннотацию или используйте что-то вроде Scalaz's some
,
Что касается меня, то это просто вопрос здравого смысла. Конечно, вы можете представить себе случай, когда вы ожидаете чего-то точно такого же типа, как "Некоторые", и "Нет" не допускается. Но обычно второй способ выглядит гораздо более естественным: возвращаемый тип - это Option, а фактическая реализация - Some(x) или None. Технически, из исходного кода, Option(x) вызывает apply()
метод сопутствующего объекта:
object Option {
import scala.language.implicitConversions
implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = xo.toList
def apply[A](x: A): Option[A] = if (x == null) None else Some(x)
def empty[A] : Option[A] = None
}
И некоторые (а) звонки apply()
метод для случая класса..
final case class Some[+A](x: A) extends Option[A] {
def isEmpty = false
def get = x
}
Все остальные методы одинаковы. Вариант использования с null
Объекты довольно хорошо объяснены в ответе Travis Brown.