Как передать функцию в качестве параметра в Java?

Можно ли передать метод в Java-метод в качестве параметра? Если да, может ли кто-нибудь, пожалуйста, направить меня? Это не кажется тривиальным

Ответ 1

Java 8 и выше

Используя лямбда-выражения Java 8+, если у вас есть класс или интерфейс только с одним абстрактным методом (иногда называемым типом SAM), например:

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 для функциональных указателей.

Ответ 2

Вы можете использовать отражение 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);
    }

}

Ответ 3

Лямбда-выражения

Чтобы добавить к jk. отличный ответ, теперь вы можете легко пройти метод, используя Lambda Expressions (в 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)
    

Обратите внимание, что лямбда-выражение очень похоже на объявление метода; вы можете рассматривать лямбда-выражения как анонимные методы-методы без имени.


Вот как вы можете "передать метод" с помощью выражения лямбда:

Примечание: здесь используется новый стандартный функциональный интерфейс, 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);
}

Ответ 4

Благодаря Java 8 вам не нужно выполнять следующие шаги, чтобы передать функцию методу, для чего предназначены лямбда, см. Oracle Lambda Expression учебник. Остальная часть этого сообщения описывает, что мы привыкли делать в плохие старые дни, чтобы реализовать эту функциональность.

Обычно вы объявляете свой метод как принятие какого-либо интерфейса с помощью одного метода, затем вы передаете объект, реализующий этот интерфейс. Например, в коллекциях commons, где у вас есть интерфейсы для Closure, Transformer и Predicate и методы, которые вы передаете им. Guava - это новые улучшенные коллективные фонды, вы можете найти там эквивалентные интерфейсы.

Так, например, в коллекциях коллекций есть 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());
    }
});

и он возвращает true или false в зависимости от того, содержит ли someCollection объект, для которого предикат возвращает true.

Во всяком случае, это всего лишь пример, и коллективные фонды устарели. Я просто забываю эквивалент в Гуаве.

Ответ 5

Java поддерживает закрытие просто отлично. Он просто не поддерживает функции, поэтому синтаксис, который вы использовали для закрытия, намного более неудобен и громоздки: вы должны обернуть все в классе с помощью метода. Например,

public Runnable foo(final int x) {
  return new Runnable() {
    public void run() {
      System.out.println(x);
    }
  };
}

Вернет объект Runnable, метод run() которого "закрывает" переданный x, как и на любом языке, который поддерживает функции и закрытия первого класса.

Ответ 6

Я использовал шаблон команды @jk. упоминается, добавив тип возврата:

public interface Callable<I, O> {

    public O call(I input);   
}

Ответ 7

Я знаю, что это довольно старый пост, но у меня есть другое, немного более простое решение. Вы можете создать другой класс внутри и сделать его абстрактным. Затем сделайте абстрактный метод, назовите его как хотите. В исходном классе создайте метод, который принимает новый класс в качестве параметра, в этом методе вызовите абстрактный метод. Это будет выглядеть примерно так.

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)

Как я уже сказал, я знаю, что он старый, но так я думаю, что это немного проще. Надеюсь, поможет.

Ответ 8

Java не поддерживает (пока) поддержку закрытий. Но есть и другие языки, такие как Scala и Groovy, которые запускаются в JVM и поддерживают закрытие.