Враховуючи параметр, який ідіоматичний спосіб отримати його значення або спробувати виняток?
def foo() : String = {
val x : Option[String] = ...
x.getOrException()
}
Враховуючи параметр, який ідіоматичний спосіб отримати його значення або спробувати виняток?
def foo() : String = {
val x : Option[String] = ...
x.getOrException()
}
NoSuchElementException
. Крім того, більшу частину часу цей метод може безпечно повернути None.
Відповіді:
(РЕДАКТУВАТИ: це не найкращий чи найідіоматичніший спосіб це зробити. Я написав це, коли не був знайомий із Scala. Я залишаю це тут для прикладу, як цього не робити. Сьогодні я б зробив як @TravisBrown)
Я думаю, це справді зводиться до двох речей:
Якщо на той момент у вашому коді ви очікуєте, що значення буде там, а у віддаленому випадку, якщо це не ви хочете, щоб ваша програма швидко вийшла з ладу , то я б зробив лише нормальний get
і дозволив Scala кинути a, NoSuchElementException
якщо не було значення :
def foo (): Рядок = { val x: Option [String] = ... x.get }
Якщо ви хочете розглянути справу інакше (киньте власний виняток), я думаю, що більш елегантний спосіб буде виглядати так:
def foo (): Рядок = { val x: Option [String] = Немає x збіг { case Деякі (value) => value case None => кинути новий MyRuntimeException ("бла") } }
І звичайно, якщо ви хочете надати своє власне альтернативне значення для випадку, яке Option
є, None
ви просто використаєте getOrElse
:
def foo (): Рядок = { val x: Option [String] = Немає x.getOrElse ("моє альтернативне значення") }
getOrElse
слід віддавати перевагу, якщо це все, що хочеться зробити. Я просто хотів проілюструвати випадок "якщо визначено, оцінюйте до X, якщо ні, киньте виняток".
getOrElse
так:x.getOrElse(throw new MyRuntimeException("message"))
throw
«Затвердження» дійсно вираз в Scala, і має тип Nothing
, який є підтипом будь-якого іншого типу. Це означає, що ви можете просто використовувати звичайний старий getOrElse
:
def myGet[A](oa: Option[A]) = oa.getOrElse(throw new RuntimeException("Can't."))
Ти справді, справді не повинен цим займатися.
OutOfMemoryError
).
Просто використовуйте метод .get.
def get[T](o:Option[T]) = o.get
Він викине NoSuchElementException, якщо o є екземпляром None.
В основному, я працював би з такими варіантами:
def addPrint(oi:Option[Int]) = oi.map(_+1).foreach(println)
addPrint(Some(41))
addPrint(Some(1336))
addPrint(None)
щоб уникнути вашого конкретного запитання.
get
це погана ідея - це просто документи про те, що ти робиш щось неприємне. Використання getOrElse
та явне створення винятку важче пропустити, коли ви вирішите, що це не просто одноразовий код і хочете зробити його безпечнішим.
Сподіваюся, це допоможе вам зрозуміти, як зображати помилки (і загалом ефекти) за допомогою типів.
Використовуйте Option
для повернення необов’язкових значень. Наприклад - не вдається знайти сутність у сховищі.
Використовуйте, Option(possiblyNull)
щоб уникнути випадків Some(null)
.
Використовуйте Either[Error, T]
для повідомлення про очікувану помилку. Наприклад - формат електронної пошти неправильний, не вдається проаналізувати рядок на число тощо.
Змоделюйте свої помилки як ADT (просто кажучи різновид ієрархій типів), щоб використовувати їх, наприклад, зліва від обох, щоб представляти більш складні сценарії помилок.
Кидок Exception
лише для сигналізації про несподівані та невиправні помилки. Як відсутній конфігураційний файл.
Використовуйте Either.catchOnly
або ( Try
або Cats.IO
додатково), а не блок блокування для обробки несподіваних несправностей. Підказка: Ви все ще можете використовувати ADT, але поширювати їх з підкинутих. Детальніше про Either
протиTry
.
Використовуйте Validated
тип даних з бібліотеки Cats для накопичення помилок, а не швидкого відмови ( Either
), але віддайте перевагу будь-якому з них на рівні модуля, щоб спростити склад програми (мати однакові типи). Наприклад - перевірка даних форми, накопичення помилок аналізу.
Використовуйте згадані типи і не оптимізуйте програму превентивно - оскільки, швидше за все, вузькі горловини будуть в бізнес-логіці, а не в типах ефектів.
Такий підхід спростить обслуговування та оновлення вашого коду, оскільки ви можете міркувати про це, не переходячи до специфікації реалізації (вона ж локальна-міркування). Також - зменшіть помилки - ви не можете пропустити помилку в типі. І скласти програму легше (за допомогою map
, flatMap
і іншими комбінаторами) - так як це простіше , на рівні типу, а не з нелокальними винятками і побічними ефектами.
Детальніше про вивчення функціональної Scala.
Але майте на увазі, що іноді при такому підході типи можуть складатись, і складніше складати речі. Дано, наприклад: x: Future[Either[Error, Option[T]]]
Що ви можете зробити:
map
та flatMap
в поєднанні зі збігом зразків для складання різних значень таких типів, наприклад:x.faltMap { case Right(Some(v)) => anotherFuture(v); case Left(er) => ... }
Future[Option[T]]
І нарешті, у вашому випадку одним із варіантів буде:
def foo() : Either[Error, String] = {
val x : Option[String] = ...
x match {
case Some(v) => Right(v)
case None => Left(Error(reason))
}
}
Scala тепер підтримує цю операцію на картах getOrElse()
методом, див. Документацію тут
Як уже зазначалося, кидання винятку в Scala - це також вираз.
Отже, ви можете зробити наступне:
myMap.getOrElse(myKey, throw new MyCustomException("Custom Message HERE")
.get
?