Как передать функцию в качестве параметра в Java?
Можно ли передать метод в метод Java в качестве параметра? Если так, может кто-нибудь, пожалуйста, наставить меня Это не кажется тривиальным
8 ответов
Java 8 и выше
Использование лямбда-выражений Java 8+, если у вас есть класс или интерфейс только с одним методом, например:
public interface MyInterface {
String doSomething(int param1, String param2);
}
затем в любом месте, где используется MyInterface, вы можете заменить лямбда-выражение:
class MyClass {
public MyInterface myInterface = (p1, p2) -> { return p2 + p1; };
}
Например, вы можете очень быстро создать новую тему:
new Thread(() -> someMethod()).start();
И используйте синтаксис ссылки на метод, чтобы сделать его еще чище:
new Thread(this::someMethod).start();
Без лямбда-выражений эти два последних примера выглядят так:
new Thread(new Runnable() { someMethod(); }).start();
До Java 8
Распространенным паттерном было бы "оборачивать" его внутри интерфейса, например Callable
Например, тогда вы передаете в Callable:
public T myMethod(Callable<T> func) {
return func.call();
}
Этот образец известен как Образец Команды.
Имейте в виду, что вам лучше всего создать интерфейс для вашего конкретного использования. Если вы решили использовать callable, то вы заменили бы T выше любым типом возвращаемого значения, например String.
В ответ на ваш комментарий ниже вы можете сказать:
public int methodToPass() {
// do something
}
public void dansMethod(int i, Callable<Integer> myFunc) {
// do something
}
затем назовите его, возможно, используя анонимный внутренний класс:
dansMethod(100, new Callable<Integer>() {
public Integer call() {
return methodToPass();
}
});
Имейте в виду, что это не "трюк". Это просто базовый концептуальный эквивалент Java для указателей на функции.
Вы можете использовать отражение Java, чтобы сделать это. Метод будет представлен как экземпляр java.lang.reflect.Method.
import java.lang.reflect.Method;
public class Demo {
public static void main(String[] args) throws Exception{
Class[] parameterTypes = new Class[1];
parameterTypes[0] = String.class;
Method method1 = Demo.class.getMethod("method1", parameterTypes);
Demo demo = new Demo();
demo.method2(demo, method1, "Hello World");
}
public void method1(String message) {
System.out.println(message);
}
public void method2(Object object, Method method, String message) throws Exception {
Object[] parameters = new Object[1];
parameters[0] = message;
method.invoke(object, parameters);
}
}
Лямбда-выражения
Чтобы добавить превосходный ответ jk., Теперь вы можете легче передавать метод с помощью лямбда-выражений (в 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 оценивает выражение и затем возвращает его значение. В качестве альтернативы вы можете использовать инструкцию возврата:
p -> { return p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25; }
Оператор возврата не является выражением; в лямбда-выражении вы должны заключать выражения в фигурные скобки ({}). Однако вам не нужно заключать вызов метода void в фигурные скобки. Например, следующее является допустимым лямбда-выражением:
email -> System.out.println(email)
Обратите внимание, что лямбда-выражение очень похоже на объявление метода; Вы можете рассматривать лямбда-выражения как анонимные методы - методы без имени.
Вот как вы можете "передать метод" с помощью лямбда-выражения:
Примечание: здесь используется новый стандартный функциональный интерфейс,java.util.function.IntConsumer
,
class A {
public static void methodToPass(int i) {
// do stuff
}
}
import java.util.function.IntConsumer;
class B {
public void dansMethod(int i, IntConsumer aMethod) {
/* you can now call the passed method by saying aMethod.accept(i), and it
will be the equivalent of saying A.methodToPass(i) */
}
}
class C {
B b = new B();
public C() {
b.dansMethod(100, j -> A.methodToPass(j)); //Lambda Expression here
}
}
Приведенный выше пример можно сократить еще больше, используя ::
оператор.
public C() {
b.dansMethod(100, A::methodToPass);
}
Благодаря Java 8 вам не нужно выполнять шаги, описанные ниже, чтобы передать функцию методу, для этого и предназначены лямбды, см . Учебник Oracle по лямбда-выражениям. В оставшейся части этого поста описывается, что мы должны были делать в старые добрые времена, чтобы реализовать эту функциональность.
Обычно вы объявляете свой метод как принимающий некоторый интерфейс с одним методом, затем вы передаете объект, который реализует этот интерфейс. Например, в коллекциях commons, где у вас есть интерфейсы для Closure, Transformer и Predicate, а также методы, в которые вы передаете их реализацию. Guava - это новые улучшенные коллекции общин, вы можете найти там эквивалентные интерфейсы.
Так, например, commons-collection имеет org.apache.commons.collections.CollectionUtils, в котором есть множество статических методов, которые принимают объекты, переданные для случайного выбора одного, существует один вызванный объект с этой сигнатурой:
static boolean exists(java.util.Collection collection, Predicate predicate)
Он принимает объект, который реализует интерфейс Predicate, что означает, что у него должен быть метод, который принимает некоторый объект и возвращает логическое значение.
Так что я могу назвать это так:
CollectionUtils.exists(someCollection, new Predicate() {
public boolean evaluate(Object object) {
return ("a".equals(object.toString());
}
});
и он возвращает истину или ложь в зависимости от того, someCollection
содержит объект, для которого предикат возвращает true.
Во всяком случае, это всего лишь пример, а commons-collection устарела. Я просто забываю эквивалент в Гуаве.
Java поддерживает замыкания просто отлично. Он просто не поддерживает функции, поэтому синтаксис, к которому вы привыкли для замыканий, гораздо более неуклюжий и громоздкий: вы должны свернуть все в классе с помощью метода. Например,
public Runnable foo(final int x) {
return new Runnable() {
public void run() {
System.out.println(x);
}
};
}
Вернет объект Runnable, чей run()
метод "закрывает" x
передается, как и в любом языке, который поддерживает первоклассные функции и замыкания.
Я использовал шаблон команды @jk. упоминается, добавив тип возврата:
public interface Callable<I, O> {
public O call(I input);
}
Я знаю, что это довольно старый пост, но у меня есть другое, немного более простое решение. Вы можете создать другой класс внутри и сделать его абстрактным. Затем сделайте абстрактный метод, назовите его как хотите. В исходном классе создайте метод, который принимает новый класс в качестве параметра, в этом методе вызовите абстрактный метод. Это будет выглядеть примерно так.
public class Demo {
public Demo(/.../){
}
public void view(Action a){
a.preform();
}
/**
* The Action Class is for making the Demo
* View Custom Code
*/
public abstract class Action {
public Action(/.../){
}
abstract void preform();
}
}
Теперь вы можете сделать что-то подобное для вызова метода из класса.
/...
Demo d = new Demo;
Action a = new Action() {
@Override
void preform() {
//Custom Method Code Goes Here
}
};
/.../
d.view(a)
Как я уже сказал, я знаю, что он старый, но так я думаю, что это немного проще. Надеюсь, поможет.
Java (пока) не поддерживает замыкания. Но есть и другие языки, такие как Scala и Groovy, которые работают в JVM и поддерживают закрытие.