Как реализовать этот код с помощью лямбда-выражений в Java?

У меня есть документ со спецификацией программного обеспечения для конкретной библиотеки в java, которая помогает собирать данные с биосенсора и имеет класс под названием «Patch». В документе указывается:

Ниже приведен конструктор класса Patch:

Patch(JSONObject options, (JSONObject)->onDiscovery,(JSONObject)->ondata,(JSONObject)->onStatus)

Где

JOSNObject options: options settings for the object
(JSONObject)->onDiscovery: Callback for receiving the sensor info as a JSONObject,
(JSONObject)->ondata: Callback for receiving the sensor data as a JSONObject,
(JSONObject)->onStatus: Callback for receiving the global errors as a JSONObject

Это должно называться как

Patch patch=newPatch(jsonConfigObject, discoveryObject, streamObject, status);

Обеспокоенный парень заявил, что эти обратные вызовы должны быть просто лямбда-выражениями.

Теперь я понимаю: конструктор принимает 4 аргумента:

  1. Пустой объект JSON,
  2. Справочник по функции обратного вызова
  3. Справочник по функции обратного вызова
  4. Справочник по функции обратного вызова

Я новичок в реализации обратного вызова, поэтому не могу понять:

  1. как мне реализовать это в моем коде?
  2. Должен ли я создавать функциональные интерфейсы для каждого из этих методов обратного вызова?

Допустим, я только что импортировал библиотеку, что мне делать дальше?


person Community    schedule 11.06.2020    source источник


Ответы (1)


Целью лямбда-выражений является обозначение методов, которые Patch-объект может вызывать (обратно) при определенных событиях. В отличие от других языков программирования, в Java нельзя было передавать функции в качестве параметров конструкторам или методам. Таким образом, необходимо было создать объект класса, который реализует определенный интерфейс (или, как известно, имеет определенный метод по типу) и передать его методу или конструктору в качестве параметра. Затем этот метод может быть вызван для переданного объекта при возникновении события.

Функциональный интерфейс уже есть. Вам не нужно создавать его самостоятельно. Он уже использовался в подписи конструктора Patch (типы последних трех параметров конструктора). Функциональные интерфейсы — это типы, которые имеют только один абстрактный метод, который необходимо переопределить.

Предположим, функциональный интерфейс выглядит так:

interface Callback { // could of course have a different name
    public void call(JSONObject o);
}

Добавлять аннотацию @FunctionalInterface не нужно. Это только предотвратит последующие модификации интерфейса, а именно добавление дополнительных абстрактных методов.

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

Хотя наиболее удобно, нет необходимости вызывать конструктор с лямбда-выражениями для последних трех параметров. Другие возможности включают ссылку на метод (используя ::), анонимные классы и создание экземпляра класса с новым определением.

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

class Main {
    static void onDiscovery(JSONObject o) {...}
    static void onData(JSONObject o) {...}
    static void onStatus(JSONObject o) {...}
}

Необязательно, чтобы эти три метода имели точно такие же имена или чтобы они были static.

В методе main этого класса (или где-то еще) могло произойти:

    public static void main(String[] args) {
        JSONObject o = // ... 
        Patch p = new Patch(o, 
                           (x) -> Main.onDiscovery(x), // (1) lambda expr. (Main. can be omitted)
                           Main::onData,               // (2) method reference
                           new Callback() {            // (3) anonymous inner class
                               public void call(JSONObject x) {
                                   Main.onStatus(x);
                               }
                           });
        //...
    }

(1)-(3) показать альтернативные способы передачи объекта типа Callback.

(1) имеет то преимущество, что более короткие реализации могут идти прямо здесь вместо вызова onDiscovery(x). Если у вас более одного оператора, вам нужны фигурные скобки вокруг операторов и ; после каждого утверждения:

(x) -> {
    System.out.println("discovery event");
    System.out.println(x.getXYZ());
    // ...
}

(2) имеет то преимущество, что он очень лаконичен. Если у вас нет статического метода для ссылки, ссылка на объект также может использоваться для обозначения объекта, для которого должен быть вызван метод.

(3) анонимные внутренние классы обычно больше не используются для функциональных интерфейсов. Если интерфейс/абстрактный класс имеет больше абстрактных методов, но один, все равно необходимо использовать анонимные внутренние классы.

class Main {
    void onData(JSONObject o) { ... }
    // ...
    public static void main(String[] args) {
        var m = new Main();
        var o = new JSONObject(); // or whatever
        var p = new Patch(o,
                          (x) -> m.onDiscovery(x),
                          m::onData;
                          new Callback() {
                              public void call(JSONObject x) {
                                  m.onStatus(x);
                              }
                          });
    }
}
person tastaturtier    schedule 08.07.2020