簡體   English   中英

Java中如何使用匿名內部類?

[英]How are Anonymous inner classes used in Java?

Java中匿名類有什么用? 我們可以說匿名類的使用是Java的優點之一嗎?

通過“匿名類”,我認為您的意思是匿名內部類

匿名內部類在使用某些“額外”(例如覆蓋方法)創建對象的實例時非常有用,而無需實際對類進行子類化。

我傾向於將其用作附加事件偵聽器的快捷方式:

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

使用這種方法可以使編碼更快一點,因為我不需要創建一個額外的類來實現ActionListener —— 我可以只實例化一個匿名內部類,而無需實際創建一個單獨的類。

我只將這種技術用於“快速而骯臟”的任務,在這種情況下,讓整個班級感覺沒有必要。 有多個匿名內部類做完全相同的事情應該重構為一個實際的類,無論是內部類還是單獨的類。

匿名內部類實際上是閉包,因此它們可用於模擬 lambda 表達式或“委托”。 以這個接口為例:

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

您可以匿名使用它在 Java 中創建一流的函數 假設您有以下方法返回給定列表中大於 i 的第一個數字,如果沒有數字更大,則返回 i :

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

然后你有另一種方法返回給定列表中小於 i 的第一個數字,如果沒有數字更小,則返回 i :

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

這些方法幾乎相同。 使用一等函數類型 F,我們可以將這些重寫為一個方法,如下所示:

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 方法:

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

這是一個非常人為的例子,但很容易看出,能夠將函數當作值來傳遞是一個非常有用的特性。 請參閱喬爾本人的“你的編程語言能做到這一點嗎”

以這種風格編寫 Java 的一個不錯的庫: Functional Java。

匿名內部類用於以下場景:

1.) 對於 Overriding(subclassing),當類定義除當前情況外不可用時:

class A{
    public void methodA() {
        System.out.println("methodA");
    }
}

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

2.) 為了實現一個接口,當只需要在當前情況下實現接口時:

interface InterfaceA{
    public void methodA();
}

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

3.) 參數定義的匿名內部類:

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

我有時將它們用作 Map 實例化的語法技巧:

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

對比

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

它在執行大量 put 語句時節省了一些冗余。 但是,當外部類需要通過遠程處理進行序列化時,我也遇到了問題。

它們通常用作回調的詳細形式。

我想你可以說它們與沒有它們相比是一個優勢,並且每次都必須創建一個命名類,但類似的概念在其他語言中實現得更好(作為閉包或塊)

這是一個揮桿的例子

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

雖然它仍然很冗長,但比強迫你為每個丟棄的監聽器定義一個命名類要好得多(盡管取決於情況和重用,這可能仍然是更好的方法)

您可以在需要在另一個函數內為特定目的創建類的情況下使用它,例如,作為偵聽器、作為可運行對象(生成線程)等。

這個想法是你從函數的代碼內部調用它們,所以你永遠不會在其他地方引用它們,所以你不需要命名它們。 編譯器只是枚舉它們。

它們本質上是語法糖,隨着它們變大,通常應該移到其他地方。

我不確定這是否是 Java 的優勢之一,但如果您確實使用它們(不幸的是,我們都經常使用它們),那么您可能會爭辯說它們是其中之一。

匿名類指南。

  1. 匿名類同時聲明和初始化。

  2. 匿名類必須擴展或實現為一個且僅一個類或接口。

  3. 由於匿名類沒有名稱,因此只能使用一次。

例如:

button.addActionListener(new ActionListener(){

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

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

這也是使用線程的匿名內部類型的示例之一

是的,匿名內部類絕對是 Java 的優勢之一。

使用匿名內部類,您可以訪問周圍類的最終變量和成員變量,這在偵聽器等中派上用場。

但是一個主要的優點是內部類代碼(至少應該)與周圍的類/方法/塊緊密耦合,具有特定的上下文(周圍的類、方法和塊)。

內部類與外部類的實例相關聯,有兩種特殊類型:本地類和匿名類 匿名類使我們能夠同時聲明和實例化一個類,從而使代碼簡潔。 當我們只需要一個本地類時,我們會使用它們,因為它們沒有名字。

考慮來自doc的示例,其中我們有一個Person類:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

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

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

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

我們有一種方法可以將符合搜索條件的成員打印為:

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

其中CheckPerson是一個接口,如:

interface CheckPerson {
    boolean test(Person p);
}

現在我們可以利用實現此接口的匿名類將搜索條件指定為:

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

這里的接口非常簡單,匿名類的語法顯得笨拙和不清楚。

Java 8引入了一個術語Functional Interface ,它是一個只有一個抽象方法的接口,因此我們可以說CheckPerson是一個函數接口。 我們可以使用Lambda 表達式,它允許我們將函數作為方法參數傳遞為:

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

我們可以使用標准的函數接口Predicate代替接口CheckPerson ,這將進一步減少所需的代碼量。

我使用匿名對象來調用新線程..

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

在為不同的對象提供不同的實現時,匿名內部類可能是有益的。 但是應該非常謹慎地使用,因為它會給程序可讀性帶來問題。

匿名類在類終結中的主要用途之一,稱為終結器監護人 在 Java 世界中,應該避免使用 finalize 方法,直到您真正需要它們為止。 你必須記住,當你覆蓋子類的 finalize 方法時,你應該總是調用super.finalize() ,因為超類的 finalize 方法不會自動調用,你可能會遇到內存泄漏的問題。

因此,考慮到上述事實,您可以使用匿名類,例如:

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

使用這種技術,您可以減輕自己和其他開發人員對需要​​ finalize 方法的HeavyClass每個子類調用super.finalize()HeavyClass

你可以這樣使用匿名類

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

這里似乎沒有人提到,但您也可以使用匿名類來保存泛型類型參數(通常由於類型擦除而丟失)

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

如果你以匿名方式實例化這個類

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

那么這樣的holder實例將包含傳遞類型的非擦除定義。

用法

這對於構建驗證器/反序列化器非常方便。 您還可以使用反射實例化泛型類型(因此,如果您想在參數化類型中執行new T() - 歡迎使用!)

缺點/限制

  1. 您應該顯式傳遞泛型參數。 不這樣做會導致類型參數丟失
  2. 每個實例化都會花費你額外的由編譯器生成的類,這會導致類路徑污染/jar 膨脹

匿名內部類用於創建一個永遠不會被再次引用的對象。 它沒有名稱,在同一個語句中聲明和創建。 這用於通常使用對象變量的地方。 您用new關鍵字、對構造函數的調用以及{}的類定義替換變量。

用 Java 編寫線程程序時,通常看起來像這樣

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

ThreadClass使用的ThreadClass將是用戶定義的。 該類將實現創建線程所需的Runnable接口。 ThreadClassrun()方法(只有Runnable方法)也需要實現。 很明顯,擺脫ThreadClass會更有效,這正是匿名內部類存在的原因。

看下面的代碼

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

此代碼替換了最上面示例中對task的引用。 Thread()構造函數中的匿名內部類沒有單獨的類,而是返回一個未命名的對象,該對象實現了Runnable接口並覆蓋了run()方法。 方法run()將包含執行線程所需工作的語句。

在回答匿名內部類是否是 Java 的優勢之一的問題時,我不得不說我不太確定,因為我目前對許多編程語言都不熟悉。 但我可以說的是,它絕對是一種更快、更簡單的編碼方法。

參考資料:Sams 自學 Java 21 天第七版

優化代碼的最佳方式。 此外,我們可以用於類或接口的覆蓋方法。

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

}

還有一個優勢:
正如你所知,Java 不支持多重繼承,所以如果你使用“Thread”類作為匿名類,那么該類仍然有一個空間可供任何其他類擴展。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM