Вопрос по java – Указатели на функции в Java

142

Это может быть что-то обычное и тривиальное, но мне кажется, что мне трудно найти конкретный ответ. В C # существует концепция делегатов, которая тесно связана с идеей указателей на функции из C ++. Есть ли подобная функциональность в Java? Учитывая, что указатели в некоторой степени отсутствуют, что лучше для этого? И чтобы быть ясным, мы говорим здесь первым классом.

Ссылки на методы Java 8 - это именно то, что вы просите. Steven
Java 8method references это именно то, что вы просите.this::myMethod семантически то же самое, что создание лямбдыparamA, paramB -> this.myMethod(paramA, paramB). Steven
Java 8 имеет лямбда-выражения:docs.oracle.com/javase/tutorial/java/javaOO/… Вы можете проверить это. Не совсем указатель на функцию, но все еще может быть более полезным. FrustratedWithFormsDesigner
возможный дубликатWhat's the nearest substitute for a function pointer in Java? nawfal
Мне просто любопытно, почему вы хотели бы, чтобы слушатели или другие конструкции ООП выполняли ту же задачу, сохраняя при этом свою объектную природу. Я имею в виду, что я понимаю необходимость функциональности, предлагаемой концепцией, просто для того, чтобы ее можно было достичь с помощью простого объекта ... если, конечно, я что-то упустил, отсюда мой вопрос! :-) Newtopian

Ваш Ответ

11   ответов
62

Вы можете заменить указатель на функцию интерфейсом. Допустим, вы хотите просмотреть коллекцию и что-то сделать с каждым элементом.

public interface IFunction {
  public void execute(Object o);
}

Это интерфейс, который мы могли бы передать, скажем, CollectionUtils2.doFunc (Коллекция c, IFunction f).

public static void doFunc(Collection c, IFunction f) {
   for (Object o : c) {
      f.execute(o);
   }
}

В качестве примера, скажем, у нас есть коллекция чисел, и вы хотели бы добавить 1 к каждому элементу.

CollectionUtils2.doFunc(List numbers, new IFunction() {
    public void execute(Object o) {
       Integer anInt = (Integer) o;
       anInt++;
    }
});
Вы можете добавить к этому дженерики ...
Очень ясно и кратко
5

В Java такого нет. Вам нужно будет обернуть вашу функцию в некоторый объект и передать ссылку на этот объект, чтобы передать ссылку на метод этого объекта.

Синтаксически это может быть в определенной степени облегчено с помощью анонимных классов, определенных на месте, или анонимных классов, определенных как переменные-члены класса.

Пример:

class MyComponent extends JPanel {
    private JButton button;
    public MyComponent() {
        button = new JButton("click me");
        button.addActionListener(buttonAction);
        add(button);
    }

    private ActionListener buttonAction = new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            // handle the event...
            // note how the handler instance can access 
            // members of the surrounding class
            button.setText("you clicked me");
        }
    }
}
5

Java8 представила лямбды иссылки на методы, Так что, если ваша функция соответствуетфункциональный интерфейс (вы можете создать свой собственный), вы можете использовать ссылку на метод в этом случае.

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

public class Test {
   public void test1(Integer i) {}
   public void test2(Integer i) {}
   public void consumer(Consumer<Integer> a) {
     a.accept(10);
   }
   public void provideConsumer() {
     consumer(this::test1);   // method reference
     consumer(x -> test2(x)); // lambda
   }
}
20

Нет, функции не являются объектами первого класса в Java. Вы можете сделать то же самое, реализовав класс-обработчик - так реализованы обратные вызовы в Swing и т. Д.

Однако в будущих версиях Java есть предложения по закрытию (официальное название того, о чем вы говорите).JavaWorld есть интересная статья.

Интересно - спасибо за информацию. Ben Lakey
2

Проверьте замыкания, как они были реализованы в библиотеке lambdaj. Они на самом деле ведут себя очень похоже на делегаты C #:

http://code.google.com/p/lambdaj/wiki/Closures

13

Это напоминает Стива ЙеггеКазнь в царстве существительных, В основном это говорит о том, что Java нуждается в объекте для каждого действия, и, следовательно, не имеет «только для глагола» сущности, такие как указатели на функции.

0

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

 /*Just to merge functions in a common name*/
 public class CustomFunction{ 
 public CustomFunction(){}
 }

 /*Actual functions*/
 public class Function1 extends CustomFunction{
 public Function1(){}
 public void execute(){...something here...}
 }

 public class Function2 extends CustomFunction{
 public Function2(){}
 public void execute(){...something here...}
 }

 .....
 /*in Main class*/
 CustomFunction functionpointer = null;

затем в зависимости от приложения назначьте

 functionpointer = new Function1();
 functionpointer = new Function2();

и т.п.

и позвонить по

 functionpointer.execute();
120

Идиома Java для функциональности, подобной указателю на функцию, является анонимным классом, реализующим интерфейс, например,

Collections.sort(list, new Comparator<MyClass>(){
    public int compare(MyClass a, MyClass b)
    {
        // compare objects
    }
});

Update: вышеупомянутое необходимо в версиях Java до Java 8. Теперь у нас есть много более хороших альтернатив, а именно лямбда-выражения:

list.sort((a, b) -> a.isGreaterThan(b));

и ссылки на методы:

list.sort(MyClass::isGreaterThan);
Java 8 имеет лучший синтаксис для этого.
@zneak, @Raedwald: Вы можете сделать это в C, передав указатель на пользовательские данные (например, struct). (и может инкапсулировать его с каждым новым уровнем косвенной обратного вызова). Это на самом деле довольно чисто.
Да, я на самом деле буду использовать указатели на функции C над грубыми классами-обертками в любой день
Стратегия дизайна шаблона. Не так страшно, как указатель на функцию C или C ++, если вы хотите, чтобы функция использовала некоторые неглобальные данные, которые не предоставлены в качестве аргумента функции.
@Raedwald C ++ имеет функторы, а C ++ 0x имеет замыкания и лямбды, построенные поверх функторов. Это даже имеетstd::bind, который привязывает параметры к функциям и возвращает вызываемый объект. Я не могу защищать C на этих основаниях, но C ++ действительноis лучше чем Java для этого.
41

Вы можете использовать отражение, чтобы сделать это.

Передайте в качестве параметра объект и имя метода (в виде строки), а затем вызовите метод. Например:

Object methodCaller(Object theObject, String methodName) {
   return theObject.getClass().getMethod(methodName).invoke(theObject);
   // Catch the exceptions
}

А затем используйте его как в:

String theDescription = methodCaller(object1, "toString");
Class theClass = methodCaller(object2, "getClass");

Конечно, проверьте все исключения и добавьте необходимые приведения.

Отражение не является правильным ответом ни на что, кроме как "Как написать медленный хитрый Java-код" : D
Это 1. уродливо и 2. медленно. Но все же правильный ответ. ;)
@zoquete .. Я просматривал этот пост, у меня были сомнения .. поэтому в вашем подходе, если я получу доступ к переменной "Описание", функция будет вызываться каждый раз, когда к переменной обращаются ??
Это может быть "некрасиво" и медленно, но я считаю, что рефлексия позволяет делать вещи, которые не может сделать решение на основе интерфейса в принятом ответе (если я не ошибаюсь), а именно вызывать различные методы одного и того же объекта в пределах одной и той же части кода.
7

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

Если бы вы должны были определить интерфейсFoo:

interface Foo {
    Object myFunc(Object arg);
}

Создать методbar который получит «указатель на функцию»; в качестве аргумента:

public void bar(Foo foo) {
    // .....
    Object object = foo.myFunc(argValue);
    // .....
}

Наконец, вызовите метод следующим образом:

bar(new Foo() {
    public Object myFunc(Object arg) {
        // Function code.
    }
}
3

Я реализовал поддержку обратного вызова / делегата в Java, используя отражение. Подробности и рабочий источникдоступно на моем сайте.

How It Works

У нас есть основной класс с именем Callback с вложенным классом с именем WithParms. API, которому необходим обратный вызов, примет объект Callback в качестве параметра и, если необходимо, создаст Callback.WithParms в качестве переменной метода. Поскольку многие приложения этого объекта будут рекурсивными, это работает очень чисто.

Поскольку производительность по-прежнему остается для меня высоким приоритетом, я не хочу создавать одноразовый массив объектов для хранения параметров для каждого вызова - ведь в большой структуре данных могут быть тысячи элементов и обработка сообщений В этом сценарии мы могли бы обработать тысячи структур данных в секунду.

Чтобы обеспечить безопасность потоков, массив параметров должен существовать уникально для каждого вызова метода API, и для эффективности его следует использовать для каждого вызова обратного вызова; Мне нужен был второй объект, который было бы дешево создать, чтобы связать обратный вызов с массивом параметров для вызова. Но в некоторых случаях вызывающий уже имел массив параметров по другим причинам. По этим двум причинам массив параметров не принадлежит объекту Callback. Кроме того, выбор вызова (передача параметров в виде массива или отдельных объектов) принадлежит API, использующему обратный вызов, позволяющий ему использовать любой вызов, который лучше всего подходит для его внутренней работы.

Тогда вложенный класс WithParms является необязательным и служит двум целям: он содержит массив объектов параметров, необходимый для вызовов обратного вызова, и предоставляет 10 перегруженных методов invoke () (с 1 до 10 параметров), которые загружают массив параметров, а затем вызвать цель обратного вызова.

Похожие вопросы