Возвращаемое значение метода интерфейса (без параметров) получается асинхронно. Можно ли это реализовать? - PullRequest
0 голосов
/ 16 января 2020

Мне дали интерфейс, подобный следующему:

MyInterface {
    List<FirebaseVisionFace> getFaceList();
}

Я должен реализовать его в классе (назовем его MyFirebaseFaceClass) таким образом, чтобы мы могли затем сделать

List<FirebaseVisionFace> faceList = myFirebaseFaceClass.getFaceList()

Проблема в том, что для получения этого списка граней требуется следующее:

Task<List<FirebaseVisionFace>> result =
    detector.detectInImage(image)
            .addOnSuccessListener(
                    new OnSuccessListener<List<FirebaseVisionFace>>() {
                        @Override
                        public void onSuccess(List<FirebaseVisionFace> faces) {
                            // Task completed successfully
                            // ...
                        }
                    })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            // Task failed with an exception
                            // ...
                        }
                    });

Возможно ли реализовать этот метод getFaceList() без передачи обратного вызова в качестве параметр и использование обратного вызова, чтобы получить список?

Ответы [ 3 ]

1 голос
/ 17 января 2020

Вы можете попробовать использовать CompletableFuture. В реализации вашего интерфейса:

class MyImplementation implements MyInterface {
  @Override
  List<FirebaseVisionFace> getFaceList() {
    final CompletableFuture<List<FirebaseVisionFace>> future = new CompletableFuture<>();
    final DetectorOfSomeKindYouDidNotSpecifyAbove detector = // ... get detector
    detector.detectInImage(image)
            .addOnSuccessListener(
                    new OnSuccessListener<List<FirebaseVisionFace>>() {
                        @Override
                        public void onSuccess(List<FirebaseVisionFace> faces) {
                            // Task completed successfully
                            future.complete(faces);
                        }
                    })
            .addOnFailureListener(
                    new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            // Task failed with an exception
                            future.completeExceptionally(e);
                        }
                    });
    try {
      return future.get(); // blocks until completion by one of the listeners above
    } catch (final RuntimeException throwMe) {
      throw throwMe;
    } catch (final InterruptedException interruptedException) {
      Thread.currentThread().interrupt();
      throw new IllegalStateException(interruptedException.getMessage(), interruptedException);
    } catch (final Exception everythingElse) {
      throw new IllegalStateException(everythingElse.getMessage(), everythingElse);
    }
  }
}

Мой код обработки ошибок выше глуп для краткости. Весь код выше не проверен и списан с манжеты. Я предполагаю, что слушатели вызываются, когда все обнаружение сделано, и вызываются один раз. Я также предполагаю, что вызван только один из слушателей, а не оба. Я также предполагаю, что они вызываются другим потоком.

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

Надеюсь, это, по крайней мере, укажет вам правильное направление!

1 голос
/ 18 января 2020

Вы не можете избежать обратных вызовов, но вы можете использовать ссылки на методы в качестве обратных вызовов, поэтому весь код выглядит чище:

class MyFirebaseFaceClass implements MyInterface {
    List<FirebaseVisionFace> faces;
    Exception exception;
    boolean done = false;

    // Task completed successfully
    private synchronized void onSuccess(List<FirebaseVisionFace> faces) {
        this.faces = faces;
        done = true;
        notifyAll();
    }

    // Task failed with an exception
    private synchronized void onError(Exception exception) {
        this.exception = exception;
        done = true;
        notifyAll();
    }

    List<FirebaseVisionFace> getFaceList() {
         detector.detectInImage(image)
            .addOnSuccessListener(this::onSuccess)
            .addOnFailureListener(this::onError);
         synchronized (this) {
            while (!done) {
                wait();
            }
            if (exception == null) {
                return faces;
            } else {
                throw new ExcecutionException(exception);
            }
        }
    }
}
1 голос
/ 17 января 2020

Если вы работаете с асинхронными задачами в Java, вы не можете избежать обратных вызовов. Вы используете класс Task, который часто использует обратные вызовы. Если вам не нравится этот синтаксис, вы можете использовать лямбда-выражения:

Task<List<FirebaseVisionFace>> result = 
    detector.detectInImage(image)
        .addOnSuccessListener((List<FirebaseVisionFace> faces) -> {
             // Task completed successfully
        })
        .addOnFailureListener((@NotNull Exception e) -> {
             // Task failed with an exception
        })
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...