Правильный способ построения объекта 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.

Другие вопросы по тегам