匿名内部クラスはJavaでどのように使用されますか?


309

Javaでの匿名クラスの使用は何ですか?匿名クラスの使用がJavaの利点の1つであると言えますか?


67
これは、Javaにクロージャがないことを回避する方法であるため、Javaの利点ではありません。
エリックウィルソン

4
Java 8ではLambda式も導入されています。答えをチェック:stackoverflow.com/questions/355167/...
akhil_mittal

回答:


369

「匿名クラス」とは、匿名の内部クラスを意味するものと解釈します。

匿名内部クラスは、メソッドをオーバーライドするなど、特定の「エクストラ」を使用してオブジェクトのインスタンスを作成するときに、実際にクラスをサブクラス化する必要がない場合に役立ちます。

私はイベントリスナーをアタッチするためのショートカットとして使用する傾向があります。

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

このメソッドを使用すると、実装する追加のクラスを作成する必要がないため、コーディングが少し速くなりActionListenerます。実際に別のクラスを作成せずに、匿名の内部クラスをインスタンス化するだけで済みます。

このテクニックは、クラス全体を作成する必要がないと感じられる「迅速で汚い」タスクにのみ使用します。まったく同じことをする複数の匿名内部クラスを持つことは、それが内部クラスであろうと別のクラスであろうと、実際のクラスにリファクタリングされるべきです。


5
または、重複する匿名の内部クラスを、匿名の内部クラス(およびおそらく他の重複するコード)を持つ1つのメソッドにリファクタリングすることもできます。
トム・ホーティン-タックライン2008

3
すばらしい答えですが、簡単な質問です。それはJavaが匿名の内部クラスなしで生きることができることを意味し、それらはそこから選択する追加のオプションのようなものですか?
realPK 14

5
非常によく説明されていますが、これを読んで、Java 8およびラムダ式がコーディングをより速く、より読みやすくするために何ができるかを確認するために、これを読んでいる人にはすごく強く勧めます。
Pievis 2014年

2
@ user2190639正確に言うと、Java8のLambdaで
bonCodigo

3
なぜあなたが言ったのoverloading methodsではなくoverriding methods
Tarun

73

匿名内部クラスは事実上クロージャであるため、ラムダ式または「デリゲート」をエミュレートするために使用できます。たとえば、次のインターフェイスを見てください。

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

これを匿名で使用して、Javaでファーストクラスの関数を作成できます。与えられたリストのiより大きい最初の数を返す次のメソッドがあるとしましょう。

public static int larger(final List<Integer> ns, final int i) {
  for (Integer n : ns)
     if (n > i)
        return n;
  return 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を使用して、これらを次のように1つのメソッドに書き直すことができます。

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

これは実際に考案された例ですが、値のように関数を渡すことができることは非常に便利な機能であることが簡単にわかります。Joel自身の「プログラミング言語でこれを実行できるか」を参照してください。

このスタイルでJavaをプログラミングするための素晴らしいライブラリ:関数型Java。


20
残念ながら、Java、IMHOで関数型プログラミングを行うことの冗長性は、その利点を上回ります。関数型プログラミングの顕著な点の1つは、コードサイズが小さくなり、物事を読みやすく、変更しやすくなることです。しかし、関数型Javaはそれをまったく実行していないようです。
Chii

27
Javaの簡潔さを備えた関数型プログラミングのすべての理解可能性!
Adam Jaskiewicz 2008

3
私の経験では、Javaの関数型スタイルは冗長性を前もって支払われていますが、長期的には簡潔になります。たとえば、myList.map(f)は、対応するforループよりもかなり冗長ではありません。
Apocalisp 2008

2
関数型プログラミングスタイルの言語であるScalaは、JVM内で適切に実行され、関数型プログラミングシナリオのオプションとなる可能性があります。
ダレルティーグ2013年

51

匿名の内部クラスは、次のシナリオで使用されます。

1.)オーバーライド(サブクラス化)の場合、クラス定義が現在のケース以外で使用できない場合:

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

8
すばらしい答えです
。3

47

私は時々それらをマップのインスタンス化のための構文ハックとして使用します:

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

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

多くのputステートメントを実行するときに冗長性を節約します。ただし、リモーティングを介して外部クラスをシリアル化する必要がある場合にも、これを実行するときに問題が発生しました。


56
明確にするために、中括弧の最初のセットは匿名の内部クラス(HashMapのサブクラス)です。中括弧の2番目のセットは、インスタンスの初期化子(静的ではない)であり、HashMapサブクラスに値を設定します。+1の場合は言及し、-1の場合は初心者には省略しません。;-D
スペンサーコルモス

4
ダブルブレース構文の詳細については、こちらをご覧ください
マーティンアンダーソン


18

これらは、通常、詳細形式のコールバックとして使用されます。

それらがなく、毎回名前付きクラスを作成する必要があることと比較して、それらは利点であると言えるかもしれませんが、同様の概念が他の言語で(クロージャーまたはブロックとして)はるかによく実装されています

これがスイングの例です

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

それはまだ厄介な冗長ですが、このようなすべての使い捨てリスナーに対して名前付きクラスを定義するように強制するよりもはるかに優れています(状況と再利用によっては、より良いアプローチである可能性があります)。


1
あなたは簡潔に言うつもりでしたか?冗長である場合、コールバックは個別にとどまり、少し大きくして冗長にします。これがまだ冗長であると言う場合、それでは簡潔な形式は何でしょうか?
user3081519 14

1
@ user3081519、のようなもの、myButton.addActionListener(e -> { /* do stuff here */})またはよりmyButton.addActionListener(stuff)簡潔になります。
Samuel Edwin Ward、

8

別の関数内に特定の目的のクラスを作成する必要がある場合、たとえばリスナーとして、実行可能として(スレッドを生成するためなど)に使用します。

これらの関数は関数のコード内から呼び出すため、他の場所で参照することはないため、名前を付ける必要はありません。コンパイラはそれらを列挙するだけです。

それらは本質的に構文上の砂糖であり、一般的に大きくなるにつれて他の場所に移動する必要があります。

それがJavaの利点の1つであるかどうかはわかりませんが、それらを使用する場合(そして残念ながら私たち全員がそれらを頻繁に使用する場合)、それらは1つであると主張することができます。


6

匿名クラスのガイドライン。

  1. 匿名クラスは同時に宣言され、初期化されます。

  2. 匿名クラスは、1つだけのクラスまたはインターフェースに拡張または実装する必要があります。

  3. 匿名クラスには名前がないため、一度しか使用できません。

例えば:

button.addActionListener(new ActionListener(){

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

    }
});

#3に関して:完全に真実ではありません。リフレクションを使用して、匿名クラスの複数のインスタンスを取得できますref.getClass().newInstance()
icza 14

ルールは質問に答えません。
ローンの侯爵

5

はい、匿名の内部クラスは間違いなくJavaの利点の1つです。

匿名の内部クラスを使用すると、周囲のクラスの最終変数とメンバー変数にアクセスでき、リスナーなどで便利です。

ただし、主な利点は、周囲のクラス/メソッド/ブロックに(少なくとも結合する必要がある)内部クラスコードが特定のコンテキスト(周囲のクラス、メソッド、ブロック)を持っていることです。


1
周辺のクラスにアクセスできることは非常に重要です。匿名クラスが使用される多くの場合、これが理由であると思います。それは、周囲のクラス/メソッドの非パブリック属性、メソッド、およびローカル変数を必要とする/使用するためです。渡されるか、公開されます。
icza 14

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

これは、スレッドを使用した匿名の内部型の例の1つでもあります。


3

新しいスレッドを呼び出すために匿名オブジェクトを使用します。

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

3

内部クラスは、外側のクラスのインスタンスに関連付けられており、二つの特別な種類がある:ローカルクラスおよび匿名クラス。匿名クラスを使用すると、クラスの宣言とインスタンス化を同時に行うことができるため、コードが簡潔になります。名前がないため、ローカルクラスが1度だけ必要な場合に使用します。

クラスがある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は、1つの抽象メソッドのみを持つインターフェースである機能インターフェースという用語を導入しました。したがってCheckPerson、機能インターフェースと言えます。次のように、メソッドの引数として関数を渡すことができるラムダ式を利用できます。

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

インターフェースのPredicate代わりに標準の機能インターフェースを使用できますCheckPerson。これにより、必要なコードの量がさらに削減されます。


2

匿名の内部クラスは、さまざまなオブジェクトにさまざまな実装を提供する際に役立ちます。ただし、プログラムの可読性に問題が生じるため、非常に慎重に使用する必要があります。


1

ファイナライザーガーディアンと呼ばれるクラスファイナライズでの匿名クラスの主な使用法の1つ。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
        }
    };
}

この手法を使用するとsuper.finalize()HeavyClassfinalizeメソッドを必要とするの各サブクラスを呼び出す必要がなくなり、自分自身と他の開発者を解放できました。


1

この方法で匿名クラスを使用できます

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

1

ここでは誰も言及していないようですが、匿名クラスを使用してジェネリック型引数を保持することもできます(通常は型消去のために失われます)

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の肥大化につながります

1

コードを最適化する最良の方法。また、クラスまたはインターフェースのオーバーライドメソッドに使用できます。

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

アン匿名内部クラスは、再び参照されることはありませんオブジェクトを作成するために使用されます。名前はなく、同じステートメントで宣言および作成されます。これは、通常オブジェクトの変数を使用する場合に使用されます。あなたはで変数を置き換えるnewキーワード、コンストラクタの呼び出しとクラス定義の内側{}

Javaでスレッド化されたプログラムを書くとき、それは通常このようになります

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

ここでThreadClass使用されるのはユーザー定義です。このクラスは、Runnableスレッドの作成に必要なインターフェースを実装します。方法(唯一の方法で)同様に実装する必要があります。取り除くことがより効率的であることは明らかであり、それがまさに匿名の内部クラスが存在する理由です。ThreadClassrun()RunnableThreadClass

次のコードを見てください

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

このコードは、task一番上の例で行われた参照を置き換えます。別個のクラスを持つのではなく、Thread()コンストラクター内の匿名内部クラスは、Runnableインターフェースを実装してメソッドをオーバーライドする名前のないオブジェクトを返しますrun()。このメソッドにrun()は、スレッドに必要な作業を実行するステートメントが含まれます。

匿名の内部クラスがJavaの利点の1つであるかどうかという質問に答えると、現時点では多くのプログラミング言語に精通していないので、よくわからないでしょう。しかし、私が言えることは、それは間違いなくコーディングのより迅速で簡単な方法です。

参考資料:Sams Teach Yourself Java in 21 Days Seventh Edition


0

もう1つの利点:
Javaは多重継承をサポートしていないことを知っているので、「スレッド」というクラスを匿名クラスとして使用すると、クラスには、他のクラスが拡張するためのスペースが1つ残っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.