Не сильно ошибусь, если предположу, что мало кто активно использует эту возможность языка. Для тех кто не помнит, что это такое можно почитать здесь. Я же переду к практике.


Наткнулся на проблему: надо послать уже существующее событие (GWT) по нажатию кнопки, но перед посылкой поставить атрибут (Command). Казалась бы, при чём здесь шаблоны…
А вот при чём:


Сам метод для создания кнопки тривиален, command это поле класса:


Component createEventLink(String link, final Event<?> event) {
    TextButton button = new TextButton(link, new SelectHandler() {
        @Override
        public void onSelect(SelectEvent e) {
            //event.setCommand(command);
            bus.fire(event);
        }
    });
    return new WidgetComponent(button);
}

Проблема в строчке:


//event.setCommand(command);

Такого метода у объекта Event нет. Решение, вроде, очевидное: унаследовать наше события от промежуточного класса CommandEvent, у которого будет этот метод и который унаследован от Event. Наш метод выглядит теперь так:


Component createEventLink(String link, final CommandEvent<?> event) {
    TextButton button = new TextButton(link, new SelectHandler() {
        @Override
        public void onSelect(SelectEvent e) {
            event.setCommand(command);
            bus.fire(event);
        }
    });
    return new WidgetComponent(button);
} 

Эврика? Ха! Тут мы обнаруживаем, что одно из наших событий уже унаследовано от другого дочернего класса (e.g. GwtEvent) и никак не может наследовать наш класс CommandEvent.
Следующий шаг — создаём интерфейс ICommandEvent c методом setCommand() и каждое наше событие реализует его. Наш метод выглядит теперь так:


Component createEventLink(String link, final Event event) {
    TextButton button = new TextButton(link, new SelectHandler() {
        @Override
        public void onSelect(SelectEvent e) {
            if (event instanceof ICommandEvent) {
                ((ICommandEvent) event).setCommand(command);
            } else {
                throw new IllegalStateException("Only ICommandEvent allowed");
            }
            bus.fire(event);
        }
    });
    return new WidgetComponent(button);
}

Ну некрасиво! К тому же в него можно передать любое событие, а обнаружится это только при запуске, что нехорошо.


И тут пора вспомнить о теме этой заметки — multiple bounds generic в Java. С ними наш метод выглядит так:


<E extends Event<?> & ICommandEvent> Component createEventLink(String link, final E event) {
    TextButton button = new TextButton(link, new SelectHandler() {
        @Override
        public void onSelect(SelectEvent e) {
            event.setCommand(command);
            bus.fire(event);
        }
    });
    return new WidgetComponent(button);
}

Ровно то, что и требовалось.

Комментарии (6)


  1. slavap
    06.12.2017 20:53

    Почему бы лямбды не использовать вместо new SelectHandler?


    1. Lure_of_Chaos
      06.12.2017 21:23

      Потому что GWT не умеет в Java 8 в целом и в лямбды в частности.


      1. foal Автор
        06.12.2017 21:34

        Немного устаревшие данные. Последний релиз поддерживает и Java 9, правда только как компилятор. Языковые конструкции обещают в следующем релизе. Т.е. сейчас можно писать на Java 8 и это скомпилируется Java SDK 9. Но статься не об этом :)


        1. Lure_of_Chaos
          06.12.2017 21:42

          Это радует. Потому что когда мне приходилось разрабатывать под гвит, это был ад — поддержка языка на уровне 6ой версии, бойлерплейт интерфейсов *Proxy, приколы с RequestFactory и (им-)мутабельностью бинов (вот это вот «Autobean is frozen»).


          1. foal Автор
            06.12.2017 21:58

            Сейчас всё намного приятнее:


            • лямды, стримы (Java8)
            • injection (GIN, Dagger 2)
            • REST API (RestyGWT)
            • доступ к любой JS библиотеке путём написания простйшего интефейса (JS Interop). При прямых руках и еще небольшом усиилии из Java даже проще.

            Например JavaScript


            var builder = new StarWebPrintBuilder();
            var request = '';
            request += builder.createInitializationElement();
            request += builder.createTextElement({characterspace:2});
            request += builder.createAlignmentElement({position:'right'});
            request += builder.createLogoElement({number:1});
            request += builder.createTextElement({data:'TEL 9999-99-9999\n'});
            request += builder.createAlignmentElement({position:'left'});
            request += builder.createTextElement({data:'\n'});
            request += builder.createTextElement({data:'Apple                $20.00\n'});
            request += builder.createTextElement({emphasis:true});
            request += builder.createTextElement({width:2, data:'Total'});
            request += builder.createTextElement({width:1, data:'   '});
            request += builder.createTextElement({width:2, data:'$210.00\n'});
            request += builder.createTextElement({width:1});
            request += builder.createTextElement({emphasis:false});
            request += builder.createTextElement({data:'\n'});
            request += builder.createCutPaperElement({feed:true});

            Тот же код из Java (JS Interop)


            StarWebPrintBuilder builder = new StarWebPrintBuilder();
            String request = new StringBuilder()
            .append(builder.createInitializationElement(new Iep()))
            .append(builder.createTextElement(new Tep() {{ characterspace = 2; }}))
            .append(builder.createAlignmentElement(new Aep() {{ position = "right"; }}))
            .append(builder.createLogoElement(new Lep() {{ number = 1;}}))
            .append(builder.createTextElement(new Tep() {{ data = "TEL 9999-99-9999\n"; }}))
            .append(builder.createAlignmentElement(new Aep() {{ position = "left"; }}))
            .append(builder.createTextElement(new Tep() {{ data = "\n"; }}))
            .append(builder.createTextElement(new Tep() {{ data = "Apple          $20.00\n"; }}))
            .append(builder.createTextElement(new Tep() {{ emphasis = true; }}))
            .append(builder.createTextElement(new Tep() {{ width = 2; data = "Total"; }}))
            .append(builder.createTextElement(new Tep() {{ width = 1; data = "   "; }}))
            .append(builder.createTextElement(new Tep() {{ width = 2; data = "$210.00\n"; }}))
            .append(builder.createTextElement(new Tep() {{ width = 1; }}))
            .append(builder.createTextElement(new Tep() {{ emphasis = false; }}))
            .append(builder.createTextElement(new Tep() {{ data = "\n"; }}))
            .append(builder.createCutPaperElement(new Cpep() {{ feed = true; }}))
             .toString();

            Тот же код из Java (JS Interop + Custom builder on Java)


            String request = new GwtPrintBuilder()
                 .init()
                 .text(characterSpace(2))
                 .align(right)
                 .logo(number(1))
                 .text("TEL 9999-99-9999")
                 .align(left)
                 .text(line())
                 .text("Apple", "$20.00", lineLengt)
                 .text(emphasis(true))
                 .text("Total", "210.00", lineLengt, 2)
                 .text(emphasis(false))
                 .text(line())
                 .cut(feed(true))
                 .build();


    1. foal Автор
      06.12.2017 21:29

      Причина тривиальна — это мой пост из песочницы 2-х давности. Тогда GWT не умела лямды.