Pointer Fungsi di Jawa

148

Ini mungkin sesuatu yang umum dan sepele, tetapi saya tampaknya kesulitan menemukan jawaban yang konkret. Dalam C # ada konsep delegasi, yang sangat terkait dengan gagasan fungsi pointer dari C ++. Apakah ada fungsi serupa di Jawa? Mengingat bahwa pointer agak tidak ada, apa cara terbaik tentang ini? Dan untuk lebih jelasnya, kita berbicara kelas satu di sini.

rambut gimbal
sumber
1
Saya hanya ingin tahu mengapa Anda ingin ini di mana pendengar atau OOP lainnya membangun akan melakukan tugas yang sama sambil mempertahankan sifat objek mereka. Maksud saya, saya memahami perlunya fungsionalitas yang ditawarkan oleh konsep itu hanya dapat dicapai dengan objek polos .. kecuali, tentu saja saya kehilangan sesuatu, maka saya mengajukan pertanyaan ini! :-)
Newtopian
2
Java 8 memiliki ekspresi lambda: docs.oracle.com/javase/tutorial/java/javaOO/… Anda mungkin ingin memeriksanya. Tidak cukup pointer fungsi, tetapi mungkin masih lebih bermanfaat.
FrustratedWithFormsDesigner
6
Referensi metode Java 8 persis seperti yang Anda minta.
Steven
8
Referensi metode Java 8 persis seperti yang Anda minta. this::myMethodsecara semantik sama dengan menciptakan lambda paramA, paramB -> this.myMethod(paramA, paramB).
Steven

Jawaban:

126

Idiom Java untuk fungsionalitas fungsi-pointer-suka adalah kelas anonim yang mengimplementasikan antarmuka, misalnya

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

Pembaruan: hal-hal di atas diperlukan dalam versi Java sebelum Java 8. Sekarang kami memiliki alternatif yang lebih baik, yaitu lambdas:

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

dan referensi metode:

list.sort(MyClass::isGreaterThan);
Michael Borgwardt
sumber
2
Pola desain Strategi. Tidak seburuk pointer fungsi C atau C ++ ketika Anda ingin fungsi menggunakan beberapa data non global yang tidak disediakan sebagai argumen ke fungsi.
Raedwald
75
@Raedwald C ++ memiliki functors, dan C ++ 0x memiliki closures dan lambdas dibangun di atas functors. Bahkan memiliki std::bind, yang mengikat parameter ke fungsi dan mengembalikan objek yang bisa dipanggil. Saya tidak bisa membela C atas dasar ini, tetapi C ++ benar - benar lebih baik daripada Java untuk ini.
zneak
21
@ zneak, @Raedwald: Anda dapat melakukan ini dalam C dengan meneruskan pointer ke data pengguna (mis: struct). (dan dapat merangkumnya dengan setiap tingkat tipuan panggilan balik baru) Ini sebenarnya cukup bersih.
brice
25
Ya, saya benar-benar akan mengambil pointer fungsi C lebih dari kelas pembungkus crufty setiap hari
BT
3
Java 8 memiliki sintaks yang lebih baik untuk ini.
Thorbjørn Ravn Andersen
65

Anda bisa mengganti penunjuk fungsi dengan antarmuka. Katakanlah Anda ingin menjalankan koleksi dan melakukan sesuatu dengan setiap elemen.

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

Ini adalah antarmuka yang bisa kita berikan kepada beberapa orang mengatakan CollectionUtils2.doFunc (Collection c, IFunction f).

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

Sebagai contoh katakanlah kami memiliki koleksi angka dan Anda ingin menambahkan 1 ke setiap elemen.

CollectionUtils2.doFunc(List numbers, new IFunction() {
    public void execute(Object o) {
       Integer anInt = (Integer) o;
       anInt++;
    }
});
Björn Raupach
sumber
42

Anda dapat menggunakan refleksi untuk melakukannya.

Lulus sebagai parameter objek dan nama metode (sebagai string) dan kemudian memanggil metode. Sebagai contoh:

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

Dan kemudian gunakan seperti pada:

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

Tentu saja, periksa semua pengecualian dan tambahkan gips yang dibutuhkan.

zoquete
sumber
7
Refleksi bukanlah jawaban yang tepat untuk apa pun kecuali "Bagaimana saya menulis kode java lambat yang cerdik": D
Jacob
5
Mungkin "jelek" dan lambat, tetapi saya percaya bahwa refleksi memungkinkan untuk melakukan hal-hal yang tidak dapat dilakukan oleh solusi berbasis antarmuka dalam jawaban yang diterima (kecuali saya salah), yaitu memanggil berbagai metode dari objek yang sama dalam kode yang sama bagian.
Eusebius
@zoquete .. Saya sudah melalui posting ini dan punya keraguan .. jadi dalam pendekatan Anda, jika saya mengakses variabel "theDescription", fungsi akan dipanggil masing-masing dan setiap kali variabel diakses ??
karthik27
20

Tidak, fungsi bukan objek kelas satu di java. Anda dapat melakukan hal yang sama dengan menerapkan kelas penangan - ini adalah bagaimana panggilan balik diterapkan dalam Swing dll.

Namun ada proposal untuk penutupan (nama resmi untuk apa yang Anda bicarakan) di versi masa depan java - Javaworld memiliki artikel yang menarik.

joolool
sumber
15

Ini mengingatkan saya pada Eksekusi Steve Yegge di Kerajaan Kata Benda . Pada dasarnya menyatakan bahwa Java membutuhkan objek untuk setiap tindakan, dan karena itu tidak memiliki entitas "hanya-kata kerja" seperti pointer fungsi.

Yuval F
sumber
8

Untuk mencapai fungsionalitas serupa, Anda bisa menggunakan kelas dalam anonim.

Jika Anda mendefinisikan antarmuka Foo:

interface Foo {
    Object myFunc(Object arg);
}

Buat metode baryang akan menerima 'penunjuk fungsi' sebagai argumen:

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

Akhirnya panggil metode tersebut sebagai berikut:

bar(new Foo() {
    public Object myFunc(Object arg) {
        // Function code.
    }
}
Kingamajick
sumber
7

Java8 telah memperkenalkan lambdas dan referensi metode . Jadi, jika fungsi Anda cocok dengan antarmuka fungsional (Anda dapat membuatnya sendiri), Anda dapat menggunakan referensi metode dalam kasus ini.

Java menyediakan satu set antarmuka fungsional umum . sedangkan Anda bisa melakukan hal berikut:

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
   }
}
Alex
sumber
apakah ada konsep alias: eg public List<T> collect(T t) = Collections::collect
javadba
@javadba sejauh yang saya tidak tahu.
Alex
5

Tidak ada yang seperti itu di Jawa. Anda perlu membungkus fungsi Anda menjadi beberapa objek dan meneruskan referensi ke objek itu untuk meneruskan referensi ke metode pada objek itu.

Secara sintaksis, ini dapat dikurangi sampai batas tertentu dengan menggunakan kelas anonim yang didefinisikan di tempat atau kelas anonim yang didefinisikan sebagai variabel anggota kelas.

Contoh:

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");
        }
    }
}
VoidPointer
sumber
3

Saya telah menerapkan dukungan panggilan balik / delegasi di Jawa menggunakan refleksi. Detail dan sumber kerja tersedia di situs web saya .

Bagaimana itu bekerja

Kami memiliki kelas prinsip bernama Callback dengan kelas bersarang bernama WithParms. API yang membutuhkan panggilan balik akan mengambil objek Callback sebagai parameter dan, jika perlu, buat Callback. Karena banyak sekali aplikasi objek ini akan bersifat rekursif, ini bekerja dengan sangat bersih.

Dengan kinerja yang masih merupakan prioritas tinggi bagi saya, saya tidak ingin diharuskan untuk membuat array objek sekali pakai untuk menyimpan parameter untuk setiap doa - setelah semua dalam struktur data besar mungkin ada ribuan elemen, dan dalam pemrosesan pesan skenario kita akhirnya bisa memproses ribuan struktur data per detik.

Untuk menjadi threadsafe, array parameter harus ada secara unik untuk setiap pemanggilan metode API, dan untuk efisiensi yang sama harus digunakan untuk setiap pemanggilan callback; Saya membutuhkan objek kedua yang akan lebih murah untuk dibuat untuk mengikat panggilan balik dengan array parameter untuk doa. Tetapi, dalam beberapa skenario, penyerang akan sudah memiliki array parameter karena alasan lain. Karena dua alasan ini, array parameter tidak termasuk dalam objek Callback. Juga pilihan doa (melewati parameter sebagai array atau sebagai objek individual) berada di tangan API menggunakan panggilan balik yang memungkinkannya untuk menggunakan doa mana saja yang paling cocok untuk pekerjaan dalam dirinya.

Kelas bersarang WithParms, kemudian, adalah opsional dan melayani dua tujuan, ini berisi array objek parameter yang diperlukan untuk panggilan balik, dan menyediakan 10 metode pemanggilan () yang berlebihan (dengan dari 1 hingga 10 parameter) yang memuat array parameter dan kemudian aktifkan target panggilan balik.

Lawrence Dol
sumber
2

Periksa penutupan bagaimana mereka telah diterapkan di perpustakaan lambdaj. Mereka sebenarnya memiliki perilaku yang sangat mirip dengan delegasi C #:

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

Mario Fusco
sumber
-1

Relatif untuk kebanyakan orang di sini saya baru mengenal java tetapi karena saya belum melihat saran serupa saya punya alternatif lain untuk menyarankan. Saya tidak yakin apakah ini praktik yang baik atau tidak, atau bahkan menyarankan sebelumnya dan saya tidak mengerti. Saya suka karena saya pikir itu deskriptif sendiri.

 /*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;

kemudian tergantung pada aplikasinya, tetapkan

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

dll.

dan menelepon

 functionpointer.execute();
ozgeneral
sumber