Bagaimana kelas batin Anonim digunakan di Jawa?


Jawaban:


369

Dengan "kelas anonim", saya menganggap Anda maksud kelas batin anonim .

Kelas dalam anonim dapat berguna ketika membuat instance dari objek dengan "ekstra" tertentu seperti metode overriding, tanpa harus benar-benar subkelas kelas.

Saya cenderung menggunakannya sebagai jalan pintas untuk melampirkan pendengar acara:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // do something
    }
});

Menggunakan metode ini membuat pengkodean sedikit lebih cepat, karena saya tidak perlu membuat kelas tambahan yang mengimplementasikan ActionListener - Saya hanya dapat membuat instance kelas batin anonim tanpa benar-benar membuat kelas yang terpisah.

Saya hanya menggunakan teknik ini untuk tugas "cepat dan kotor" di mana membuat seluruh kelas terasa tidak perlu. Memiliki beberapa kelas batin anonim yang melakukan hal yang persis sama harus di refactored ke kelas yang sebenarnya, baik itu kelas batin atau kelas terpisah.


5
Atau Anda bisa refactor duplikat kelas batin anonim menjadi satu metode dengan kelas batin anonim (dan mungkin beberapa kode duplikat lainnya).
Tom Hawtin - tackline

3
Jawaban bagus tapi pertanyaan singkat. Apakah itu berarti Java dapat hidup tanpa kelas batin anonim dan mereka seperti opsi tambahan untuk dipilih?
realPK

5
Dijelaskan dengan sangat baik, walaupun sulit saya sarankan kepada siapa pun yang membaca ini untuk mencari dan melihat apa yang bisa dilakukan ekspresi java 8 dan lambda untuk membuat pengkodean lebih cepat dan lebih mudah dibaca.
Pievis

2
@ user2190639 Tepatnya, tidak bisa meminta yang lebih baik dengan Lambda di Java8
bonCodigo

3
mengapa kamu mengatakan overloading methodsdan tidak overriding methods?
Tarun

73

Kelas dalam anonim secara efektif ditutup, sehingga dapat digunakan untuk meniru ekspresi lambda atau "delegasi". Misalnya, ambil antarmuka ini:

public interface F<A, B> {
   B f(A a);
}

Anda dapat menggunakan ini secara anonim untuk membuat fungsi kelas satu di Java. Katakanlah Anda memiliki metode berikut yang mengembalikan angka pertama lebih besar dari pada daftar yang diberikan, atau jika tidak ada angka yang lebih besar:

public static int larger(final List<Integer> ns, final int i) {
  for (Integer n : ns)
     if (n > i)
        return n;
  return i;
}

Dan kemudian Anda memiliki metode lain yang mengembalikan angka pertama lebih kecil dari saya dalam daftar yang diberikan, atau saya jika tidak ada nomor lebih kecil:

public static int smaller(final List<Integer> ns, final int i) {
   for (Integer n : ns)
      if (n < i)
         return n;
   return i;
}

Metode-metode ini hampir identik. Menggunakan fungsi kelas-pertama tipe F, kita dapat menulis ulang ini menjadi satu metode sebagai berikut:

public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
   for (T t : ts)
      if (f.f(t))
         return t;
   return z;
}

Anda bisa menggunakan kelas anonim untuk menggunakan metode firstMatch:

F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
   Boolean f(final Integer n) {
      return n > 10;
   }
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);

Ini adalah contoh yang benar-benar dibuat-buat, tetapi mudah untuk melihat bahwa dapat melewati fungsi seolah-olah mereka nilai adalah fitur yang sangat berguna. Lihat "Bisakah Bahasa Pemrograman Anda Melakukan Ini" oleh Joel sendiri.

Perpustakaan yang bagus untuk pemrograman Java dengan gaya ini: Java Fungsional.


20
Sayangnya, verbositas melakukan pemrograman fungsional di java, IMHO, lebih besar daripada keuntungannya - salah satu poin mencolok dari pemrograman fungsional adalah cenderung mengurangi ukuran kode, dan membuat segala sesuatu lebih mudah untuk dibaca dan dimodifikasi. Tapi java fungsional sepertinya tidak melakukan itu sama sekali.
Chii

27
Semua kemampuan pemrograman fungsional, dengan kesederhanaan Java!
Adam Jaskiewicz

3
Dalam pengalaman saya, gaya fungsional di Jawa dibayar dengan verbositas di muka, tetapi menghasilkan singkat dalam jangka panjang. Sebagai contoh, myList.map (f) jauh lebih sedikit verbose daripada for-loop yang sesuai.
Apocalisp

2
Scala , bahasa gaya pemrograman fungsional, konon berjalan dengan baik di dalam JVM dan mungkin menjadi pilihan untuk skenario pemrograman fungsional.
Darrell Teague

51

Kelas dalam anonim digunakan dalam skenario berikut:

1.) Untuk Overriding (Sub classing), Ketika definisi kelas tidak dapat digunakan kecuali kasus saat ini:

class A{
   public void methodA() {
      System.out.println("methodA");
    }
}
class B{
    A a = new A() {
     public void methodA() {
        System.out.println("anonymous methodA");
     }
   };
}

2.) Untuk mengimplementasikan antarmuka, Ketika implementasi antarmuka hanya diperlukan untuk kasus saat ini:

interface interfaceA{
   public void methodA();
}
class B{
   interfaceA a = new interfaceA() {
     public void methodA() {
        System.out.println("anonymous methodA implementer");
     }
   };
}

3.) Argument Defined Anonymous inner class:

 interface Foo {
   void methodFoo();
 }
 class B{
  void do(Foo f) { }
}

class A{
   void methodA() {
     B b = new B();
     b.do(new Foo() {
       public void methodFoo() {
         System.out.println("methodFoo");
       } 
     });
   } 
 } 

8
Jawaban yang bagus, sepertinya 3.) adalah pola yang digunakan untuk pendengar acara
xdl

47

Saya menggunakannya kadang-kadang sebagai hack sintaks untuk Instantiation Map:

Map map = new HashMap() {{
   put("key", "value");
}};

vs.

Map map = new HashMap();
map.put("key", "value");

Menghemat redundansi saat melakukan banyak pernyataan put. Namun, saya juga mengalami masalah dalam melakukan ini ketika kelas luar perlu diserialisasi melalui remoting.


56
Agar jelas, set kawat gigi pertama adalah kelas dalam anonim (subclassing HashMap). Set kawat gigi kedua adalah inisialisasi instance (bukan yang statis) yang kemudian menetapkan nilai pada subkelas HashMap Anda. +1 untuk menyebutkannya, -1 untuk tidak mengeja untuk noobs. ;-D
Spencer Kormos

4
Baca lebih lanjut tentang sintaks penjepit ganda di sini .
Martin Andersson


18

Mereka biasanya digunakan sebagai bentuk panggilan balik verbal.

Saya kira Anda bisa mengatakan mereka lebih diuntungkan daripada tidak memilikinya, dan harus membuat kelas bernama setiap saat, tetapi konsep serupa diimplementasikan jauh lebih baik dalam bahasa lain (sebagai penutup atau blok)

Inilah contoh ayunan

myButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        // do stuff here...
    }
});

Meskipun masih berantakan, itu jauh lebih baik daripada memaksa Anda untuk mendefinisikan kelas bernama untuk setiap pendengar membuang seperti ini (meskipun tergantung pada situasi dan penggunaan kembali, itu mungkin masih merupakan pendekatan yang lebih baik)


1
Apakah Anda bermaksud mengatakan singkat? Jika itu akan bertele-tele, panggilan balik akan tetap terpisah, membuatnya sedikit lebih besar, dan dengan demikian membuatnya bertele-tele. Jika Anda mengatakan bahwa ini masih bertele-tele, apa bentuk singkatnya?
user3081519

1
@ user3081519, sesuatu seperti myButton.addActionListener(e -> { /* do stuff here */})atau myButton.addActionListener(stuff)akan terser.
Samuel Edwin Ward

8

Anda menggunakannya dalam situasi di mana Anda perlu membuat kelas untuk tujuan tertentu di dalam fungsi lain, misalnya, sebagai pendengar, sebagai runnable (untuk menelurkan utas), dll.

Idenya adalah bahwa Anda memanggil mereka dari dalam kode fungsi sehingga Anda tidak pernah merujuk mereka di tempat lain, sehingga Anda tidak perlu menyebutkannya. Kompilator hanya menyebutkannya.

Mereka pada dasarnya gula sintaksis, dan umumnya harus dipindahkan ke tempat lain ketika mereka tumbuh lebih besar.

Saya tidak yakin apakah itu salah satu kelebihan Java, meskipun jika Anda menggunakannya (dan kita semua sering menggunakannya, sayangnya), maka Anda bisa berpendapat bahwa itu adalah salah satunya.


6

GuideLines untuk Kelas Anonim.

  1. Kelas anonim dideklarasikan dan diinisialisasi secara bersamaan.

  2. Kelas anonim harus diperluas atau diterapkan ke satu dan hanya satu kelas atau antarmuka saja.

  3. Karena kelas anonimouse tidak memiliki nama, kelas ini hanya dapat digunakan sekali.

misalnya:

button.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub

    }
});

Mengenai # 3: Tidak sepenuhnya benar. Anda dapat memperoleh beberapa instance kelas anonim dengan refleksi, misalnya ref.getClass().newInstance().
icza

Aturan tidak menjawab pertanyaan.
Marquis of Lorne

5

Ya, kelas batin anonim jelas merupakan salah satu kelebihan Jawa.

Dengan kelas batin anonim Anda memiliki akses ke variabel akhir dan anggota dari kelas sekitarnya, dan itu sangat berguna di pendengar dll.

Tetapi keuntungan utama adalah bahwa kode kelas dalam, yang (setidaknya harus) digabungkan erat dengan kelas / metode / blok di sekitarnya, memiliki konteks spesifik (kelas sekitarnya, metode, dan blok).


1
Memiliki akses ke kelas sekitarnya sangat penting! Saya pikir ini adalah alasan dalam banyak kasus di mana kelas anonim digunakan, karena membutuhkan / menggunakan atribut non-publik, metode dan variabel lokal dari kelas / metode sekitarnya yang lebih jauh (jika kelas yang terpisah akan digunakan) harus diteruskan atau dipublikasikan.
icza

5
new Thread() {
        public void run() {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                System.out.println("Exception message: " + e.getMessage());
                System.out.println("Exception cause: " + e.getCause());
            }
        }
    }.start();

Ini juga salah satu contoh untuk tipe dalam anonim menggunakan utas


3

saya menggunakan objek anonim untuk memanggil Thread baru ..

new Thread(new Runnable() {
    public void run() {
        // you code
    }
}).start();

3

Sebuah kelas batin dikaitkan dengan sebuah instance dari kelas luar dan ada dua jenis khusus: kelas lokal dan kelas Anonymous . Kelas anonim memungkinkan kita untuk mendeklarasikan dan membuat instance kelas pada saat yang sama, sehingga membuat kode ringkas. Kami menggunakannya ketika kami membutuhkan kelas lokal hanya sekali karena mereka tidak memiliki nama.

Pertimbangkan contoh dari doc di mana kita memiliki Personkelas:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }

    public void printPerson() {
        // ...
    }
}

dan kami memiliki metode untuk mencetak anggota yang cocok dengan kriteria pencarian sebagai:

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

di mana CheckPersonantarmuka seperti:

interface CheckPerson {
    boolean test(Person p);
}

Sekarang kita dapat menggunakan kelas anonim yang mengimplementasikan antarmuka ini untuk menentukan kriteria pencarian sebagai:

printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

Di sini antarmuka sangat sederhana dan sintaksis kelas anonim tampaknya sulit dan tidak jelas.

Java 8 telah memperkenalkan istilah Fungsional Antarmuka yang merupakan antarmuka dengan hanya satu metode abstrak, maka kita dapat mengatakan CheckPersonadalah antarmuka fungsional. Kita dapat menggunakan Ekspresi Lambda yang memungkinkan kita untuk melewatkan fungsi sebagai argumen metode sebagai:

printPersons(
                roster,
                (Person p) -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25
        );

Kita dapat menggunakan antarmuka fungsional standar Predicatesebagai pengganti antarmuka CheckPerson, yang selanjutnya akan mengurangi jumlah kode yang diperlukan.


2

Kelas dalam anonim dapat bermanfaat sambil memberikan implementasi yang berbeda untuk objek yang berbeda. Tetapi harus digunakan sangat hemat karena menciptakan masalah untuk keterbacaan program.


1

Salah satu penggunaan utama dari kelas anonim dalam finalisasi kelas yang disebut finalizer wali . Di dunia Jawa menggunakan metode penyelesaian harus dihindari sampai Anda benar-benar membutuhkannya. Anda harus ingat, ketika Anda mengganti metode finalisasi untuk sub-kelas, Anda harus selalu memohon super.finalize()juga, karena metode finalisasi kelas super tidak akan dipanggil secara otomatis dan Anda dapat mengalami masalah dengan kebocoran memori.

jadi mengingat fakta yang disebutkan di atas, Anda bisa menggunakan kelas anonim seperti:

public class HeavyClass{
    private final Object finalizerGuardian = new Object() {
        @Override
        protected void finalize() throws Throwable{
            //Finalize outer HeavyClass object
        }
    };
}

Dengan menggunakan teknik ini, Anda merasa lega dengan diri Anda dan pengembang lain untuk memanggil super.finalize()setiap sub-kelas HeavyClassyang membutuhkan metode penyelesaian.


1

Anda dapat menggunakan kelas anonim dengan cara ini

TreeSet treeSetObj = new TreeSet(new Comparator()
{
    public int compare(String i1,String i2)
    {
        return i2.compareTo(i1);
    }
});

1

Sepertinya tidak ada yang disebutkan di sini tetapi Anda juga dapat menggunakan kelas anonim untuk mengadakan argumen tipe umum (yang biasanya hilang karena penghapusan tipe) :

public abstract class TypeHolder<T> {
    private final Type type;

    public TypeReference() {
        // you may do do additional sanity checks here
        final Type superClass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public final Type getType() {
        return this.type;
    }
}

Jika Anda akan membuat instance kelas ini dengan cara anonim

TypeHolder<List<String>, Map<Ineger, Long>> holder = 
    new TypeHolder<List<String>, Map<Ineger, Long>>() {};

lalu seperti itu holder instance akan mengandung definisi tipe pass yang tidak dihapus.

Pemakaian

Ini sangat berguna untuk membangun validator / deserializator. Anda juga dapat instantiate tipe generik dengan refleksi (jadi jika Anda ingin melakukan new T()dalam tipe parametrized - Anda dipersilakan!) .

Kekurangan / Keterbatasan

  1. Anda harus memberikan parameter generik secara eksplisit. Gagal melakukannya akan menyebabkan hilangnya parameter
  2. Setiap instantiasi akan dikenakan biaya kelas tambahan yang akan dihasilkan oleh kompiler yang mengarah ke polusi classpath / gembung kembung

1

Cara terbaik untuk mengoptimalkan kode. juga, Kita bisa menggunakan metode kelas atau antarmuka utama.

import java.util.Scanner;
abstract class AnonymousInner {
    abstract void sum();
}

class AnonymousInnerMain {
    public static void main(String []k){
        Scanner sn = new Scanner(System.in);
        System.out.println("Enter two vlaues");
            int a= Integer.parseInt(sn.nextLine());
            int b= Integer.parseInt(sn.nextLine()); 
        AnonymousInner ac = new AnonymousInner(){
            void sum(){
                int c= a+b;
                System.out.println("Sum of two number is: "+c);
            }
        };
        ac.sum();
    }

}

1

Sebuah Anonymous batin Kelas digunakan untuk membuat sebuah objek yang tidak akan pernah dirujuk lagi. Tidak memiliki nama dan dideklarasikan dan dibuat dalam pernyataan yang sama. Ini digunakan di mana Anda biasanya menggunakan variabel objek. Anda mengganti variabel dengan newkata kunci, panggilan ke konstruktor dan definisi kelas di dalam {dan }.

Saat menulis Program Berurutan di Jawa, biasanya akan terlihat seperti ini

ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();

Yang ThreadClassdigunakan di sini akan ditentukan oleh pengguna. Kelas ini akan mengimplementasikan Runnableantarmuka yang diperlukan untuk membuat utas. Dalam ThreadClasssatu run()metode (hanya metode dalam Runnable) perlu dilaksanakan juga. Jelas bahwa menyingkirkanThreadClass akan lebih efisien dan itulah mengapa Kelas Batin Anonim ada.

Lihatlah kode berikut

Thread runner = new Thread(new Runnable() {
    public void run() {
        //Thread does it's work here
    }
});
runner.start();

Kode ini menggantikan referensi yang dibuat taskpada contoh paling atas. Alih-alih memiliki kelas terpisah, Kelas Batin Anonim di dalam Thread()konstruktor mengembalikan objek tanpa nama yang mengimplementasikan Runnableantarmuka dan mengganti run()metode. Metode ini run()akan mencakup pernyataan di dalam yang melakukan pekerjaan yang diperlukan oleh utas.

Menjawab pertanyaan apakah Anonymous Inner Classes adalah salah satu kelebihan Java, saya harus mengatakan bahwa saya tidak begitu yakin karena saya tidak akrab dengan banyak bahasa pemrograman saat ini. Tapi yang bisa saya katakan adalah metode pengkodean yang lebih cepat dan mudah.

Referensi: Sams Teach Yourself Java dalam Edisi 21 Hari Ketujuh


0

Satu lagi keuntungan:
Seperti yang Anda tahu bahwa Java tidak mendukung multiple inheritance, jadi jika Anda menggunakan "Thread" agak kelas sebagai kelas anonim maka kelas masih memiliki satu ruang tersisa untuk kelas lain untuk diperluas.

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.