Anonim iç sınıflar Java'da nasıl kullanılır?


309

Java'da anonim sınıfların kullanımı nedir? Anonim sınıf kullanımının Java'nın avantajlarından biri olduğunu söyleyebilir miyiz?


67
Java'daki kapanışların olmaması için bir yol olduğu için Java'nın bir avantajı değil.
Eric Wilson

4
Java 8 Lambda ifadelerini de tanıttı. Cevabı kontrol et: stackoverflow.com/questions/355167/…
akhil_mittal

Yanıtlar:


369

"Anonim bir sınıf" derken, anonim iç sınıf demek istiyorsun .

Anonim bir iç sınıf, bir sınıfı gerçekten alt sınıfa ayırmak zorunda kalmadan, geçersiz kılma yöntemleri gibi belirli "ekstralar" içeren bir nesnenin örneğini oluştururken yararlı olabilir.

Bir olay dinleyicisi eklemek için bir kısayol olarak kullanma eğilimindeyim:

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

Bu yöntemi kullanmak, kodlamayı biraz daha hızlı hale getirir, çünkü uygulayan ekstra bir sınıf yapmam gerekmiyor ActionListener- sadece ayrı bir sınıf yapmadan anonim bir iç sınıf başlatabilirim.

Bu tekniği sadece bir sınıfın tamamının gereksiz hissettirdiği "hızlı ve kirli" görevler için kullanıyorum. Aynı şeyi yapan birden fazla anonim iç sınıfa sahip olmak, ister bir iç sınıfa, ister ayrı bir sınıfa, gerçek bir sınıfa yeniden konulmalıdır.


5
Ya da anonim iç sınıfları (ve muhtemelen başka bir çoğaltılmış kodu) kullanarak, anonim iç sınıfları tek bir yönteme yeniden aktarabilirsiniz.
Tom Hawtin - tackline

3
Harika bir cevap ama hızlı bir soru. Java, anonim iç sınıflar olmadan yaşayabileceği ve seçim yapmak için ekstra bir seçenek gibi olduğu anlamına mı geliyor?
realPK

5
Çok iyi açıkladı, zor bile ben herkes okuma ve java 8 ve lambda ifadelerin kodlamayı daha hızlı ve daha okunabilir hale getirmek için neler yapabileceğini görmek için bunu tavsiye ederim.
Ocak'ta Pievis

2
@ user2190639 Java8'de Lambda
bonCodigo

3
neden söyledin overloading methods, değil overriding methodsmi?
Tarun

73

Anonim iç sınıflar etkili bir şekilde kapanır, bu nedenle lambda ifadelerini veya "delegeleri" taklit etmek için kullanılabilirler. Örneğin, şu arayüzü alın:

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

Java'da birinci sınıf bir işlev oluşturmak için bunu anonim olarak kullanabilirsiniz . Diyelim ki, verilen listede i'den büyük olan ilk sayıyı döndüren aşağıdaki yöntem var, ya da daha büyük bir sayı yoksa i:

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

Ve sonra verilen listede i'den daha küçük olan ilk sayıyı döndüren başka bir yönteminiz var ya da hiçbir sayı daha küçük değilse i:

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

Bu yöntemler hemen hemen aynıdır. Birinci sınıf işlev türü F'yi kullanarak, bunları aşağıdaki gibi bir yöntemle yeniden yazabiliriz:

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;
}

FirstMatch yöntemini kullanmak için anonim bir sınıf kullanabilirsiniz:

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

Bu gerçekten anlaşılır bir örnektir, ancak fonksiyonları sanki değerlermiş gibi aktarabilmenin oldukça kullanışlı bir özellik olduğunu görmek kolaydır. Joel'in "Programlama Diliniz Bunu Yapabilir" başlığına bakın .

Java'yı bu tarzda programlamak için güzel bir kütüphane: Fonksiyonel Java.


20
maalesef, java, IMHO'da fonksiyonel programlama yapmanın ayrıntıları, kazanımlarından daha ağır basmaktadır - fonksiyonel programlamanın dikkat çekici noktalarından biri, kod boyutunu küçültme eğilimi göstermesi ve işleri okumayı ve değiştirmeyi kolaylaştırmasıdır. Ama fonksiyonel java bunu hiç yapmıyor gibi görünüyor.
Chii

27
Java'nın kıvraklığı ile fonksiyonel programlamanın tüm anlaşılırlığı!
Adam Jaskiewicz

3
Benim tecrübelerime göre, Java'daki fonksiyonel stil, ön planda verbosity ile ödenir, ancak uzun vadede kısalık verir. Örneğin, myList.map (f), karşılık gelen for döngüsünden çok daha az ayrıntılıdır.
Apocalisp

2
Fonksiyonel bir programlama tarzı dili olan Scala , JVM'nin içinde iyi çalışır ve fonksiyonel programlama senaryoları için bir seçenek olabilir.
Darrell Teague

51

Anonim iç sınıf aşağıdaki senaryoda kullanılır:

1.) Geçersiz kılma için (Alt sınıflandırma), Mevcut durum dışında sınıf tanımı kullanılamadığında:

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

2.) Bir arayüz uygulamak için, sadece mevcut durum için arayüzün uygulanması gerektiğinde:

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

3.) Argüman Tanımlı Anonim iç sınıf:

 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
Harika yanıt, 3'e benziyor.) Olay dinleyicileri için kullanılan
kalıptır

47

Bunları bazen Harita örneklemesi için sözdizimi kesmek olarak kullanıyorum:

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

vs

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

Çok sayıda put ifadesi yaparken fazladan tasarruf sağlar. Ancak, dış sınıf uzaklaştırma yoluyla serileştirilmesi gerektiğinde de bunu yaparken sorunlarla karşılaştım.


56
Açık olmak gerekirse, ilk parantez kümesi anonim iç sınıftır (HashMap alt sınıfı). İkinci küme ayracı kümesi, daha sonra HashMap alt sınıfınızdaki değerleri ayarlayan bir örnek başlatıcıdır (statik olandan ziyade). Bahsettiğiniz için +1, noobs için yazmamak için -1. ;-D
Spencer Kormos

4
Çift ayraçlı sözdizimi hakkında daha fazla bilgiyi buradan edinebilirsiniz .
Martin Andersson


18

Genellikle geri arama için ayrıntılı bir form olarak kullanılırlar.

Sanırım onlara sahip olmamak ve her seferinde adlandırılmış bir sınıf oluşturmak zorunda kalmakla karşılaştırıldığında bir avantaj olduklarını söyleyebilirsiniz, ancak benzer kavramlar diğer dillerde (kapaklar veya bloklar olarak) çok daha iyi uygulanır

İşte bir salıncak örneği

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

Hala dağınık bir şekilde ayrıntılı olmasına rağmen, bu gibi her atma dinleyicisi için adlandırılmış bir sınıf tanımlamaya zorlamaktan çok daha iyidir (duruma ve yeniden kullanıma bağlı olarak, yine de daha iyi bir yaklaşım olabilir)


1
Bunu mu demek istediniz: terse Ayrıntılı olsaydı, geri arama ayrı kalır, biraz daha büyük olur ve böylece ayrıntılı olur. Bunun hala ayrıntılı olduğunu söylerseniz, o zaman kısa bir form ne olurdu?
user3081519

1
@ user3081519, benzeri bir şey olabilir myButton.addActionListener(e -> { /* do stuff here */})veya myButton.addActionListener(stuff)terser olurdu.
Samuel Edwin Ward

8

Dinleyici olarak, çalıştırılabilir (bir iş parçacığı oluşturmak için) vb. Gibi başka bir işlevin içinde belirli bir amaç için bir sınıf oluşturmanız gereken durumlarda kullanırsınız.

Fikir, onları bir işlevin kodunun içinden çağırmanızdır, böylece onlara başka bir yerde asla başvurmazsınız, bu yüzden onları adlandırmanıza gerek yoktur. Derleyici sadece numaralandırır.

Esasen sözdizimsel şekerdirler ve genellikle büyüdükçe başka yerlere taşınmalıdırlar.

Java'nın avantajlarından biri olup olmadığından emin değilim, ancak bunları kullanırsanız (ve ne yazık ki hepsini sık sık kullanırız), o zaman bunların biri olduğunu iddia edebilirsiniz.


6

Anonim Sınıf için GuideLines.

  1. Anonim sınıf aynı anda bildirilir ve başlatılır.

  2. Anonim sınıf bir ve yalnızca bir sınıfı veya arabirimi genişletmeli veya uygulamalıdır.

  3. Anonymouse sınıfının adı olmadığından, yalnızca bir kez kullanılabilir.

Örneğin:

button.addActionListener(new ActionListener(){

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

    }
});

# 3 ile ilgili: Tamamen doğru değil. Anonim bir sınıfın birden fazla örneğini yansımayla, örn ref.getClass().newInstance().
icza

Kurallar soruyu cevaplamıyor.
Lorne Marquis

5

Evet, anonim iç sınıflar kesinlikle Java'nın avantajlarından biridir.

Anonim bir iç sınıfla, çevredeki sınıfın nihai ve üye değişkenlerine erişebilirsiniz ve bu dinleyiciler vb.

Ancak en büyük avantajı, çevreleyen sınıfa / yönteme / bloğa sıkıca bağlı olan (en azından) iç sınıf kodunun belirli bir bağlama (çevre sınıfı, yöntem ve blok) sahip olmasıdır.


1
Çevredeki sınıfa erişim çok önemlidir! Anonim sınıfın kullanılmasının birçok durumda nedeni olduğunu düşünüyorum, çünkü çevreleyen sınıfın / yöntemin halka açık olmayan özniteliklerini, yöntemlerini ve yerel değişkenlerini ihtiyaç duyduğu / kullandığı için (ayrı bir sınıf kullanılacaksa) geçilmesi veya yayınlanması.
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();

Bu aynı zamanda iş parçacığı kullanan anonim iç tip için bir örnektir


3

yeni konuları çağırmak için anonim nesneler kullanıyorum.

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

3

Bir iç sınıf dış sınıfın bir örneği ile bağlantılıdır ve iki özel türü vardır: Yerel sınıf ve anonim sınıfı . Anonim bir sınıf, aynı zamanda bir sınıfı ilan etmemizi ve başlatmamızı sağlar, böylece kodu özlü hale getirir. Bunları, yerel bir sınıfa ihtiyaç duymadığımız zaman, isimleri olmadığı için kullanırız.

Bir sınıfımız olan doc örneğini düşünün Person:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

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

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

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

ve arama ölçütleriyle eşleşen üyeleri şu şekilde yazdırma yöntemimiz vardır:

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

CheckPersonaşağıdaki gibi bir arayüz nerede :

interface CheckPerson {
    boolean test(Person p);
}

Artık arama ölçütlerini şu şekilde belirtmek için bu arabirimi uygulayan anonim sınıfı kullanabiliriz:

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

Burada arayüz çok basit ve anonim sınıfın sözdizimi hantal ve belirsiz görünüyor.

Java 8 , sadece bir soyut yönteme sahip bir arayüz olan bir Fonksiyonel Arayüz terimini tanıttı , bu nedenle CheckPersonfonksiyonel bir arayüz olduğunu söyleyebiliriz . İşlevi yöntem argümanı olarak geçirmemizi sağlayan Lambda İfadesini şu şekilde kullanabiliriz :

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

Gerekli kod miktarını daha da azaltan, Predicatearayüz yerine standart bir fonksiyonel arayüz kullanabiliriz CheckPerson.


2

Farklı nesneler için farklı uygulamalar yapılırken anonim iç sınıf faydalı olabilir. Ancak program okunabilirliği için sorun yarattığı için çok az kullanılmalıdır.


1

Sınıflandırmada anonim sınıfların finalizer vasi olarak adlandırılan en büyük kullanımlarından biri . Java dünyasında, gerçekten ihtiyacınız olana kadar sonlandırma yöntemlerini kullanmaktan kaçınılmalıdır. Unutmamalısınız, alt sınıflar için sonlandırma yöntemini geçersiz kıldığınızda, her zaman da çağırmanız gerekir super.finalize(), çünkü süper sınıfın sonlandırma yöntemi otomatik olarak çağrılmaz ve bellek sızıntılarında sorun yaşayabilirsiniz.

yani yukarıda belirtilen gerçeği göz önünde bulundurarak, sadece anonim sınıfları kullanabilirsiniz:

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

Bu tekniği kullanarak kendinizi ve diğer geliştiricilerinizi, sonlandırma yöntemine ihtiyaç duyan super.finalize()her bir alt sınıfa çağırmak için rahatladınız HeavyClass.


1

Anonim sınıfı bu şekilde kullanabilirsiniz

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

1

Burada hiç kimsenin bahsetmediği gibi, genel tür argümanını (tip silme nedeniyle normal olarak kaybedilen) tutmak için anonim sınıfı da kullanabilirsiniz :

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;
    }
}

Bu sınıfı anonim olarak başlatırsanız

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

bu tür bir holderörnek, geçirilmeyen türün silinmemiş tanımını içerecektir.

kullanım

Bu, doğrulayıcılar / serileştiriciler oluşturmak için çok kullanışlıdır. Ayrıca yansıma ile genel bir tür başlatabilirsiniz (bu yüzden hiç new T()parametreleştirilmiş tipte yapmak istediyseniz - hoş geldiniz!) .

Dezavantajları / Sınırlamalar

  1. Genel parametreyi açıkça geçmelisiniz. Bunu yapmamak tip parametre kaybına yol açacaktır
  2. Her örnekleme, derleyici tarafından oluşturulacak ek sınıf maliyetine yol açar ve bu da sınıf yolu kirliliği / kavanoz şişkinliğine yol açar

1

Kodu optimize etmenin en iyi yolu. Ayrıca, bir sınıfın veya arabirimin geçersiz kılma yöntemi için kullanabiliriz.

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

Bir Anonim İç Sınıfı tekrar başvurulan asla bir nesne oluşturmak için kullanılır. Adı yoktur ve aynı ifadede bildirilmiş ve yaratılmıştır. Bu, normalde bir nesnenin değişkenini kullandığınız yerde kullanılır. Sen ile değişkenin yerini newanahtar kelime, bir yapıcı ve sınıf tanımı içine bir çağrı {ve }.

Java'da bir Dişli Program yazarken, genellikle şöyle görünür

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

ThreadClassBurada kullanılan kullanıcı tanımlı olacaktır. Bu sınıf, Runnableiş parçacıkları oluşturmak için gereken arabirimi uygular . Olarak yöntem (sadece yöntemde ) ve uygulanması gerekmektedir. Kurtulmanın daha verimli olacağı açıktır ve bu yüzden Anonim İç Sınıflar mevcuttur.ThreadClassrun()RunnableThreadClass

Aşağıdaki koda bakın

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

Bu kod task, en üstteki örnekte yapılan başvurunun yerine geçer . Thread()Yapıcı içindeki Anonymous Inner Class, ayrı bir sınıfa sahip olmak yerine, Runnablearabirimi uygulayan ve run()yöntemi geçersiz kılan adsız bir nesne döndürür . Yöntem run(), iş parçacığının gerektirdiği işi yapan ifadeleri içerir.

Anonim İç Sınıfların Java'nın avantajlarından biri olup olmadığı sorusunu yanıtlarken, şu anda birçok programlama diline aşina olmadığımdan emin olmadığımı söylemek zorundayım. Ama söyleyebileceğim kesinlikle daha hızlı ve daha kolay bir kodlama yöntemi.

Kaynaklar: Sams 21 Gün Yedinci Baskıda Kendine Java Öğretiyor


0

Bir avantaj daha:
Java birden fazla miras desteklemez biliyorsunuz, bu nedenle anonim sınıf olarak "Thread" tür sınıf kullanırsanız, sınıf hala başka bir sınıfın genişletmek için bir boşluk kaldı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.