Как передать функцию в качестве параметра в Java? [дубликат]
этот вопрос уже есть ответ здесь:
- метод Java Pass в качестве параметра 13 ответов
можно ли передать метод в метод 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
, например, затем вы переходите в вызываемое:
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.ленг.отражать.Метод.
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 по лямбда-выражениям:
синтаксис лямбда-выражений
лямбда-выражение состоит из следующего:
A разделенный запятыми список формальных параметров, заключенный в круглые скобки. В CheckPerson.метод теста содержит один параметр, p, который представляет собой экземпляр класса человек.
Примечание: ты можно опустить тип данных параметров в лямбда-выражении. В кроме того, вы можете опустить скобки, если есть только один параметр. Например, также допустимо следующее лямбда-выражение: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)
обратите внимание, что лямбда-выражение очень похоже на объявление метода ; лямбда-выражения можно рассматривать как анонимные методы-методы без имени.
вот как вы можете "передать метод", используя лямбда-выражение:
Примечание: это использует новый стандарт функциональный интерфейс, 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-collections, где у вас есть интерфейсы для замыкания, трансформатора и предиката и методы, в которые вы передаете реализации этих. Guava-это новые улучшенные общие коллекции, вы можете найти там эквивалентные интерфейсы.
Так, например, commons-collections имеет org.апаш.палата общин.коллекции.CollectionUtils, который имеет множество статических методов, которые принимают переданные объекты, чтобы выбрать один наугад, есть один, называемый exists с этой сигнатурой:
static boolean exists(java.util.Collection collection, Predicate predicate)
Он принимает объект это реализует предикат интерфейса, что означает, что на нем должен быть метод, который принимает некоторый объект и возвращает логическое значение.
Так я могу назвать это так:
CollectionUtils.exists(someCollection, new Predicate() {
public boolean evaluate(Object object) {
return ("a".equals(object.toString());
}
});
и это возвращает true или false в зависимости от того,someCollection
содержит объект, для которого предикат возвращает true.
В любом случае, это всего лишь пример, и commons-collections устарели. Я просто забыл эквивалент в гуаве.
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 и поддерживают закрытие.