Практическое использование multiple bounds generic в Java

  • Tutorial

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


Наткнулся на проблему: надо послать уже существующее событие (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);
}

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

  • +13
  • 6.4k
  • 6
Share post

Comments 6

    +1
    Почему бы лямбды не использовать вместо new SelectHandler?
      0
      Потому что GWT не умеет в Java 8 в целом и в лямбды в частности.
        0
        Немного устаревшие данные. Последний релиз поддерживает и Java 9, правда только как компилятор. Языковые конструкции обещают в следующем релизе. Т.е. сейчас можно писать на Java 8 и это скомпилируется Java SDK 9. Но статься не об этом :)
          0
          Это радует. Потому что когда мне приходилось разрабатывать под гвит, это был ад — поддержка языка на уровне 6ой версии, бойлерплейт интерфейсов *Proxy, приколы с RequestFactory и (им-)мутабельностью бинов (вот это вот «Autobean is frozen»).
            0

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


            • лямды, стримы (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
        Причина тривиальна — это мой пост из песочницы 2-х давности. Тогда GWT не умела лямды.

      Only users with full accounts can post comments. Log in, please.