無名クラスとラムダ式
について、インプットしたことです。
【説明】
「関数」と「メソッド」
なぜJavaでは関数を扱えないかというとJavaは「処理をオブジェクトごとに行う」構造だからです。
典型的なJavaプログラム
以下はよく見るJavaのプログラムで、サブクラスから作られたオブジェクトsClassがmyMethod();を呼び出しています。
Mainクラス
package about_lambda_expression1;
public class Main {
public static void main(String[] args) {
SubClass sClass = new SubClass();
sClass.myMethod();
}
}
サブクラス
package about_lambda_expression1;
public class SubClass {
public void myMethod() {
System.out.println("サブクラスのメソッドです");
}
}
実行結果
サブクラスのメソッドです
- 関数: 引数と戻り値(結果)だけが情報
- Javaのメソッド: 上記にプラスで「どのオブジェクトを呼び出すのか」という情報を持つ。
以上の理由により、単純にメソッドに変数をつけたり、メソッドを他のメソッドの引数にしたりすることが出来ない。
Javaで「関数」があればいいと思うケースとは?
「取引の一件ごと」や、「データ送受信の一件ごと」などをオブジェクトとして扱うプログラミングでJavaの「オブジェクトがメソッドを呼び出す」という考え方は便利です。
それでも「関数を使いたいケース」は「場合によって処理を切り替えたい場合」です。
処理をそのまま書く
以下はコマンド引数の数字によって出力結果を切り替えるプログラムです。
こちらのコードは 変数 myTime に引数を与えると値によって返す挨拶が変わるプログラムです。
public class Main {
//Eclipse上で「実行の構成」引数タブ「コマンド引数」に任意の値で出力される内容が変わる。
public static void main(String[] args) {
int myTime = Integer.parseInt(args[0]);
if (5 <= myTime && myTime <= 10) {
System.out.println("おはよう!");
} else if (11 < myTime && myTime <= 16) {
System.out.println("こんにちは!");
} else if (17 < myTime && myTime <= 22) {
System.out.println("こんばんわ!");
} else if (17 < myTime && myTime <= 22) {
System.out.println("もう早く寝たほうがいいですよ");
}
}
}
おはよう!
「メソッド名」で「処理の内容」を表す。
上記のプログラムに朝、昼、夜の挨拶の処理ににそれぞれ名前をつけます。
package about_lambda_expression1;
public class SelectGreet {
public void greetMorning() {
System.out.println("おはよう!");
}
public void greetAfternoon() {
System.out.println("こんにちは!");
}
public void greetEvening() {
System.out.println("こんばんわ!");
}
public void greetOther() {
System.out.println("もう早く寝たほうがいいですよ");
}
}
package about_lambda_expression1;
public class Main {
//Eclipse上で「実行の構成」引数タブ「コマンド引数」に任意の値で出力される内容が変わる。
public static void main(String[] args) {
int myTime = Integer.parseInt(args[0]);
SelectGreet sGreet = new SelectGreet();
if (5 <= myTime && myTime <= 10) {
sGreet.greetMorning();
} else if (11 < myTime && myTime <= 16) {
sGreet.greetAfternoon();
} else if (17 < myTime && myTime <= 22) {
sGreet.greetEvening();
} else if (17 < myTime && myTime <= 22) {
sGreet.greetOther();
}
}
}
それぞれに名前をつけることによって何をしているのかわかりやすくなったものの、一つ一つメソッドをと定義したりめんどくさい部分が出てきました。
それを解消するために「Runnable」が便利です。
Runnableでメソッドと変数を対応させる
package about_lambda_expression1;
public class Main {
// Eclipse上で「実行の構成」引数タブ「コマンド引数」に任意の値で出力される内容が変わる。
public static void main(String[] args) {
Runnable morningRunnable = new Runnable() {
@Override
public void run() {
System.out.println("おはよう!");
}
};
Runnable AfternoonRunnable = new Runnable() {
@Override
public void run() {
System.out.println("こんにちは!");
}
};
Runnable EveningRunnable = new Runnable() {
@Override
public void run() {
System.out.println("こんばんわ!");
}
};
Runnable OtherRunnable = new Runnable() {
@Override
public void run() {
System.out.println("もう早く寝たほうがいいですよ");
}
};
int myTime = Integer.parseInt(args[0]);
if (5 <= myTime && myTime <= 10) {
morningRunnable.run();
} else if (11 < myTime && myTime <= 16) {
AfternoonRunnable.run();
} else if (17 < myTime && myTime <= 22) {
EveningRunnable.run();
} else if (17 < myTime && myTime <= 22) {
OtherRunnable.run();
}
}
}
本来の書き方で「Runnable」を使うと、各変数の違いは実装メソッドの「runの中身」だけになる。
Runnableで処理の内容に変数をつけることは出来ないものの、runというメソッドの内容と「変数名」が対応付けることが出来ました。
ラムダ式を使う
上記のコードをラムダ式を使用してとてもシンプルにまとまりました。
このようにインターフェースを実装した無名クラスのそもそもの役割が、メソッドの内容と変数を対応させるこのにあるからこそラムダ式に代える意義があります。
package about_lambda_expression1;
public class Main {
// Eclipse上で「実行の構成」引数タブ「コマンド引数」に任意の値で出力される内容が変わる。
public static void main(String[] args) {
// ラムダ式に書き換える
Runnable morningRunnable = () -> System.out.println("おはよう!");
Runnable AfternoonRunnable = () -> System.out.println("こんにちは!");
Runnable EveningRunnable = () -> System.out.println("こんばんわ!");
Runnable OtherRunnable = () -> System.out.println("もう早く寝たほうがいいですよ");
int myTime = Integer.parseInt(args[0]);
if (5 <= myTime && myTime <= 10) {
morningRunnable.run();
} else if (11 < myTime && myTime <= 16) {
AfternoonRunnable.run();
} else if (17 < myTime && myTime <= 22) {
EveningRunnable.run();
} else if (17 < myTime && myTime <= 22) {
OtherRunnable.run();
}
}
}
ラムダ式と無名クラスの対応
どのようにラムダ式を表すのかおさらいです。
ラムダ式の正体はオブジェクト
ラムダ式の正体はオブジェクトだが、特別な書き方でなくても本来の構文でもかくことができる。
形式はメソッドに対応
ラムダ式の書き方は、実装するインターフェースのメソッドに従う。
「() - >」の丸括弧は引数がないという意味のカッコですが、Runnableインターフェースのメソッド「run」が引数を取らないメソッドであることに相当する。
「- >」の右側は、メソッドの「run」の中身に相当。
()- > {
system.out....
}
public void run() {
system.out....
}
インターフェースのメソッドと違う形式では書けない
下のように書くとエラーになる。
理由はメソッド「run」が引数を取らないから。
Runnable rn = (msg) - > System.out.println(msg);
ラムダ式は元のインターフェースのメソッド定義を書き換える記法なので、「元の定義と異なる書き方はできない。」
【まとめ】
使いこなすことで冗長な記述がすくなくなりそうです。
まずは、上記の例のような簡単な「インターフェースを実装した無名クラス」をラムダ式で書き換えることが出来ないか、見返してみるといいかもしれないです。