Как да внедрим този код с помощта на ламбда изрази в Java?

Имам документ със софтуерна спецификация за конкретна библиотека в java, която помага за събирането на данни от биосензор и има клас, наречен „Кръпка“. В документа е посочено:

Следва конструкторът на класа 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