Delegasi Jawa?


194

Apakah bahasa Jawa memiliki fitur delegasi, mirip dengan bagaimana C # memiliki dukungan untuk delegasi?


20
@Suma Bagaimana ini bisa menjadi duplikat jika pertanyaan yang Anda sebutkan diposting setahun setelah ini?
Ani

1
Java 8 memiliki fitur yang cukup mirip delegasi. Ini disebut lambdas.
tbodt

4
@tbodt agar lebih benar, Java 8 memiliki fitur yang cukup mirip delegasi. Ini disebut antarmuka fungsional . Lambdas adalah salah satu cara untuk menciptakan contoh delegasi tersebut (secara anonim).
nawfal

3
Jawaban di bawah ini berasal dari Pra-Jawa 8. Jawa Pos 8 melihat jawaban di utas ini: stackoverflow.com/questions/20311779/…
Michael Lloyd Lee mlk

@nawfal, +1, tetapi untuk lebih tepatnya, Java 7 dan di bawahnya sudah memiliki fitur yang cukup mirip delegasi. Ini disebut antarmuka biasa.
Pacerier

Jawaban:


152

Tidak juga, tidak.

Anda mungkin dapat mencapai efek yang sama dengan menggunakan refleksi untuk mendapatkan objek Metode yang dapat Anda panggil, dan cara lain adalah membuat antarmuka dengan metode 'pemanggilan' atau 'eksekusi' tunggal, dan kemudian instantiate mereka untuk memanggil metode Anda tertarik (yaitu menggunakan kelas batin anonim).

Anda mungkin juga menemukan artikel ini menarik / berguna: Programmer Java Melihat C # Delegate (@ archive.org)


3
Solusi dengan memanggil () sebagai satu-satunya fungsi anggota antarmuka sangat bagus.
Stephane Rolland

1
Tapi lihat contoh saya di sini tentang apa yang akan dilakukan, bahkan di Jawa 7, untuk mencapai yang setara dengan delegasi C #.
ToolmakerSteve

63

Bergantung tepat apa yang Anda maksud, Anda dapat mencapai efek yang sama (melewati metode) menggunakan Pola Strategi.

Alih-alih baris seperti ini yang menyatakan tanda tangan metode bernama:

// C#
public delegate void SomeFunction();

mendeklarasikan antarmuka:

// Java
public interface ISomeBehaviour {
   void SomeFunction();
}

Untuk implementasi konkret dari metode ini, tentukan kelas yang mengimplementasikan perilaku:

// Java
public class TypeABehaviour implements ISomeBehaviour {
   public void SomeFunction() {
      // TypeA behaviour
   }
}

public class TypeBBehaviour implements ISomeBehaviour {
   public void SomeFunction() {
      // TypeB behaviour
   }
}

Maka di mana pun Anda memiliki SomeFunctiondelegasi dalam C #, gunakan ISomeBehaviourreferensi sebagai gantinya:

// C#
SomeFunction doSomething = SomeMethod;
doSomething();
doSomething = SomeOtherMethod;
doSomething();

// Java
ISomeBehaviour someBehaviour = new TypeABehaviour();
someBehaviour.SomeFunction();
someBehaviour = new TypeBBehaviour();
someBehaviour.SomeFunction();

Dengan kelas dalam anonim, Anda bahkan dapat menghindari mendeklarasikan kelas bernama terpisah dan hampir memperlakukannya seperti fungsi delegasi nyata.

// Java
public void SomeMethod(ISomeBehaviour pSomeBehaviour) {
   ...
}

...

SomeMethod(new ISomeBehaviour() { 
   @Override
   public void SomeFunction() {
      // your implementation
   }
});

Ini mungkin hanya boleh digunakan ketika implementasi sangat spesifik untuk konteks saat ini dan tidak akan mendapat manfaat dari digunakan kembali.

Dan tentu saja di Jawa 8, ini pada dasarnya menjadi ekspresi lambda:

// Java 8
SomeMethod(() -> { /* your implementation */ });

6
+1. ini adalah solusi yang layak, dan verbositas dapat dikompensasi oleh rawatan di masa depan.
nawfal

ini hebat ... Saat ini sedang mengerjakan proyek di mana saya tidak dapat menggunakan refleksi karena kendala proyek dan penyelesaian ini menyelesaikan pekerjaan dengan baik :)
Jonathan Camarena

Apakah Java memiliki konvensi penamaan awalan-I untuk antarmuka? Saya belum pernah melihat itu sebelumnya
Kyle Delaney

36

Cerpen: tidak .

pengantar

Versi terbaru dari lingkungan pengembangan Microsoft Visual J ++ mendukung konstruksi bahasa yang disebut delegasi atau referensi metode terikat . Konstruk ini, dan kata kunci baru delegatedan multicastdiperkenalkan untuk mendukungnya, bukan bagian dari bahasa pemrograman Java TM , yang ditentukan oleh Spesifikasi Bahasa Jawa dan diubah oleh Spesifikasi Kelas Batin yang disertakan dalam dokumentasi untuk perangkat lunak JDKTM 1.1 .

Sepertinya bahasa pemrograman Java tidak akan menyertakan konstruk ini. Sun sudah mempertimbangkan untuk mengadopsi itu pada tahun 1996, sejauh membangun dan membuang prototipe kerja. Kesimpulan kami adalah bahwa referensi metode terikat tidak perlu dan merugikan bahasa. Keputusan ini dibuat berdasarkan konsultasi dengan Borland International, yang memiliki pengalaman sebelumnya dengan referensi metode terikat dalam Delphi Object Pascal.

Kami percaya referensi metode terikat tidak diperlukan karena alternatif desain lain, kelas dalam , menyediakan fungsionalitas yang sama atau unggul. Secara khusus, kelas dalam sepenuhnya mendukung persyaratan penanganan acara antarmuka pengguna, dan telah digunakan untuk mengimplementasikan API antarmuka pengguna setidaknya sekomprehensif Windows Foundation Classes.

Kami percaya referensi metode terikat berbahaya karena mengurangi kesederhanaan bahasa pemrograman Java dan karakter yang berorientasi objek secara luas dari API. Referensi metode Bound juga memperkenalkan penyimpangan ke dalam sintaksis bahasa dan aturan pelingkupan. Akhirnya, mereka mencairkan investasi dalam teknologi VM karena VM diharuskan untuk menangani jenis referensi tambahan dan berbeda dan hubungan metode secara efisien.


Seperti yang dikatakan dalam apa yang Patrick hubungkan, Anda ingin menggunakan kelas dalam saja.
SCdF

16
Artikel bagus Saya SUKA definisi mereka "sederhana": Java adalah dirancang untuk menjadi bahasa yang sederhana seperti pada "Jawa harus sederhana untuk menulis kompiler / VM untuk" sementara mengabaikan "Jawa harus mudah ditulis / dibaca oleh seseorang" . Menjelaskan banyak hal.
Juozas Kontvainis

5
Saya hanya berpikir SUN membuat kesalahan besar. Mereka hanya belum diyakinkan oleh paradigma fungsional, dan hanya itu.
Stephane Rolland

7
@Juozas: Python secara eksplisit dibuat agar mudah ditulis / dibaca oleh seseorang dan ia mengimplementasikan fungsi / delegasi lambda .
Stephane Rolland

5
Digantikan dengan tautan archive.org. Juga, itu benar-benar bodoh, Oracle.
Patrick

18

Sudahkah Anda membaca ini :

Delegasi adalah konstruk yang berguna dalam sistem berbasis acara. Delegasi pada dasarnya adalah objek yang menyandikan metode pengiriman pada objek tertentu. Dokumen ini menunjukkan bagaimana kelas dalam java menyediakan solusi yang lebih umum untuk masalah seperti itu.

Apa itu Delegasi? Benar-benar sangat mirip dengan fungsi pointer ke anggota seperti yang digunakan dalam C ++. Tapi seorang delegasi berisi objek target beserta metode yang akan dipanggil. Idealnya akan menyenangkan untuk mengatakan:

obj.registerHandler (ano.methodOne);

..dan metode methodOne akan dipanggil pada ano ketika beberapa peristiwa tertentu diterima.

Inilah yang dicapai oleh struktur Delegasi.

Kelas Dalam Jawa

Telah diperdebatkan bahwa Java menyediakan fungsionalitas ini melalui kelas-kelas dalam anonim dan dengan demikian tidak memerlukan konstruksi Delegasi tambahan.

obj.registerHandler(new Handler() {
        public void handleIt(Event ev) {
            methodOne(ev);
        }
      } );

Pada pandangan pertama ini tampaknya benar tetapi pada saat yang sama merupakan gangguan. Karena untuk banyak contoh pemrosesan acara, kesederhanaan sintaksis Delegasi sangat menarik.

General Handler

Namun, jika pemrograman berbasis peristiwa digunakan dengan cara yang lebih luas, katakanlah, misalnya, sebagai bagian dari lingkungan pemrograman asinkron umum, ada lebih banyak yang dipertaruhkan.

Dalam situasi umum seperti itu, tidak cukup hanya menyertakan metode target dan instance objek target. Secara umum mungkin ada parameter lain yang diperlukan, yang ditentukan dalam konteks ketika event handler terdaftar.

Dalam situasi yang lebih umum ini, pendekatan java dapat memberikan solusi yang sangat elegan, terutama jika dikombinasikan dengan penggunaan variabel final:

void processState(final T1 p1, final T2 dispatch) { 
  final int a1 = someCalculation();

  m_obj.registerHandler(new Handler() {
    public void handleIt(Event ev) {
     dispatch.methodOne(a1, ev, p1);
    }
  } );
}

final * final * final

Mendapat perhatian Anda?

Perhatikan bahwa variabel terakhir dapat diakses dari dalam definisi metode kelas anonim. Pastikan untuk mempelajari kode ini dengan cermat untuk memahami akibatnya. Ini berpotensi merupakan teknik yang sangat kuat. Misalnya, ini dapat digunakan untuk efek yang baik ketika mendaftarkan penangan di MiniDOM dan dalam situasi yang lebih umum.

Sebaliknya, konstruksi Delegasi tidak memberikan solusi untuk persyaratan yang lebih umum ini, dan karenanya harus ditolak sebagai idiom yang menjadi dasar desain.



5

Tidak, tetapi mereka dapat dipalsukan menggunakan proxy dan refleksi:

  public static class TestClass {
      public String knockKnock() {
          return "who's there?";
      }
  }

  private final TestClass testInstance = new TestClass();

  @Test public void
  can_delegate_a_single_method_interface_to_an_instance() throws Exception {
      Delegator<TestClass, Callable<String>> knockKnockDelegator = Delegator.ofMethod("knockKnock")
                                                                   .of(TestClass.class)
                                                                   .to(Callable.class);
      Callable<String> callable = knockKnockDelegator.delegateTo(testInstance);
      assertThat(callable.call(), is("who's there?"));
  }

Hal yang menyenangkan tentang idiom ini adalah Anda dapat memverifikasi bahwa metode yang didelegasikan-ke ada, dan memiliki tanda tangan yang diperlukan, pada titik di mana Anda membuat delegator (walaupun tidak pada waktu kompilasi, sayangnya, meskipun plug-in FindBugs mungkin bantu di sini), lalu gunakan dengan aman untuk mendelegasikan ke berbagai kejadian.

Lihat kode karg di github untuk tes dan implementasi lebih lanjut .


2

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

Bagaimana itu bekerja

Ada kelas prinsip bernama Callback dengan kelas bersarang bernama WithParms. API yang membutuhkan callback akan mengambil objek Callback sebagai parameter dan, jika perlu, membuat Callback. WithParms sebagai variabel metode. 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 bagian dalamnya.

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.

Berikut ini adalah contoh menggunakan panggilan balik untuk memproses file dalam pohon direktori. Ini adalah pass validasi awal yang hanya menghitung file untuk diproses dan memastikan tidak ada yang melebihi ukuran maksimum yang telah ditentukan. Dalam hal ini kami hanya membuat panggilan balik sesuai dengan permintaan API. Namun, kami mencerminkan metode target sebagai nilai statis sehingga refleksi tidak dilakukan setiap waktu.

static private final Method             COUNT =Callback.getMethod(Xxx.class,"callback_count",true,File.class,File.class);

...

IoUtil.processDirectory(root,new Callback(this,COUNT),selector);

...

private void callback_count(File dir, File fil) {
    if(fil!=null) {                                                                             // file is null for processing a directory
        fileTotal++;
        if(fil.length()>fileSizeLimit) {
            throw new Abort("Failed","File size exceeds maximum of "+TextUtil.formatNumber(fileSizeLimit)+" bytes: "+fil);
            }
        }
    progress("Counting",dir,fileTotal);
    }

IoUtil.processDirectory ():

/**
 * Process a directory using callbacks.  To interrupt, the callback must throw an (unchecked) exception.
 * Subdirectories are processed only if the selector is null or selects the directories, and are done
 * after the files in any given directory.  When the callback is invoked for a directory, the file
 * argument is null;
 * <p>
 * The callback signature is:
 * <pre>    void callback(File dir, File ent);</pre>
 * <p>
 * @return          The number of files processed.
 */
static public int processDirectory(File dir, Callback cbk, FileSelector sel) {
    return _processDirectory(dir,new Callback.WithParms(cbk,2),sel);
    }

static private int _processDirectory(File dir, Callback.WithParms cbk, FileSelector sel) {
    int                                 cnt=0;

    if(!dir.isDirectory()) {
        if(sel==null || sel.accept(dir)) { cbk.invoke(dir.getParent(),dir); cnt++; }
        }
    else {
        cbk.invoke(dir,(Object[])null);

        File[] lst=(sel==null ? dir.listFiles() : dir.listFiles(sel));
        if(lst!=null) {
            for(int xa=0; xa<lst.length; xa++) {
                File ent=lst[xa];
                if(!ent.isDirectory()) {
                    cbk.invoke(dir,ent);
                    lst[xa]=null;
                    cnt++;
                    }
                }
            for(int xa=0; xa<lst.length; xa++) {
                File ent=lst[xa];
                if(ent!=null) { cnt+=_processDirectory(ent,cbk,sel); }
                }
            }
        }
    return cnt;
    }

Contoh ini menggambarkan keindahan dari pendekatan ini - logika spesifik aplikasi diabstraksi ke dalam callback, dan kesulitan berjalan secara rekursif pohon direktori terselip dengan baik dalam metode utilitas statis yang sepenuhnya dapat digunakan kembali. Dan kita tidak harus berulang kali membayar harga untuk mendefinisikan dan mengimplementasikan antarmuka untuk setiap penggunaan baru. Tentu saja, argumen untuk antarmuka adalah bahwa itu jauh lebih eksplisit tentang apa yang harus diterapkan (itu ditegakkan, tidak hanya didokumentasikan) - tetapi dalam praktiknya saya belum menemukan itu menjadi masalah untuk mendapatkan definisi panggilan balik dengan benar.

Mendefinisikan dan mengimplementasikan antarmuka tidak terlalu buruk (kecuali Anda mendistribusikan applet, seperti saya, di mana menghindari membuat kelas tambahan sebenarnya penting), tetapi di mana ini benar-benar bersinar adalah ketika Anda memiliki beberapa callback dalam satu kelas. Tidak hanya dipaksa untuk mendorong mereka masing-masing ke kelas dalam yang ditambahkan tambahan overhead dalam aplikasi dikerahkan, tetapi itu benar-benar membosankan untuk program dan semua kode boiler-plate benar-benar hanya "noise".


1

Ya & Tidak, tetapi pola pendelegasian di Jawa dapat dipikirkan dengan cara ini. Video tutorial ini adalah tentang pertukaran data antara aktivitas - fragmen, dan memiliki esensi besar pola penyerahan delegasi menggunakan antarmuka.

Antarmuka Java


1

Itu tidak memiliki delegatekata kunci eksplisit sebagai C #, tetapi Anda dapat mencapai yang serupa di Java 8 dengan menggunakan antarmuka fungsional (yaitu setiap antarmuka dengan tepat satu metode) dan lambda:

private interface SingleFunc {
    void printMe();
}

public static void main(String[] args) {
    SingleFunc sf = () -> {
        System.out.println("Hello, I am a simple single func.");
    };
    SingleFunc sfComplex = () -> {
        System.out.println("Hello, I am a COMPLEX single func.");
    };
    delegate(sf);
    delegate(sfComplex);
}

private static void delegate(SingleFunc f) {
    f.printMe();
}

Setiap objek tipe baru SingleFuncharus diimplementasikan printMe(), sehingga aman untuk meneruskannya ke metode lain (misalnya delegate(SingleFunc)) untuk memanggil printMe()metode tersebut.


0

Meskipun tidak ada yang bersih, tetapi Anda bisa mengimplementasikan sesuatu seperti delegasi C # menggunakan Java Proxy .


2
Meskipun tautan ini dapat menjawab pertanyaan, lebih baik untuk memasukkan bagian-bagian penting dari jawaban di sini dan memberikan tautan untuk referensi. Jawaban hanya tautan dapat menjadi tidak valid jika halaman tertaut berubah.
StackFlowed

2
@StackFlowed Lepaskan tautannya, dan apa yang Anda dapatkan? Pos dengan informasi. Pilih untuk disimpan.
Scimonster

1
@Scimonster apa yang akan terjadi jika tautannya tidak valid lagi?
StackFlowed

@StackFlowed Masih memberikan jawaban - gunakan Java Proxy.
Scimonster

maka itu bisa dibiarkan sebagai komentar?
StackFlowed

0

Tidak, tetapi memiliki perilaku serupa, secara internal.

Dalam C # delegasi digunakan untuk membuat titik entri yang terpisah dan mereka bekerja seperti penunjuk fungsi.

Di java tidak ada hal sebagai penunjuk fungsi (pada tampilan atas) tetapi secara internal Java perlu melakukan hal yang sama untuk mencapai tujuan ini.

Misalnya, membuat utas di Java memerlukan kelas yang memperpanjang Utas atau menerapkan Runnable, karena variabel objek kelas dapat digunakan sebagai penunjuk lokasi memori.



0

Kode yang dijelaskan menawarkan banyak keuntungan dari delegasi C #. Metode, baik statis atau dinamis, dapat diperlakukan secara seragam. Kompleksitas dalam metode panggilan melalui refleksi berkurang dan kode dapat digunakan kembali, dalam arti tidak memerlukan kelas tambahan dalam kode pengguna. Catatan kami sedang memanggil versi kenyamanan alternatif dari invoke, di mana metode dengan satu parameter dapat dipanggil tanpa membuat array objek. Kode Java di bawah ini:

  class Class1 {
        public void show(String s) { System.out.println(s); }
    }

    class Class2 {
        public void display(String s) { System.out.println(s); }
    }

    // allows static method as well
    class Class3 {
        public static void staticDisplay(String s) { System.out.println(s); }
    }

    public class TestDelegate  {
        public static final Class[] OUTPUT_ARGS = { String.class };
        public final Delegator DO_SHOW = new Delegator(OUTPUT_ARGS,Void.TYPE);

        public void main(String[] args)  {
            Delegate[] items = new Delegate[3];

            items[0] = DO_SHOW .build(new Class1(),"show,);
            items[1] = DO_SHOW.build (new Class2(),"display");
            items[2] = DO_SHOW.build(Class3.class, "staticDisplay");

            for(int i = 0; i < items.length; i++) {
                items[i].invoke("Hello World");
            }
        }
    }

-11

Java tidak memiliki delegasi dan bangga karenanya :). Dari apa yang saya baca di sini saya menemukan dua cara intinya untuk delegasi palsu: 1. refleksi; 2. kelas batin

Pantulannya slooooow! Kelas dalam tidak mencakup fungsi use-case: sort yang paling sederhana. Tidak ingin masuk ke detail, tetapi solusi dengan kelas dalam pada dasarnya adalah membuat kelas pembungkus untuk array bilangan bulat untuk diurutkan dalam urutan naik dan kelas untuk array bilangan bulat untuk diurutkan dalam urutan menurun.


Apa hubungan kelas batin dengan pemilahan? Dan apa hubungannya dengan pertanyaan itu?
nawfal
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.