Функционални указатели в Java

Това може да е нещо обичайно и тривиално, но изглежда ми е трудно да намеря конкретен отговор. В C# има концепция за делегати, която е силно свързана с идеята за функционални указатели от C++. Има ли подобна функционалност в Java? Като се има предвид, че указателите донякъде липсват, какъв е най-добрият начин за това? И за да бъде ясно, тук говорим за първа класа.


person dreadwail    schedule 02.07.2009    source източник
comment
Просто съм любопитен защо бихте искали това, при което слушателите или друга OOP конструкция биха изпълнили същата задача, като същевременно запазват своята обектна природа. Искам да кажа, че разбирам необходимостта от функционалността, предлагана от концепцията, само че може да се постигне с обикновен обект.. освен ако, разбира се, не пропускам нещо, затова задавам този въпрос! :-)   -  person Newtopian    schedule 02.07.2009
comment
Java 8 има ламбда изрази: docs.oracle.com/javase/tutorial/ java/javaOO/ Може да искате да проверите това. Не е съвсем функционален указател, но все пак може да бъде от по-голяма полза.   -  person FrustratedWithFormsDesigner    schedule 06.05.2014
comment
Препратките към методите на Java 8 са точно това, което питате.   -  person Steven    schedule 05.06.2014
comment
Java 8 препратки към методи е точно това, което питате. this::myMethod е семантично същото като създаването на ламбда paramA, paramB -> this.myMethod(paramA, paramB).   -  person Steven    schedule 05.06.2014
comment
възможен дубликат на Кой е най-близкият заместител на указател на функция в Java?   -  person nawfal    schedule 05.07.2014


Отговори (11)


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

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

Актуализация: горното е необходимо във версии на Java преди Java 8. Сега имаме много по-добри алтернативи, а именно ламбда:

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

и препратки към методите:

list.sort(MyClass::isGreaterThan);
person Michael Borgwardt    schedule 02.07.2009
comment
Шаблонът за проектиране на стратегията. Не е толкова грозен като указател на C или C++ функция, когато искате функцията да използва някои неглобални данни, които не са предоставени като аргумент на функцията. - person Raedwald; 14.01.2011
comment
@Raedwald C++ има функтори, а C++0x има затваряния и ламбда изградени върху функтори. Той дори има std::bind, който обвързва параметри към функции и връща извикващ се обект. Не мога да защитя C на тези основания, но C++ наистина е по-добър от Java за това. - person zneak; 02.03.2011
comment
Е, C++0x може да е, но сегашният стандарт C++ не е. - person Raedwald; 02.03.2011
comment
@zneak, @Raedwald: Можете да направите това в C, като прехвърлите указател към потребителски данни (напр.: struct). (и може да го капсулира с всяко ново ниво на индиректно обратно извикване) Всъщност е доста чисто. - person brice; 10.06.2011
comment
Да, всъщност всеки ден ще взема указатели на функции на C над груби класове за обвивки - person B T; 17.05.2012
comment
@Raedwald, за това са делегатите в C#! Те са чисти, безопасни за типа функционални указатели. - person devinbost; 09.05.2014
comment
Java 8 има по-добър синтаксис за това. - person Thorbjørn Ravn Andersen; 25.06.2014

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

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

Това е интерфейсът, който можем да предадем на някои, да речем, CollectionUtils2.doFunc(Collection 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++;
    }
});
person raupach    schedule 02.07.2009

Можете да използвате отражение, за да го направите.

Предайте като параметър обекта и името на метода (като низ) и след това извикайте метода. Например:

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");

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

person zoquete    schedule 31.05.2010
comment
Отражението не е правилният отговор за нищо друго, освен за това как да напиша бавен код на java :D - person Jay; 05.08.2013
comment
Може да е грозно и бавно, но вярвам, че отражението позволява да се правят неща, които базираното на интерфейс решение в приетия отговор не може да направи (освен ако не греша), а именно извикване на различни методи на един и същ обект в рамките на една и съща част от кода. - person Eusebius; 09.04.2014
comment
@zoquete.. Преглеждах тази публикация и имах съмнения.. така че според вашия подход, ако имам достъп до променливата theDescription, функцията ще се извиква всеки път, когато се осъществява достъп до променливата?? - person karthik27; 16.04.2014

Не, функциите не са първокласни обекти в java. Можете да направите същото, като имплементирате клас манипулатор - това е начинът, по който обратните извиквания се изпълняват в Swing и т.н.

Има обаче предложения за затваряне (официалното име на това, за което говорите) в бъдещи версии на java - Javaworld има интересна статия.

person jwoolard    schedule 02.07.2009

Това напомня за Екзекуцията в Кралството на Стив Йеге Съществителни имена. Основно се посочва, че Java се нуждае от обект за всяко действие и следователно няма "глаголни" обекти като функционални указатели.

person Yuval F    schedule 14.07.2009

За да постигнете подобна функционалност, можете да използвате анонимни вътрешни класове.

Ако трябваше да дефинирате интерфейс 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.
    }
}
person Kingamajick    schedule 02.07.2009

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
   }
}
person Alexander Oh    schedule 20.01.2016
comment
има ли концепцията за alias: напр. public List<T> collect(T t) = Collections::collect - person WestCoastProjects; 05.01.2020
comment
@javadba доколкото знам не. - person Alexander Oh; 05.01.2020

В 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");
        }
    }
}
person VoidPointer    schedule 02.07.2009

Внедрих поддръжка за обратно извикване/делегиране в Java, използвайки отражение. Подробности и работещ източник са достъпни на моя уебсайт.

Как работи

Имаме принципен клас с име Callback с вложен клас с име WithParms. API, който се нуждае от обратното извикване, ще приеме обект за обратно извикване като параметър и, ако е необходимо, ще създаде Callback.WithParms като променлива на метода. Тъй като много от приложенията на този обект ще бъдат рекурсивни, това работи много чисто.

Тъй като производителността все още е с висок приоритет за мен, не исках да се изисква да създавам масив от обекти за изхвърляне, който да съхранява параметрите за всяко извикване - в края на краищата в голяма структура от данни може да има хиляди елементи и при обработка на съобщения сценарий, в крайна сметка можем да обработваме хиляди структури от данни в секунда.

За да бъде нишково безопасен, масивът от параметри трябва да съществува уникално за всяко извикване на API метода и за ефективност трябва да се използва един и същ за всяко извикване на обратното извикване; Имах нужда от втори обект, който би бил евтин за създаване, за да обвържа обратното извикване с масив от параметри за извикване. Но в някои сценарии извикващият вече би имал масив от параметри по други причини. Поради тези две причини масивът от параметри не принадлежи към обекта за обратно извикване. Също така изборът на извикване (предаване на параметрите като масив или като отделни обекти) принадлежи на API, използвайки обратното извикване, което му позволява да използва извикването, което е най-подходящо за неговата вътрешна работа.

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

person Lawrence Dol    schedule 16.01.2010

Проверете затварянията как са били внедрени в библиотеката lambdaj. Те всъщност имат поведение, много подобно на C# делегатите:

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

person Mario Fusco    schedule 12.09.2009

В сравнение с повечето хора тук, аз съм нов в 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();
person ozgeneral    schedule 10.08.2015