Передача ссылки на метод конструктору объектов

Я чувствую, что что-то упускаю, когда речь идет о статически типизированных языках. Когда-то давно я использовал только Perl, и у меня было много способов указать объекту, какую функцию вызывать.

Теперь, когда я на Java, я не понимаю, как я могу сделать что-то подобное простым способом.

У меня есть общий класс Button. Это подкласс всех фактических кнопок, которые будут использоваться: каждая с другим методом для вызова при нажатии.

Действительно ли нет способа передать ссылку на метод для вызова при нажатии, чтобы я мог использовать один класс для всех кнопок?

В настоящее время я создаю такие кнопки:

// Specifically using the subclass that sets "firemode" to "close"
FiremodeClose fc = new FiremodeClose(Settings.ui_panel_start, Settings.ui_panel_row_firemode, game);
painter.addSelectionButton(fc);
clickTracker.addSelectionButton(fc);

Это, конечно, вызывает множество подклассов, каждый из которых отличается только размещением, меткой/графикой и вызовом метода. Разумнее сделать что-то подобное:

// Generic button, the method that sets "firemode" is somehow passed as arguement to the contsructor.
Button fc = new Button(&referenceToFunctionToCallWhenClicked, otherArguementsEtc);
painter.addSelectionButton(fc);
clickTracker.addSelectionButton(fc);

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


person Jarmund    schedule 27.08.2016    source источник
comment
Вот для чего нужен интерфейс. Java 8 и Groovy упрощают реализацию встроенного интерфейса для таких случаев использования.   -  person chrylis -cautiouslyoptimistic-    schedule 27.08.2016
comment
^^^ Это и см. также анонимные классы.   -  person Jason C    schedule 27.08.2016
comment
@chrylis Если для этого нужны интерфейсы, то я, должно быть, использовал их для чего-то другого, кроме их предполагаемой цели. Я хотел бы увидеть ответ с некоторыми примерами кода для этого.   -  person Jarmund    schedule 27.08.2016
comment
Это не единственное использование интерфейсов, но основное. Интерфейс — это строго типизированная версия утиной печати, объявляющая, какая конкретная функциональность вам нужна, не заботясь о реализации.   -  person chrylis -cautiouslyoptimistic-    schedule 27.08.2016


Ответы (3)


Если это то, для чего нужны интерфейсы, то я, должно быть, использовал их для чего-то другого, кроме их предполагаемой цели. Я хотел бы увидеть ответ с некоторыми примерами кода для этого.

Пусть ваш Buttons реализует шаблон наблюдателя, точно так же, как Swing делает. Затем вы даже можете просто использовать Swing ActionListener интерфейс или даже Runnable не является плохой выбор, или, например. свернуть самостоятельно:

// Your interface.
public interface MyButtonListener {
    public void buttonClicked ();
}

// Somewhere else:
Button fc = ...;
fc.addButtonListener(new MyButtonListener () {
    @Override public void buttonClicked () {
        // do stuff here
    }
});

// And in your Button have it simply iterate through all of its registered
// MyButtonListeners and call their buttonClicked() methods.

Есть множество других способов реализовать это. Например, вы даже можете сделать что-то вроде:

public interface ThingThatCaresAboutButtons {
    public void buttonClicked (Button button);
}

Тогда ваша логика пользовательского интерфейса более высокого уровня будет выглядеть примерно так:

public class MyUI implements ThingThatCaresAboutButtons {
    @Override public void buttonClicked (Button button) {
        if (button == theOneButton) {
            // do whatever
        } else if (button == theOtherButton) {
            // do whatever
        }
    }
}

И при создании кнопок:

theOneButton = new Button(theUI, ...);
theOtherButton = new Button(theUI, ...);

Или заставьте их поддерживать список вместо одного объекта, переданного в конструкторе. Или что угодно.

Бесконечные способы снять шкуру с этой кошки, но, надеюсь, вы черпаете здесь вдохновение. Посмотрите, как работает Swing.

person Jason C    schedule 27.08.2016

Например, вы можете использовать Runnable:

class MyButton {
    private final Runnable action;

    public MyButton(Runnable action) {
        this.action = action;
    }
    ...
}

А затем вызовите action.run() при нажатии кнопки.

Затем при создании кнопки вы можете передать ссылку на метод, если он имеет возвращаемый тип void и не принимает аргументов.

Button fc = new Button(EnclosingClass::methodToCall, otherArguementsEtc);

Другие интерфейсы могут использоваться для других сигнатур методов.

person Jorn Vernee    schedule 27.08.2016

В Java 8 вы можете использовать как ссылки на методы, так и лямбды:

class Button {
    Button(Runnable function) {
    }
}
Button b1 = new Button(() -> System.out.println("works!"));
Button b2 = new Button(System::gc);

Вы можете сделать то же самое в Java ‹ 8, но с анонимными классами это будет более подробно:

Button b3 = new Button(new Runnable() {
    @Override
    public void run() {
        System.out.println("works!");
    }
});
person Michal Kordas    schedule 27.08.2016