Java Передает метод в качестве параметра
Я ищу способ передать метод по ссылке. Я понимаю, что Java не передает методы в качестве параметров, однако я хотел бы получить альтернативу.
Мне говорили, что интерфейсы являются альтернативой передаче методов в качестве параметров, но я не понимаю, как интерфейс может действовать как метод по ссылке. Если я правильно понимаю, интерфейс - это просто абстрактный набор методов, которые не определены. Я не хочу отправлять интерфейс, который нужно определять каждый раз, потому что несколько разных методов могут вызывать один и тот же метод с одинаковыми параметрами.
Чего я хотел бы достичь, так это чего-то подобного этому:
public void setAllComponents(Component[] myComponentArray, Method myMethod) {
for (Component leaf : myComponentArray) {
if (leaf instanceof Container) { //recursive call if Container
Container node = (Container) leaf;
setAllComponents(node.getComponents(), myMethod);
} //end if node
myMethod(leaf);
} //end looping through components
}
вызывается, например:
setAllComponents(this.getComponents(), changeColor());
setAllComponents(this.getComponents(), changeSize());
Переведено автоматически
Ответ 1
Редактировать: начиная с Java 8, лямбда-выражения являются хорошим решением, как указывали другие ответы. Приведенный ниже ответ был написан для Java 7 и более ранних версий...
Взгляните на шаблон команды.
// NOTE: code not tested, but I believe this is valid java...
public class CommandExample
{
public interface Command
{
public void execute(Object data);
}
public class PrintCommand implements Command
{
public void execute(Object data)
{
System.out.println(data.toString());
}
}
public static void callCommand(Command command, Object data)
{
command.execute(data);
}
public static void main(String... args)
{
callCommand(new PrintCommand(), "hello world");
}
}
Редактировать: как указывает Пит Киркхэм, есть другой способ сделать это с помощью посетителя. Подход посетителя немного сложнее - все ваши узлы должны быть ориентированы на посетителя с помощью acceptVisitor()
метода - но если вам нужно пройти более сложный объектный граф, тогда его стоит изучить.
Ответ 2
В Java 8 теперь вы можете передавать метод более легко, используя лямбда-выражения и ссылки на методы. Сначала немного предыстории: функциональный интерфейс - это интерфейс, который имеет один и только один абстрактный метод, хотя он может содержать любое количество методов по умолчанию (новых в Java 8) и статических методов. Лямбда-выражение может быстро реализовать абстрактный метод без всего ненужного синтаксиса, необходимого, если вы не используете лямбда-выражение.
Без лямбда-выражений:
obj.aMethod(new AFunctionalInterface() {
@Override
public boolean anotherMethod(int i)
{
return i == 982
}
});
С помощью лямбда-выражений:
obj.aMethod(i -> i == 982);
Вот выдержка из руководства по Java по лямбда-выражениям:
Синтаксис лямбда-выражений
Лямбда-выражение состоит из следующего:
Список формальных параметров, разделенных запятыми, заключен в круглые скобки. Метод CheckPerson.test содержит один параметр, p, который представляет экземпляр класса Person.
Примечание: Вы можете не указывать тип данных параметров в лямбда-выражении. Кроме того, вы можете не заключать в круглые скобки, если есть только один параметр. Например, также допустимо следующее лямбда-выражение:
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25Маркер стрелки,
->
Тело, состоящее из одного выражения или блока инструкций. В этом примере используется следующее выражение:
p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25Если вы укажете единственное выражение, среда выполнения Java вычислит выражение и затем вернет его значение. В качестве альтернативы,
вы можете использовать оператор return:
p -> {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}Оператор return не является выражением; в лямбда-выражении вы должны заключать операторы в фигурные скобки ({}). Однако вам не обязательно
заключать вызов метода void в фигурные скобки. Например,
следующее является допустимым лямбда-выражением:email -> System.out.println(email)
Обратите внимание, что лямбда-выражение очень похоже на объявление метода;
вы можете рассматривать лямбда-выражения как анонимные методы —methods
без имени.
Вот как вы можете "передать метод", используя лямбда-выражение:
interface I {
public void myMethod(Component component);
}
class A {
public void changeColor(Component component) {
// code here
}
public void changeSize(Component component) {
// code here
}
}
class B {
public void setAllComponents(Component[] myComponentArray, I myMethodsInterface) {
for(Component leaf : myComponentArray) {
if(leaf instanceof Container) { // recursive call if Container
Container node = (Container)leaf;
setAllComponents(node.getComponents(), myMethodInterface);
} // end if node
myMethodsInterface.myMethod(leaf);
} // end looping through components
}
}
class C {
A a = new A();
B b = new B();
public C() {
b.setAllComponents(this.getComponents(), component -> a.changeColor(component));
b.setAllComponents(this.getComponents(), component -> a.changeSize(component));
}
}
Класс C
может быть сокращен еще немного за счет использования ссылок на методы, подобных этому:
class C {
A a = new A();
B b = new B();
public C() {
b.setAllComponents(this.getComponents(), a::changeColor);
b.setAllComponents(this.getComponents(), a::changeSize);
}
}
Ответ 3
Начиная с Java 8 существует Function<T, R>
интерфейс (docs), в котором есть метод
R apply(T t);
Вы можете использовать его для передачи функций в качестве параметров другим функциям. T - тип ввода функции, R - тип возвращаемого значения.
В вашем примере вам нужно передать функцию, которая принимает Component
тип в качестве входных данных и ничего не возвращает - Void
. В данном случае Function<T, R>
это не лучший выбор, поскольку отсутствует автоматическая вставка типа Void. Интерфейс, который вы ищете, вызывается Consumer<T>
(docs) с помощью метода
void accept(T t);
Это будет выглядеть примерно так:
public void setAllComponents(Component[] myComponentArray, Consumer<Component> myMethod) {
for (Component leaf : myComponentArray) {
if (leaf instanceof Container) {
Container node = (Container) leaf;
setAllComponents(node.getComponents(), myMethod);
}
myMethod.accept(leaf);
}
}
И вы бы вызвали его, используя ссылки на методы:
setAllComponents(this.getComponents(), this::changeColor);
setAllComponents(this.getComponents(), this::changeSize);
Предполагается, что вы определили методы changeColor() и changeSize() в одном классе.
Если ваш метод принимает более одного параметра, вы можете использовать BiFunction<T, U, R>
- T и U являются типами входных параметров, а R - типом возвращаемого значения. Также есть BiConsumer<T, U>
(два аргумента, без возвращаемого типа). К сожалению, для 3 и более входных параметров вам придется создавать интерфейс самостоятельно. Например:
public interface Function4<A, B, C, D, R> {
R apply(A a, B b, C c, D d);
}
Ответ 4
Используйте java.lang.reflect.Method
объект и вызывайте invoke