Идиоматический способ обработки вложенных обнуляемых объектов в scala?

#scala

#scala

Вопрос:

Я работаю с scala и недавно унаследовал некоторый Java-код, который необходимо внедрить, и, к сожалению, переписать его в Scala не в карточках. Он имеет глубоко вложенную структуру объектов, и любой уровень может быть нулевым. Часто меня интересуют только значения глубоко внутри вложенности.

В идеале я бы сделал что-то вроде этого:

  Option(foo.blah.blarg.doh)
  

Но если какой-либо из foo.blah.blarg имеет значение null, это приведет к созданию NPE.

На данный момент я решил обернуть это попыткой:

 Try(Option(foo.blah.blarg.doh)).getOrElse(None)
  

Обратите внимание, что использование .toOption работает не совсем правильно, поскольку это может привести к Some(null), если последний бит цепочки равен null.

Мне не особенно нравится эта конструкция, есть еще какие-нибудь идеи?

Комментарии:

1. если вы можете использовать scalaz, то это облегчит вам работу с exception монадическим способом и получение сообщения об исключении. def yourMethod = /.fromTryCatch { Option(foo.blah.blarg.doh) } где yourMethod возвращает дизъюнкцию `/[Throwable,Option[T]]

Ответ №1:

Плоская карта:

 for {
  a <- Option(foo)
  b <- Option(a.blah)
  c <- Option(b.blarg)
  d <- Option(c.doh)
} yield d
  

Комментарии:

1. Я понимаю, что FTS как фраза сама по себе идиоматична 🙂 — однако это тот случай, когда я не так сильно копаюсь в этом. Таких конечных точек много, и для каждой из них потребуется несколько строк кода, чтобы добраться до них с помощью этого механизма. Если бы был способ обобщить этот шаблон, чтобы он был записан один раз и использовался где угодно (включая разные гнезда и т. Д.) Мне бы это понравилось намного больше, но это выходит за рамки моего scala-fu.

2. с помощью динамических макросов вы могли бы преобразовать что-то вроде DynAccess(foo).blah.blarg.doh в a для понимания / некоторых нулевых проверок. Но у меня нет времени писать это в данный момент.

Ответ №2:

Воспользуйтесь параметрами по имени для создания собственной конструкции:

 def handleNull[T](x: => T): Option[T] = try Option(x) catch {
  case _: NullPointerException => None
}

handleNull(foo.blah.blarg.doh)
  

Ответ №3:

Если cats в вашем проекте есть библиотека, вы можете использовать ее методы расширения:

   import cats.implicits._

  Either
    .catchOnly[NullPointerException](foo.blah.blarg.doh)
    .collectFirstSome(Option(_))
  

Ответ №4:

Вы также можете использовать этот метод для безопасного перемещения по цепочке вызовов, где каждый шаг может возвращать значение null. Его удобнее использовать, чем Options и flatMaps.

   /**
    * @param expr expression which is a chain of java calls
    * @tparam A the type of the last expression
    * @return Some(A) if there was no null, None otherwise
    */
  def safeTraverseNullableChain[A](expr: => A): Option[A] = {
    Try(expr).filter(_ != null).toOption
  }