Использование Option повсюду кажется немного неудобным.Я делаю что-то неправильно?

StackOverflow https://stackoverflow.com/questions/2476835

  •  21-09-2019
  •  | 
  •  

Вопрос

Благодаря статьям, которые я прочитал о Option класс, который помогает вам избежать исключений NullPointerException, я начал использовать его повсюду.Представьте себе что-то вроде этого:

var file:Option[File] = None

и позже, когда я его использую:

val actualFile = file.getOrElse(new File("nonexisting"))
if(actualFile.getName.equals("nonexisting")) { // instead of null checking

}
else { // value of file was good

}

Делать подобные вещи мне кажется не совсем «правильным».Я также заметил, что .get стал устаревшим..Ребята, вы тоже делаете что-то подобное с Option, или я иду неправильным путем?

Это было полезно?

Решение

Обычно возвращаться не лучшая идея. Option а затем использовать getOrElse для получения некоторого контрольного значения, которое означает «не найдено».Это то что Option предназначен для:чтобы указать, что значение не найдено!

Option действительно демонстрирует свою мощь при использовании в сочетании с конструкциями функционального программирования, такими как map и foreach.Это наиболее эффективно при работе с несколькими вариантами.Например, предположим, что я пишу метод, который принимает строку и возвращает мне файл, но только если файл существует и является файлом, а не каталогом:

import java.io._;
def niceFile1(s: String): File = {
  val f = new File(s);
  if (f.exists && !f.isDirectory) f else null
}
def niceFile2(s: String): Option[File] = {
  val f = new File(s);
  if (f.exists && !f.isDirectory) Some(f) else None
}

До сих пор, используя null проще - по крайней мере, пока вы не забудете, что это может дать вам null и вы получите NPE.В любом случае, давайте теперь попробуем его использовать.

def niceFopen1(s: String) = {
  val f = niceFile1(s);
  if (f!=null) new FileInputStream(f) else null;
}
def niceFopen2(s: String) = niceFile2(s).map(f => new FileInputStream(f))

Посмотрите, что произошло!В первом случае нам пришлось вручную выполнять логические тесты и создавать временные переменные.Фу!Во втором случае map сделал за нас всю грязную работу:None было сопоставлено с None, и Some(file) был сопоставлен с Some(fileinputstream).Легкий!

Но дальше становится лучше.Возможно, мы хотим узнать размер целой группы файлов:

def totalSize2(ss: Seq[String]) = {
  (0L /: ss.flatMap(niceFile2)){(sum,f) => sum+f.length}
}

Подождите, что здесь происходит? А как насчет всех этих None?Разве нам не нужно обращать на них внимание и как-то справляться с ними?Ну, вот где flatMap приходит в:он объединяет все ответы в один список. None является ответом нулевой длины, поэтому он игнорирует его. Some(f) есть один ответ--f--поэтому он помещает его в список.Затем мы используем складку, чтобы сложить все длины — теперь, когда все элементы в списке действительны.Довольно приятно!

Другие советы

Это хорошая идея не решить ценность Option, но применять логика к тому, что она содержит:

findFile.foreach(process(_))

В основном это обрабатывает File если кто-то найден и ничего не делает иначе (и это эквивалентно первому Томасу for понимание, потому что for компилируется в вызов foreach).Это более краткая версия:

findFile match {
  case Some(f) => process(f)
  case None =>
}

Более того, самое замечательное в этом то, что вы можете цепь операции, что-то вроде:

(findLiveFile orElse fileBackupFile orElse findTempFile).foreach(process(_)

В большинстве случаев вы будете использовать сопоставление с образцом

file match {
   case Some(f) => { .. } //file is there
   case _ => { .. } //file is not there 
}

Если вас интересует только файл, если он есть, вы можете использовать выражение for

for(f <- file) { //file is there 
}

Затем вы можете создать цепочку выражений для работы на нескольких уровнях контейнеров.

for{ 
  option <- List(Some(1), None, Some(2))
  f <- option
} yield f

res0: List[Int] = List(1, 2)

Альтернативно вы можете использовать isDefined и получить:

if(option.isDefined) {
   val x = option.get;
} else {
}

get не устарел в Scala 2.8.0.Beta-1.

Вот альтернатива:

var file:Option[File] = None
// ...
file map (new File(_)) foreach { fh =>
  // ...
}

Однако если вам нужно что-то сделать, если файл существует, и что-то еще, если его нет, match утверждение более уместно:

var file:Option[File] = None
// ...
file map (new File(_)) match {
  case Some(fh) =>
    // ...
  case None =>
    // ...
}

Это почти то же самое, что if утверждение, но мне нравится, что оно лучше связывает природу для таких вещей, как Option, где я также хочу извлечь значение.

В основном повторяю то, что все говорят, но я думаю, что есть как минимум 4 различные ситуации, с которыми вы столкнетесь, когда у вас будет такая переменная, допускающая значение NULL:

1.Файл не должен быть нулевым

load(file.get())

Вызовет исключение, когда вы попытаетесь использовать файл.Потерпите неудачу быстро, ура!

2.Файл имеет значение null, но в данном случае у нас есть разумное значение по умолчанию:

load(file.getOrElse(new File("/defaultFile")))

3.Две ветви логики в зависимости от существования файла:

  file match {
    case Some(f) => { .. } //file is there
    case _ => { .. } //file is not there 
  }

4.Нет операции, если файл имеет значение null, то есть молчаливый сбой.Я не думаю, что этот вариант часто предпочтительнее для меня в реальной жизни:

for (f <- file) {
//do some stuff
}

Или, может быть, более четко?

if (f.isDefined) {
  //do some stuff
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top