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

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

新規登録して質問してみよう
ただいま回答率
85.50%
JavaFX

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

Java

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

Q&A

解決済

5回答

14866閲覧

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

mr0237

総合スコア164

JavaFX

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

Java

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

0グッド

3クリップ

投稿2017/01/07 10:03

編集2017/01/08 00:42

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

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


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

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

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

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

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

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

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

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

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

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

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

Java

1import java.util.ArrayList; 2import java.util.List; 3public class Test { 4 public static class Person { 5 private String name; 6 private int age; 7 public Person(String name, int age) { 8 this.name = name; 9 this.age = age; 10 } 11 public String getName() { return this.name; } 12 public int getAge() { return this.age; } 13 } 14 public static void main(String[] args) { 15 List<Person> list = new ArrayList<>(); 16 list.add(new Person("太郎", 18)); 17 list.add(new Person("花子", 17)); 18 list.add(new Person("権太", 20)); 19 for (final Person person : list) { 20 System.out.println(person.getName() + " " + person.getAge() + "歳"); 21 } 22 } 23}

※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)を実装したクラスのオブジェクトを渡し、それを使って比較しながらソートするというものです。こちらだったら、使えそうですので、早速書いてみましょう。

Java

1import java.util.ArrayList; 2import java.util.List; 3import java.util.Collections; 4import java.util.Comparator; 5public class Test { 6 public static class Person { 7 // 中略 8 } 9 public static class PersonComparatorByAge implements Comparator<Person> { 10 public int compare(Person o1, Person o2) { 11 return Integer.compare(o1.getAge(), o2.getAge()); 12 } 13 } 14 public static void main(String[] args) { 15 List<Person> list = new ArrayList<>(); 16 list.add(new Person("太郎", 18)); 17 list.add(new Person("花子", 17)); 18 list.add(new Person("権太", 20)); 19 Collections.sort(list, new PersonComparatorByAge()); 20 for (final Person person : list) { 21 System.out.println(person.getName() + " " + person.getAge() + "歳"); 22 } 23 } 24}

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

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

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

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

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

Java

1import java.util.ArrayList; 2import java.util.List; 3import java.util.Collections; 4import java.util.Comparator; 5public class Test { 6 public static class Person { 7 // 中略 8 } 9 public static void main(String[] args) { 10 List<Person> list = new ArrayList<>(); 11 list.add(new Person("太郎", 18)); 12 list.add(new Person("花子", 17)); 13 list.add(new Person("権太", 20)); 14 Collections.sort(list, new Comparator<Person>() { 15 public int compare(Person o1, Person o2) { 16 return Integer.compare(o1.getAge(), o2.getAge()); 17 } 18 }); 19 for (final Person person : list) { 20 System.out.println(person.getName() + " " + person.getAge() + "歳"); 21 } 22 } 23}

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

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

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

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

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

Java

1import java.util.ArrayList; 2import java.util.List; 3import java.util.Collections; 4public class Test { 5 public static class Person { 6 // 中略 7 } 8 public static void main(String[] args) { 9 List<Person> list = new ArrayList<>(); 10 list.add(new Person("太郎", 18)); 11 list.add(new Person("花子", 17)); 12 list.add(new Person("権太", 20)); 13 Collections.sort(list, 14 (o1, o2) -> Integer.compare(o1.getAge(), o2.getAge())); 15 for (final Person person : list) { 16 System.out.println(person.getName() + " " + person.getAge() + "歳"); 17 } 18 } 19}

匿名クラス部分が、(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のこの機能はクロージャーでは無く、クロージャー擬きと考えています。

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

Java

1import java.util.ArrayList; 2import java.util.List; 3import java.util.Collections; 4import java.util.Comparator; 5public class Test { 6 public static class Person { 7 // 中略 8 } 9 public static class PersonComparatorByAge implements Comparator<Person> { 10 private int order; 11 public PersonComparatorByAge(int order) { this.order = order; } 12 public int compare(Person o1, Person o2) { 13 return Integer.compare(o1.getAge(), o2.getAge()) * this.order; 14 } 15 } 16 public static void main(String[] args) { 17 final int order = (args.length < 1 || !"-r".equals(args[0])) ? 1 : -1; 18 List<Person> list = new ArrayList<>(); 19 list.add(new Person("太郎", 18)); 20 list.add(new Person("花子", 17)); 21 list.add(new Person("権太", 20)); 22 Collections.sort(list, new PersonComparatorByAge(order)); 23 for (final Person person : list) { 24 System.out.println(person.getName() + " " + person.getAge() + "歳"); 25 } 26 } 27}

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

投稿2017/01/07 13:13

編集2017/01/08 01:37
raccy

総合スコア21733

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Navina

2020/06/16 08:39

突然の質問、および数年前のご回答へ質問申し訳ありません。 今年より新入社員としてJavaの勉強を始めたものです。 匿名クラスの使い方利用価値について大変わかりやすく説明していただきありがとうございます。 匿名クラスの使い方はわかったもののどうしてもわからない箇所があります。 下記コード内で『』で囲んでいる箇所、compareの引数、Person型のo1,o2ですが、実際にどんな値が入ってくるのでしょうか? 今まで勉強してきた中では Person o1 = new Person (001, "田中"); Person o2 = new Person (002, "鈴木"); のように具体的な値が代入された状態で引数へと渡されていました。 今回はそれがなく突然のo1,o2… compareメソッドでo1.getAge()とo2.getAge()の比較値を…といわれてもピンときませんでした。 数年前のご回答、さらに他者からの質問で大変申し訳ないのですが、 お手すきの時間で構いませんのでご教授いただければ幸いです。 //-------------------------------------------------------------------------------------------- public static class PersonComparatorByAge implements Comparator<Person> { public int compare(Person 『o1』, Person 『o2』) { return Integer.compare(o1.getAge(), o2.getAge()); } //-------------------------------------------------------------------------------------------------
raccy

2020/06/16 10:09 編集

Collections.sortが一体どういう動きをするものかと言うことは理解できているでしょうか?たぶん、これがわからないとcompareに一体どういうものが渡されるのかは理解できません。より、正しく理解するにはソートというものは一体何で、どういう仕組みであるかという所から入って、始めて、なぜ、比較のためのメソッドが実装されたクラスを用意する必要があったのかが分かるかと思います。 逆に言うと、きちんと説明するにはそういった知識から必要になってきますので、ここで全てを書くのは難しいです。この回答は、単に説明のためにCollections.sortを使っただけで、その動作、つまり、ソート自体の仕組みの説明まですることは冗長ですし、コメント欄では小さすぎます。 もし、ソートというものが何かを知らずに、メソッドがどういう動きかを取りあえず知りたいというのであれば、他の手段として、ソースコードを追うというのがあります。openJDKなら、ソースコードが公開されていますので、次のリンクの順番に読んでいってください。 https://github.com/openjdk/jdk/blob/jdk-16%2B0/src/java.base/share/classes/java/util/Collections.java#L178 https://github.com/openjdk/jdk/blob/jdk-16%2B0/src/java.base/share/classes/java/util/List.java#L506 https://github.com/openjdk/jdk/blob/jdk-16%2B0/src/java.base/share/classes/java/util/Arrays.java#L1225 https://github.com/openjdk/jdk/blob/jdk-16%2B0/src/java.base/share/classes/java/util/Arrays.java#L1237 https://github.com/openjdk/jdk/blob/jdk-16%2B0/src/java.base/share/classes/java/util/Arrays.java#L1329 https://github.com/openjdk/jdk/blob/jdk-16%2B0/src/java.base/share/classes/java/util/Arrays.java#L1338 私に今できる説明はこれぐらいです。もっと体系だったちゃんとした回答が欲しい場合は、Collections.sortの動作について別途質問して、他の人にも回答を貰えるようにする方がいいと思います。
guest

0

ベストアンサー

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

java

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

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

投稿2017/01/08 02:27

swordone

総合スコア20649

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kitajima_rccm

2017/12/19 02:01 編集

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

0

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

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

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

投稿2017/01/07 14:25

HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

java

1 new Thread (new Runnable () { 2 3 public void run () { 4 //スレッドで実行したい処理 5 } 6 }).start ();

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

java

1class AWP{ 2 3public static void main(String[] args){ 4 5 6 new Thread (new Runnable () { 7 8 public void run () { 9 for(int i=1;i<10;i++){ 10 System.out.println(i+"回目のスレッド0"); 11 try{ 12 Thread.sleep((int)(Math.random()*1000)); 13 14 }catch(Exception e){} 15 } 16 } 17 }).start (); 18 19 20 new Thread (new Runnable () { 21 22 public void run () { 23 for(int i=1;i<10;i++){ 24 System.out.println(i+"回目のスレッド1"); 25 try{ 26 Thread.sleep((int)(Math.random()*1000)); 27 }catch(Exception e){} 28 } 29 30 } 31 }).start (); 32 33 34 new Thread (new Runnable () { 35 36 public void run () { 37 for(int i=1;i<10;i++){ 38 System.out.println(i+"回目のスレッド2"); 39 try{ 40 Thread.sleep((int)(Math.random()*1000)); 41 }catch(Exception e){} 42 } 43 44 } 45 }).start (); 46 47 48 49} 50 51 52}

投稿2017/01/07 13:51

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

InterfaceClass list = new InterfaceClass() { public Object interfaceName(int arg) { return null; } };

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

投稿2017/01/07 10:19

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/01/07 10:20 編集

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問