web-dev-qa-db-ja.com

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

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

293
Warrior

「匿名クラス」とは、君が言う意味の 匿名内部クラス である。

匿名内部クラスは、実際にクラスをサブクラス化しなくても、メソッドのオーバーライドなど、特定の「追加機能」を使用してオブジェクトのインスタンスを作成するときに役立ちます。

私はそれをイベントリスナを付けるためのショートカットとして使う傾向があります。

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

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

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

354
coobird

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

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よりも小さい最初の数を返す別のメソッドがあります。もしそれ以下の数がなければ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をプログラミングするための素晴らしいライブラリ: Functional Java。

73
Apocalisp

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

1.)オーバーライド(Sub classing)の場合、現在の場合以外はクラス定義が使用できない場合

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

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

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

vs

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

多くのputステートメントを実行するとき、それはいくらかの冗長性を節約します。しかし、外部クラスをリモーティングによって直列化する必要があるときにも、これを実行すると問題が発生します。

45
Chase Seibert

これらは一般的に冗長形式のコールバックとして使用されます。

それらを持たず、毎回名前付きクラスを作成しなければならないのと比較して、それらは利点であると言えるかもしれませんが、他の言語でも同様の概念がはるかによく実装されます

これがスイングの例です

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

それはまだ非常に冗長ですが、このようにすべての捨てリスナーに対して名前付きクラスを定義することを強いるよりもはるかに優れています(状況や再利用にもよりますが、それでもまだより良い方法です)

18
madlep

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

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

それらは本質的に構文糖であり、そしてそれらが大きくなるにつれて一般的に他の場所に移動されるべきである。

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

8
Uri

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

  1. 無名クラスは同時に宣言および初期化されます。

  2. 匿名クラスは、それぞれ唯一のクラスまたはインタフェースに拡張または実装する必要があります。

  3. 匿名クラスには名前がないため、一度だけ使用できます。

例えば:

button.addActionListener(new ActionListener(){

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

    }
});
6

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

匿名の内部クラスを使用すると、周囲のクラスのfinal変数とmember変数にアクセスできます。これはリスナーなどに役立ちます。

しかし、大きな利点は、(少なくとも)周囲のクラス/メソッド/ブロックに密接に結合されている内部クラスコードが特定のコンテキスト(周囲のクラス、メソッド、およびブロック)を持つことです。

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つでもあります。

5
raja

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

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

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

Personクラスがある doc の例を見てください。

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 8Functional Interface という用語を導入しました。これは1つの抽象メソッドしか持たないインタフェースであるため、CheckPersonは機能的なインターフェース。 Lambda Expression を使うことができます。これにより、関数をメソッドの引数として渡すことができます。

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

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

3
i_am_zero

匿名の内部クラスは、異なるオブジェクトに対して異なる実装を与える一方で有益です。しかしそれはプログラムの読みやすさの問題を引き起こすので非常に控えめに使用されるべきです。

2
user1923551

finalizer guardianと呼ばれる、クラスのファイナライズにおける無名クラスの主な使用法の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
        }
    };
}

このテクニックを使うことで、finalizeメソッドを必要とするHeavyClassの各サブクラスでsuper.finalize()を呼び出すようにあなた自身や他の開発者を安心させました。

1
Hazhir

二度と参照されないオブジェクトを作成するために、Anonymous Inner Classが使われます。名前はなく、同じ文で宣言および作成されます。これは、通常オブジェクトの変数を使用する場所で使用されます。変数をnewキーワード、コンストラクターの呼び出し、および{}内のクラス定義に置き換えます。

Javaでスレッドプログラムを書くとき、それは通常このように見えるでしょう

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

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

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

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

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

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

参考文献:Samsは21日第7版であなた自身のJavaを教える

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

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

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
vsminkov

このように匿名クラスを使うことができます

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

もう一つの利点:
Javaが多重継承をサポートしていないことを知っているので、匿名クラスとして "Thread"ちょっとクラスを使用するならば、そのクラスはまだ拡張するための他のクラスのために残っている1つのスペースを持ちます。

0
Arun Raaj