Измените Seq [Future [Boolean]] в будущее [Seq [Boolean]] [duplicate]

Что такое NullPointerException?

Хорошим местом для начала является JavaDocs . Они охватывают это:

Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  • Вызов метода экземпляра нулевого объекта.
  • Доступ или изменение поля нулевого объекта.
  • Выполнение длины null, как если бы это был массив.
  • Доступ или изменение слотов с нулевым значением, как если бы это был массив.
  • Бросать нуль, как если бы это было значение Throwable.

Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.

Также, если вы попытаетесь использовать нулевую ссылку с synchronized, который также выдаст это исключение, за JLS :

SynchronizedStatement:
    synchronized ( Expression ) Block
  • В противном случае, если значение выражения равно null, NullPointerException.

Как это исправить?

Итак, у вас есть NullPointerException. Как вы это исправите? Возьмем простой пример, который выдает NullPointerException:

public class Printer {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.print();
    }
}

Идентифицирует нулевые значения

. Первый шаг - точно определить , значения которого вызывают исключение . Для этого нам нужно выполнить некоторую отладку. Важно научиться читать stacktrace . Это покажет вам, где было выбрано исключение:

Exception in thread "main" java.lang.NullPointerException
    at Printer.printString(Printer.java:13)
    at Printer.print(Printer.java:9)
    at Printer.main(Printer.java:19)

Здесь мы видим, что исключение выбрано в строке 13 (в методе printString). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, что s имеет значение null, а вызов метода length на него вызывает исключение. Мы видим, что программа прекращает бросать исключение, когда s.length() удаляется из метода.

Трассировка, где эти значения взяты из

Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что s передается с printString(name) в методе print(), а this.name - null.

Трассировка, где эти значения должны быть установлены

Где установлен this.name? В методе setName(String). С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. ​​

Этого достаточно, чтобы дать нам решение: добавить вызов printer.setName() перед вызовом printer.print().

Другие исправления

Переменная может иметь значение по умолчанию setName может помешать ему установить значение null):

private String name = "";

Либо метод print, либо printString может проверить значение null например:

printString((name == null) ? "" : name);

Или вы можете создать класс, чтобы name всегда имел ненулевое значение :

public class Printer {
    private final String name;

    public Printer(String name) {
        this.name = Objects.requireNonNull(name);
    }

    public void print() {
        printString(name);
    }

    private void printString(String s) {
        System.out.println(s + " (" + s.length() + ")");
    }

    public static void main(String[] args) {
        Printer printer = new Printer("123");
        printer.print();
    }
}

См. также:

Я все еще не могу найти проблему

Если вы попытались отладить проблему и до сих пор не имеете решения, вы можете отправить вопрос для получения дополнительной справки, но не забудьте включить то, что вы пробовали до сих пор. Как минимум, включите stacktrace в вопрос и отметьте важные номера строк в коде. Также попробуйте сначала упростить код (см. SSCCE ).

97
задан Joe 2 January 2014 в 01:01
поделиться

5 ответов

Фокус в том, чтобы сначала убедиться, что ни один из фьючерсов не сработал. .recover - ваш друг здесь, вы можете объединить его с map, чтобы преобразовать все результаты Future[T] в экземпляры Future[Try[T]]], все из которых наверняка будут успешными фьючерсами.

Примечание: здесь вы также можете использовать Option или Either, но Try является самым чистым способом, если вы специально хотите улавливать исключения

def futureToFutureTry[T](f: Future[T]): Future[Try[T]] =
  f.map(Success(_)).recover(x => Failure(x))

val listOfFutures = ...
val listOfFutureTrys = listOfFutures.map(futureToFutureTry(_))

Затем используйте Future.sequence как и раньше, чтобы дать вам Future[List[Try[T]]]

val futureListOfTrys = Future.sequence(listOfFutureTrys)

Затем фильтр:

val futureListOfSuccesses = futureListOfTrys.map(_.filter(_.isSuccess))

Вы можете даже вытащить определенные сбои, если они вам понадобятся:

val futureListOfFailures = futureListOfTrys.map(_.filter(_.isFailure))
111
ответ дан Kevin Wright 19 August 2018 в 09:07
поделиться
  • 1
    Благодаря! .recover действительно был недостающим для меня. – Joe 2 January 2014 в 06:33
  • 2
    Вы можете использовать _.collect{ case Success(x) => x} вместо _.filter(_.isSuccess), чтобы избавиться от Try в типе futureListOfSuccesses. – senia 2 January 2014 в 08:35
  • 3
    В scala 2010 .recover(x => Failure(x)) недействительно, вместо этого используйте .recover({case e => Failure(e)}) – FGRibreau 11 July 2014 в 17:32
  • 4
    Я думаю, что вам не хватает будущей оболочки: def futureToFutureOfTry [A] (f: Future [A]): ​​Future [Try [A]] = {val p = Promise [Try [A]] () f.map {a = & GT; p.success (scala.util.Success (a))} .recover {case x: Throwable = & gt; p.success (Failure (x))} p.future} – Dario Black 8 July 2015 в 20:55
  • 5
    не так. Я рисую Будущее в другое будущее, промежуточное обещание не нужно и будет расточительным – Kevin Wright 8 July 2015 в 23:53

Вы можете легко переносить будущий результат с помощью опции, а затем сгладить список:

def futureToFutureOption[T](f: Future[T]): Future[Option[T]] =
    f.map(Some(_)).recover {
      case e => None
    }
val listOfFutureOptions = listOfFutures.map(futureToFutureOption(_))

val futureListOfOptions = Future.sequence(listOfFutureOptions)

val futureListOfSuccesses = futureListOfOptions.flatten
0
ответ дан Amir Hossein Javan 19 August 2018 в 09:07
поделиться

Я попробовал ответить Кевину, и я столкнулся с ошибкой в ​​своей версии Scala (2.11.5) ... Я исправил это и написал несколько дополнительных тестов, если кому-то интересно ... вот моя версия>

implicit class FutureCompanionOps(val f: Future.type) extends AnyVal {

    /** Given a list of futures `fs`, returns the future holding the list of Try's of the futures from `fs`.
      * The returned future is completed only once all of the futures in `fs` have been completed.
      */
    def allAsTrys[T](fItems: /* future items */ List[Future[T]]): Future[List[Try[T]]] = {
      val listOfFutureTrys: List[Future[Try[T]]] = fItems.map(futureToFutureTry)
      Future.sequence(listOfFutureTrys)
    }

    def futureToFutureTry[T](f: Future[T]): Future[Try[T]] = {
      f.map(Success(_)) .recover({case x => Failure(x)})
    }

    def allFailedAsTrys[T](fItems: /* future items */ List[Future[T]]): Future[List[Try[T]]] = {
      allAsTrys(fItems).map(_.filter(_.isFailure))
    }

    def allSucceededAsTrys[T](fItems: /* future items */ List[Future[T]]): Future[List[Try[T]]] = {
      allAsTrys(fItems).map(_.filter(_.isSuccess))
    }
}


// Tests... 



  // allAsTrys tests
  //
  test("futureToFutureTry returns Success if no exception") {
    val future =  Future.futureToFutureTry(Future{"mouse"})
    Thread.sleep(0, 100)
    val futureValue = future.value
    assert(futureValue == Some(Success(Success("mouse"))))
  }
  test("futureToFutureTry returns Failure if exception thrown") {
    val future =  Future.futureToFutureTry(Future{throw new IllegalStateException("bad news")})
    Thread.sleep(5)            // need to sleep a LOT longer to get Exception from failure case... interesting.....
    val futureValue = future.value

    assertResult(true) {
      futureValue match {
        case Some(Success(Failure(error: IllegalStateException)))  => true
      }
    }
  }
  test("Future.allAsTrys returns Nil given Nil list as input") {
    val future =  Future.allAsTrys(Nil)
    assert ( Await.result(future, 100 nanosecond).isEmpty )
  }
  test("Future.allAsTrys returns successful item even if preceded by failing item") {
    val future1 =  Future{throw new IllegalStateException("bad news")}
    var future2 = Future{"dog"}

    val futureListOfTrys =  Future.allAsTrys(List(future1,future2))
    val listOfTrys =  Await.result(futureListOfTrys, 10 milli)
    System.out.println("successItem:" + listOfTrys);

    assert(listOfTrys(0).failed.get.getMessage.contains("bad news"))
    assert(listOfTrys(1) == Success("dog"))
  }
  test("Future.allAsTrys returns successful item even if followed by failing item") {
    var future1 = Future{"dog"}
    val future2 =  Future{throw new IllegalStateException("bad news")}

    val futureListOfTrys =  Future.allAsTrys(List(future1,future2))
    val listOfTrys =  Await.result(futureListOfTrys,  10 milli)
    System.out.println("successItem:" + listOfTrys);

    assert(listOfTrys(1).failed.get.getMessage.contains("bad news"))
    assert(listOfTrys(0) == Success("dog"))
  }
  test("Future.allFailedAsTrys returns the failed item and only that item") {
    var future1 = Future{"dog"}
    val future2 =  Future{throw new IllegalStateException("bad news")}

    val futureListOfTrys =  Future.allFailedAsTrys(List(future1,future2))
    val listOfTrys =  Await.result(futureListOfTrys,  10 milli)
    assert(listOfTrys(0).failed.get.getMessage.contains("bad news"))
    assert(listOfTrys.size == 1)
  }
  test("Future.allSucceededAsTrys returns the succeeded item and only that item") {
    var future1 = Future{"dog"}
    val future2 =  Future{throw new IllegalStateException("bad news")}

    val futureListOfTrys =  Future.allSucceededAsTrys(List(future1,future2))
    val listOfTrys =  Await.result(futureListOfTrys,  10 milli)
    assert(listOfTrys(0) == Success("dog"))
    assert(listOfTrys.size == 1)
  }
9
ответ дан Chris Bedford 19 August 2018 в 09:07
поделиться

Я только что наткнулся на этот вопрос и предложил другое решение:

def allSuccessful[A, M[X] <: TraversableOnce[X]](in: M[Future[A]])
                                                (implicit cbf: CanBuildFrom[M[Future[A]], A, M[A]], 
                                                 executor: ExecutionContext): Future[M[A]] = {
    in.foldLeft(Future.successful(cbf(in))) {
      (fr, fa) ⇒ (for (r ← fr; a ← fa) yield r += a) fallbackTo fr
    } map (_.result())
}

Идея здесь заключается в том, что в пределах складки вы ожидаете завершения следующего элемента в списке (используя синтаксис понимания), и если следующий не удастся, вы просто откажитесь от того, что у вас уже есть.

6
ответ дан Idan Waisman 19 August 2018 в 09:07
поделиться
  • 1
    Мне не нравится имя, но мне нравится, как это делается, прямо из последовательности impl – crak 31 July 2017 в 16:04

Scala 2.12 имеет улучшение на Future.transform, которое поддается андерсеру с меньшим количеством кодов.

val futures = Seq(Future{1},Future{throw new Exception})

val seq = Future.sequence(futures.map(_.transform(Success(_)))) // instead of map and recover

@val successes = seq.map(_.collect{case Success(x)=>x})
successes: Future[Seq[Int]] = Future(Success(List(1)))

@val failures = seq.map(_.collect{case Failure(x)=>x})
failures: Future[Seq[Throwable]] = Future(Success(List(java.lang.Exception)))
0
ответ дан WeiChing Lin 19 August 2018 в 09:07
поделиться
  • 1
    Не понимаю, почему нисходящий. Это допустимое решение. – PH88 26 May 2018 в 07:29
Другие вопросы по тегам:

Похожие вопросы: