質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.33%

  • Java

    14425questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • JavaFX

    410questions

    JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。

匿名クラスってわかりやすい例に例えると?またそのメリットは?

解決済

回答 5

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 5,248

mr0237

score 143

JAVAを勉強している者です。説明が下手ですいませんが、「匿名クラス」って一体なんなんでしょうか?ネットや参考書みても「その場で使い捨てるクラス」と書いてあるんですが、そんな機能なんかメリットあるんですか?
私はまだ勉強不足だと思いますが、「匿名クラス」ってなんのメリットがあるのか、いまいちわかりません。
わかりやすいような例や例えで教えてくれませんか?
できれば身近な例を挙げて教えてくれませんか?
よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

+8

少々難しいかも知れません。しかも長い。


イントロダクション - Javaは制限だらけ

プログラムは、端的に言うと、命令と言う手続きの集合です。そして、Javaで何らかの手続きの集合は全てメソッドです。手続きとしてなんらかの処理を行いたい場合は、必ずメソッドを定義する必要があり、メソッド内でしか処理(手続きの内容)を書くこと(実装)はできません。

また、Javaでは、実装がある(手続きが書かれているということ)メソッドはクラス内でしか定義することができません(※1)。つまり、必ずクラスを定義しないと、実装があるメソッドを定義できないと言うことです。もし、何らかの任意の処理を行いたい場合は、どこかでメソッドを定義し、また、そのメソッドが定義されるクラスが必要になります。

※1 Java8からインターフェースでもstaticメソッドとdefaultメソッドでのみ内容を実装できるようになりました。しかし、インターフェースに結びつけられ、インターフェースを定義しないと実装があるメソッドを定義できないという点では、クラスと同じです。

もう一つ、Javaで重要なことがあります。Javaではメソッドが第一級オブジェクトでは無いと言うことです。第一級オブジェクトというのは聞いたことが無いかも知れませんが、Javaではプリミティブ値や他の通常のオブジェクトが第一級オブジェクトにあたります。第一級オブジェクトは、引数の一つとして他のメソッドに渡したり、変数に代入したりすることができるもので、普通のデータとして扱えるものと思って貰うとわかると思います。メソッドは、そんなこと、つまり、引数として渡したり、変数に代入するなどと言うことはできません。

ここまではいいでしょうか?まとめると、Javaという言語には次の制限があります。(※2)

  1. 全ての処理は、クラスの中のメソッドとして書く必要がある。
  2. メソッドは普通にデータ(プリミティブ値やオブジェクト)のように扱えない。

※2 これらはJavaの制限であって、他の言語ではそうではありません。例えば似たような名前のJavaScriptでは、逆に全てが関数と呼ばれるものものであり(メソッドもありますが、メソッドは関数の一種に過ぎません)、関数は第一級オブジェクトです。

このような制限があっても、困ることはあまりありません。しかし、処理自体を引数として渡したいと言った場合に、この制限はかなり厳しいものになります。そのときに役に立つのが匿名クラスです。そんなことがあるのか?どれだけ役に立つのかを見に行ってきましょう。

任意の処理を指定する方法 - クラスを作るしか無いのか?

次のようなプログラムを考えてみました。(※3)

import java.util.ArrayList;
import java.util.List;
public class Test {
  public static class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
      this.name = name;
      this.age = age;
    }
    public String getName() { return this.name; }
    public int getAge() { return this.age; }
  }
  public static void main(String[] args) {
    List<Person> list = new ArrayList<>();
    list.add(new Person("太郎", 18));
    list.add(new Person("花子", 17));
    list.add(new Person("権太", 20));
    for (final Person person : list) {
      System.out.println(person.getName() + " " + person.getAge() + "歳");
    }
  }
}

※3 内部クラスを使っているのは単にソースが1ファイルで書けるようにするためですので、深い意味はありません。

人物の名前と年齢をまとめたデータをリスト化して処理するプログラムです。さて、このプログラムのlistの内容ですが、addを使って登録された順番になっています。出力する場合も、登録された順番で出力されます。ここで、一つ要望が出てきました。このlistの内容について、年齢の若い順に並び替えて欲しい、と言われました。どうしましょうか?あ、諸事情により、Personは変更できない物とします。Personの実装がこれ以上肥大化するのは禁止という事らしいです。

通常、ListのソートといえばCollections.sort(List<T> list)を使います。しかし、今回はこれが使えません。なぜなら、List<T>のT(このコードではTはPersonのことです)がComparableインターフェースを実装してなければならないからです。PersonはComparableインターフェースを実装していません。PersonにComparableインターフェースを追加するというのもありますが、今回はPersonの実装が肥大化するのが禁止されているので、その方法も使えません。

ではどうするのか?というと、もう一つのsortメソッド、Collections.sort(List<T> list, Comparator<? super T> c)を使います。これはT、つまり、このコードで言うとPersonについて、Comparatorインターフェースを実装した、つまり、比較するメソッド(comapare)を実装したクラスのオブジェクトを渡し、それを使って比較しながらソートするというものです。こちらだったら、使えそうですので、早速書いてみましょう。

import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.Comparator;
public class Test {
  public static class Person {
    // 中略
  }
  public static class PersonComparatorByAge implements Comparator<Person> {
    public int compare(Person o1, Person o2) {
      return Integer.compare(o1.getAge(), o2.getAge());
    }
  }
  public static void main(String[] args) {
    List<Person> list = new ArrayList<>();
    list.add(new Person("太郎", 18));
    list.add(new Person("花子", 17));
    list.add(new Person("権太", 20));
    Collections.sort(list, new PersonComparatorByAge());
    for (final Person person : list) {
      System.out.println(person.getName() + " " + person.getAge() + "歳");
    }
  }
}

Comparator<Person>インターフェースを実装したPersonComaratorByAgeクラスを作って、そのオブジェクトを渡すことで無事年齢順にソートできました。

しかし、これって面倒とは思えませんか?他で再利用するのはともかく、ここでしかPersonComaratorByAgeを使わない場合はどうなんでしょう。わざわざちゃんとクラスを定義することは無駄にも思えます。それともう一つ。使うところと定義されるところがバラバラですので、Collections.sort(list, new PersonComparatorByAge());を見てもどういう比較をしてソートするのかわかりません。もちろん、クラス名をわかりやすくしているため予想は付きますし、IDEの助けがあれば、すぐに定義部分を表示できるでしょう。でも、やっぱり、冗長です。

と言う不便さをなくすためにあるのが匿名クラスです。

匿名クラス - 名前が無い、その場限りのクラス

早速、匿名クラスを使った例に書きかえます。

import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.Comparator;
public class Test {
  public static class Person {
    // 中略
  }
  public static void main(String[] args) {
    List<Person> list = new ArrayList<>();
    list.add(new Person("太郎", 18));
    list.add(new Person("花子", 17));
    list.add(new Person("権太", 20));
    Collections.sort(list, new Comparator<Person>() {
      public int compare(Person o1, Person o2) {
        return Integer.compare(o1.getAge(), o2.getAge());
      }
    });
    for (final Person person : list) {
      System.out.println(person.getName() + " " + person.getAge() + "歳");
    }
  }
}

PersonComaratorByAgeクラスがなくなり、Colletions.sortの引数を渡すところで、同じ実装がそのまま書かれました。この場合ですと、使っているところで、どのようなソートをしているかの実装があるので、年齢の若い順にソートか、とすぐにわかります。余計なクラス定義も無くなりますし、クラス管理もしなくても良くなります。他でも利用とかはできなくなりますが、もし、この場所で使わなければ、特に問題はありません。

どうでしたか?便利だと思いましたか?ちょっとしたことを書くだけなら、クラス定義が不要になる分、匿名クラスは便利…なように見えます。しかし、実際は別にクラスとしてちゃんと定義して、別の場所でも利用できるようにした方がいい場合もあります。その点は一長一短なので、どちらとも言えないと言ったところでしょう。

さて、今回はこれで終わりではありません。この匿名クラスの書き方。普通にクラスを定義する場合とさほど書き込む量が変わりません。Java8からはもっと簡易な書き方が用意されるようになりました。それはラムダ式です。

ラムダ式 - Javaは関数型プログラミングを夢見たのか?

ラムダ式とは何か?を説明するとLISPの歴史まで語ることになるので省きますが、Java以外のメジャーな言語でラムダ式が無かったのはC言語ぐらいです。Javaのラムダ式採用はかなり後発です。早速、実装を見てみましょう。

import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
public class Test {
  public static class Person {
    // 中略
  }
  public static void main(String[] args) {
    List<Person> list = new ArrayList<>();
    list.add(new Person("太郎", 18));
    list.add(new Person("花子", 17));
    list.add(new Person("権太", 20));
    Collections.sort(list,
                     (o1, o2) -> Integer.compare(o1.getAge(), o2.getAge()));
    for (final Person person : list) {
      System.out.println(person.getName() + " " + person.getAge() + "歳");
    }
  }
}

匿名クラス部分が、(o1, o2) -> Integer.compare(o1.getAge(), o2.getAge())という一行だけになりました。見ればわかるとおり、これは元々の実装のコアの部分です。つまり、周りの部分が全部無くなっています。さて、これはどういう事かというと、Javaのラムダ式は、このようにどんなインターフェースのオブジェクトが必要か自明の場合、ラムダ式の部分を自動的に匿名クラスと同じように扱ってくれます。つまり、これは匿名クラスを使った場合と実質同じ扱いです。

ただ、これは何でも使えるというわけではありません。実装が必要な抽象メソッドが一つだけという関数型インターフェースで無ければならないという条件があります。Comparatorインターフェースはその条件を満たす(compare()のみ実装が必要)ため、関数型インターフェースであり、ラムダ式を使用できると言うことです。

匿名クラスが必要になる場合のほとんどは、関数型インターフェースの実装であることが多いです。Java8以降しか対応する予定が無い!というのであれば、より便利になったラムダ式も使ってみてください。※4

※4 今回は説明しませんが、ラムダ式と似たような機能でメソッド参照というのがあります。変数名::メソッド名と言う形でラムダ式と同じ所に書けるようになっています。興味があれば一度調べてみてください。

もう一つの利点 - 制限付きクロージャー

さて、匿名クラスおよびラムダ式でかけば、とてもすっきり書けることがわかったと思います。しかし、もう一つの利点があります。それは、finalまたは実質的にfinalなローカル変数を内部で使用できるということです。

実行するときに引数"-r"をつけると年齢が高い順になるよう、プログラムを書き換えて見ましょう。

import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
public class Test {
  public static class Person {
    // 中略
  }
  public static void main(String[] args) {
    final int order = (args.length < 1 || !"-r".equals(args[0])) ? 1 : -1;
    List<Person> list = new ArrayList<>();
    list.add(new Person("太郎", 18));
    list.add(new Person("花子", 17));
    list.add(new Person("権太", 20));
    Collections.sort(
        list, (o1, o2) -> Integer.compare(o1.getAge(), o2.getAge()) * order);
    for (final Person person : list) {
      System.out.println(person.getName() + " " + person.getAge() + "歳");
    }
  }
}

変数orderは"-r"がなければ1、あれば-1になります。そして、変数orderがラムダ式の中でも使えていることに注目です。これは匿名クラスであっても、同様に使用できます。このようにfinalなローカル変数を内部で使用することができます。普通のローカル変数以外にも仮引数であっても同様に使用可能です。このような仕組みをクロージャーといいます。クロージャーが何かを説明すると、また、LISPの歴史から始めることになりますので、ここでは割愛しておきます。

しかし、Javaのクロージャーには制限があります。それは、変数がfinal、または、実質的にfinalで無ければならないと言うことです。実質的にfinalとは、宣言時にfinalがなくても、finalが付いているものとして見なすことができるものを言います。暗黙的なfinalがついていると言った方がいいでしょう。なお、実質的にfinalなローカル変数に代入しようとすると、ラムダ式や匿名クラス内部での使用と矛盾するため、コンパイルエラーになります。(※5)

※5 このfinalまたは実質的にfinalという制限は、Java特有のもので、ほとんどの言語のクロージャーではそのような制限がありません。そのため、私は個人的に、Javaのこの機能はクロージャーでは無く、クロージャー擬きと考えています。

最後に、ラムダ式や匿名クラスを使わずに、別のクラスにする書き方だった場合はどうなるのかを示しましょう。

import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.Comparator;
public class Test {
  public static class Person {
    // 中略
  }
  public static class PersonComparatorByAge implements Comparator<Person> {
    private int order;
    public PersonComparatorByAge(int order) { this.order = order; }
    public int compare(Person o1, Person o2) {
      return Integer.compare(o1.getAge(), o2.getAge()) * this.order;
    }
  }
  public static void main(String[] args) {
    final int order = (args.length < 1 || !"-r".equals(args[0])) ? 1 : -1;
    List<Person> list = new ArrayList<>();
    list.add(new Person("太郎", 18));
    list.add(new Person("花子", 17));
    list.add(new Person("権太", 20));
    Collections.sort(list, new PersonComparatorByAge(order));
    for (final Person person : list) {
      System.out.println(person.getName() + " " + person.getAge() + "歳");
    }
  }
}

インスタンスフィールドとコンストラクタまで必要になってしまいました。できないことは無いですけど、一回限りしか使わないようなクラスにここまでするのは冗長すぎますよね。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

0

JavaFXがタグについているのでGUIを使った話でも。
よく使うのは、ボタンをクリックしたときにどういう動作をさせるかを記述するときです。
例えばAndroidで、あるボタンをクリックしたときに画面下に文字を表示したい、というような場合、

button.setOnClickListener(new View.OnClickListener(){
    @Override
    public void onClick(View v){
        Toast.makeText(this, "クリックしたよ", Toast.LENGTH_LONG).show();
    }
});


のように書きます。こうすることで、「このボタンを押すと何が起こるのか」がすぐ近くに書いてあってわかりやすいというメリットがあります。
また匿名クラスを使わない場合、別のjavaファイルにそれ用のクラスを作ることになると思います。今は1個なのでそれほど問題ではないかもしれませんが、もしこれが100とか200とかになったとしたらどうします?どれがどのボタンに結びついているのかわからなくなってきませんか?
匿名クラスの場合、使い捨てなのでそんな配慮が不要になります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/19 10:59 編集

    ひとつ質問してよろしいですか?
    匿名クラスやラムダ式を使って、JavaFXのControllerクラスに処理を記述して行くとそのクラスが肥大化していくと思うのですが、その辺はどう考えればよいのでしょう?
    例えばコーディング規約で1クラスは600行以内とか決めていても、すぐにそれを越えてしまします。
    Controllerクラスが数千行にもなってしまい困っております。
    実際のプログラムで本当に匿名クラスやラムダ式を多用するものなのか
    疑問に思っています。

    キャンセル

0

InterfaceClass list = new InterfaceClass() {

public Object interfaceName(int arg) {
    return null;
}

};

のように クラス宣言をせずに拡張したクラスのことを匿名クラスといいます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/07 19:19 編集

    メリット : 使い切りのクラスを別クラスファイルで定義する必要がない。
    デメリット: 可読性の低下

    キャンセル

0

簡易にスレッドを作って実行したい時、下記の匿名クラスの使い方はとても便利です

       new Thread (new Runnable () {

                public void run () {
       //スレッドで実行したい処理
                          }
            }).start ();


下記ではスレッドを三つ同時に動かしていますが、上の文に単に実行したい内容を書き込んで3つ並べるだけで全てのスレッドを同時に動かせます。

class AWP{

public static void main(String[] args){


        new Thread (new Runnable () {

                public void run () {
                    for(int i=1;i<10;i++){
                        System.out.println(i+"回目のスレッド0");
                        try{
                            Thread.sleep((int)(Math.random()*1000));

                        }catch(Exception e){}
                    }
                }
            }).start ();


        new Thread (new Runnable () {

                public void run () {
                    for(int i=1;i<10;i++){
                        System.out.println(i+"回目のスレッド1");
                        try{
                            Thread.sleep((int)(Math.random()*1000));
                        }catch(Exception e){}
                    }

                }
            }).start ();


        new Thread (new Runnable () {

                public void run () {
                    for(int i=1;i<10;i++){
                        System.out.println(i+"回目のスレッド2");
                        try{
                            Thread.sleep((int)(Math.random()*1000));
                        }catch(Exception e){}
                    }

                }
            }).start ();



}


}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

「使い捨て」という性質は、「影響範囲の制限」とも言えます。

プログラムを作るうえでは、どうしてもデバッグやセキュリティホール対策が必要となります。一方、「影響範囲が広い機能」の多用はこれらの作業の妨げとなります。影響範囲の広い機能が複雑に絡み合うと意図しないことが起こりやすくなるためです。

これが匿名クラスの有用性の一つです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.33%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Java

    14425questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • JavaFX

    410questions

    JavaFXとは、Java仮想マシン上で動作するリッチインターネットアプリケーション (RIA) のGUIライブラリです。Swingとは異なり、FXMLと呼ばれる XMLとCSSを併用してデザインを記述します。