Това може да е нещо обичайно и тривиално, но изглежда ми е трудно да намеря конкретен отговор. В C# има концепция за делегати, която е силно свързана с идеята за функционални указатели от C++. Има ли подобна функционалност в Java? Като се има предвид, че указателите донякъде липсват, какъв е най-добрият начин за това? И за да бъде ясно, тук говорим за първа класа.
Функционални указатели в Java
Отговори (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);
std::bind
, който обвързва параметри към функции и връща извикващ се обект. Не мога да защитя C на тези основания, но C++ наистина е по-добър от Java за това.
- person zneak; 02.03.2011
Можете да замените указател на функция с интерфейс. Да кажем, че искате да преминете през колекция и да направите нещо с всеки елемент.
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++;
}
});
Можете да използвате отражение, за да го направите.
Предайте като параметър обекта и името на метода (като низ) и след това извикайте метода. Например:
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. Можете да направите същото, като имплементирате клас манипулатор - това е начинът, по който обратните извиквания се изпълняват в Swing и т.н.
Има обаче предложения за затваряне (официалното име на това, за което говорите) в бъдещи версии на java - Javaworld има интересна статия.
Това напомня за Екзекуцията в Кралството на Стив Йеге Съществителни имена. Основно се посочва, че Java се нуждае от обект за всяко действие и следователно няма "глаголни" обекти като функционални указатели.
За да постигнете подобна функционалност, можете да използвате анонимни вътрешни класове.
Ако трябваше да дефинирате интерфейс 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.
}
}
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
}
}
alias
: напр. public List<T> collect(T t) = Collections::collect
- person WestCoastProjects; 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");
}
}
}
Внедрих поддръжка за обратно извикване/делегиране в Java, използвайки отражение. Подробности и работещ източник са достъпни на моя уебсайт.
Как работи
Имаме принципен клас с име Callback с вложен клас с име WithParms. API, който се нуждае от обратното извикване, ще приеме обект за обратно извикване като параметър и, ако е необходимо, ще създаде Callback.WithParms като променлива на метода. Тъй като много от приложенията на този обект ще бъдат рекурсивни, това работи много чисто.
Тъй като производителността все още е с висок приоритет за мен, не исках да се изисква да създавам масив от обекти за изхвърляне, който да съхранява параметрите за всяко извикване - в края на краищата в голяма структура от данни може да има хиляди елементи и при обработка на съобщения сценарий, в крайна сметка можем да обработваме хиляди структури от данни в секунда.
За да бъде нишково безопасен, масивът от параметри трябва да съществува уникално за всяко извикване на API метода и за ефективност трябва да се използва един и същ за всяко извикване на обратното извикване; Имах нужда от втори обект, който би бил евтин за създаване, за да обвържа обратното извикване с масив от параметри за извикване. Но в някои сценарии извикващият вече би имал масив от параметри по други причини. Поради тези две причини масивът от параметри не принадлежи към обекта за обратно извикване. Също така изборът на извикване (предаване на параметрите като масив или като отделни обекти) принадлежи на API, използвайки обратното извикване, което му позволява да използва извикването, което е най-подходящо за неговата вътрешна работа.
След това вложеният клас WithParms не е задължителен и служи за две цели, той съдържа масива от обекти на параметри, необходими за извикванията за обратно извикване, и предоставя 10 претоварени метода invoke() (с от 1 до 10 параметъра), които зареждат масива от параметри и след това извикване на целта за обратно извикване.
Проверете затварянията как са били внедрени в библиотеката lambdaj. Те всъщност имат поведение, много подобно на C# делегатите:
http://code.google.com/p/lambdaj/wiki/Closures
В сравнение с повечето хора тук, аз съм нов в 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();
this::myMethod
е семантично същото като създаването на ламбдаparamA, paramB -> this.myMethod(paramA, paramB)
. - person Steven   schedule 05.06.2014