483 Views
February 23, 24
スライド概要
ゆるゆるJavaというDiscord上でのイベントのまとめです。
内容としては以下です。
- 第一回でつまづいていた内容
第一回は以下のJava21で使用できる文法でデザインパターンを新しく書いた以下のリポジトリとYoutubeを見ました。
https://github.com/yanaga/revisiting-design-patterns
https://www.youtube.com/watch?v=VCMVtodGZiA
- ラムダ式の簡単な復習
前回つまづいたところのまとめ
今回の経緯 1. Java 21におけるデザインパターンを前回のゆるゆるJavaで読みました 2. 一部分のコードの意味がわからず、首を傾げました。 3. 一部分のコードの意味がわかるように、復習のための資料を作成しました。 やっていないこと 1. Strategy パターンを実際に使うタイミングの解説 2. Java21での新しいStrategyパターンの書き方についての解説
該当コード
public class RevisitedStrategy {
public static void main(String[] args) {
var endpoint = getEndpoint();
var passInformation = new PassInformation(new UserProfile(true, true, true));
String jwt = endpoint.get(passInformation);
System.out.println(jwt);
}
public static GoogleWalletEndpoint getEndpoint() {
var endpoint = new GoogleWalletEndpoint();
endpoint.setStrategy(pass -> "abc");
endpoint.setStrategy(new AddToGoogleWalletLink()::complete);
endpoint.setStrategy(new AddToGoogleWalletLink()::preCreated);
return endpoint;
}
}
class GoogleWalletEndpoint {
private Function<PassInformation, String> strategy;
public String get(PassInformation passInformation) {return strategy.apply(passInformation);}
public void setStrategy(Function<PassInformation, String> strategy) {this.strategy = strategy;}
}
引用元 : revisiting-design-patterns
該当コードの解説
endpoint.setStrategy(pass -> "abc");
public void setStrategy(Function<PassInformation, String> strategy) {
this.strategy = strategy;
}
疑問だった点 : pass -> "abc"
このpassがPassInformationの型を満たしていない…?
Functionはどこに行ったのか…?
誤解1 そもそも型は要らない 公式のJava Laungage Specificationから引用 Lambda Expressions (int x) -> x+1 (int x) -> { return x+1; } (x) -> x+1 x -> x+1 // // // // // Single declared-type Single declared-type Single inferred-type Parentheses optional single inferred-type parameter parameter parameter for parameter
誤解2
意味がある文章だと思い込んでいた
public void setStrategy(Function<PassInformation, String> strategy) {
this.strategy = strategy;
}
endpoint.setStrategy(pass -> "abc");
どんなPassInformationに対しても、String"abc"を返すという意味
これ以上の意味があるのではないかと勝手に思い込んでいた
復習ラムダ式
ラムダ式の例1
()
()
()
()
()
->
->
->
->
->
{}
42
null
{ return 42; }
{ System.gc(); }
//
//
//
//
//
No
No
No
No
No
parameters;
parameters,
parameters,
parameters,
parameters,
result is void
expression body
expression body
block body with return
void block body
ラムダ式の例2 () -> { // Complex block body with returns if (true) return 12; else { int result = 15; for (int i = 1; i < 10; i++) result *= i; return result; } }
ラムダ式の例3
(int x) -> x+1
(int x) -> { return x+1; }
(x) -> x+1
x -> x+1
//
//
//
//
(String s) -> s.length()
(Thread t) -> { t.start(); }
s -> s.length()
t -> { t.start(); }
Single declared-type
Single declared-type
Single inferred-type
Parentheses optional
//
//
//
//
Single
Single
Single
Single
parameter
parameter
parameter
for
declared-type
declared-type
inferred-type
inferred-type
parameter
parameter
parameter
parameter
(int x, int y) -> x+y // Multiple declared-type parameters
(x, y) -> x+y
// Multiple inferred-type parameters
(x, int y) -> x+y
// Illegal: can't mix inferred and declared types
(x, final y) -> x+y // Illegal: no modifiers with inferred types
Q. ラムダ式ってそもそも何? A. 関数型インターフェースの実装 参考 : https://xtech.nikkei.com/it/article/COLUMN/20140311/542582/?P=6 補足 関数型インターフェース = Java特有の用語
関数型 内部状態に関わらず、入力に対して一定の値を返すもの Javaにおいてインターフェースは状態を持たない(抽象クラスは状態をもつ) ここでの状態はフィールド変数のことを指しています。 参考 : Python公式ドキュメント関数型 参考 : "Interfaces have no instance variables" Java Laungage Specification ちなみに、static finalな変数はinterfaceで宣言できます。 ただ、これは定数と呼んで差し支えないでしょう。
ラムダ式からラムダ式の外部のインスタンス変数へのアクセスについて 実質的に定数(finalでなくても、変数の再代入が行われないもの)のみアクセス可能 →これは、「関数型」の"定義の状態によらない入力に対する出力"を実現するための制 限と思われます。 ただ、もともと匿名クラスにはそういう制限があった模様です。 日経クロステック いまから覚えるのであれば、関数型という言葉の意味から理解しても良いかと思いま す。
そもそもラムダ式とは ラムダ式とは 「ラムダ式は関数型インタフェースを実装した匿名クラス」 引用 : 日経クロステック 個人的にもっと抽象化して言うのであれば 「クラスの定義無しにメソッドを定義する表現方法」
広い意味でのラムダ式 参考 : Wikipedia ラムダ式は他の言語でも使用される。 他の言語ではクロージャと呼ばれる機能も、Javaではラムダ式として内包される。 参考 : JSR "Lambda expressions (informally, "closures" or "anonymous methods")" 意訳 : ラムダ式(非公式にはクロージャ・匿名メソッド) 補足 : anonymous = 匿名(自分の名前を隠して知らせないこと) 無名クラスの方が適切と言う表現もある。
今回調べてみての感想 1. 張り切ってJava言語仕様書を読んでみたが、大したことは書いていなかった。 2. JVMの仕様書も目次を洗ってみたが、ラムダ式についての記載はなかった。 3. StreamAPIとラムダ式の関係性がいまいち掴めなかった。 StreamAPIのためにラムダ式が導入された? 4. 宣言型の記法とメソッドチェーン的な記法の境界が掴めなかった。 5. 我々はJVMの奴隷かもしれない… Javaとしての仕様なのか、コンピュータの仕様なのか、境界線が理解し難い
調べきれなかったこと1 1. Javaの世界では並列処理と並行処理は区別されないということについて理解ができなか った。 2. ラムダ式によってForループより性能が上がるということについて理解ができなかった。 3. ParallelStereamによって並列処理がサポートされていたが、Virtual Threadの登場によっ てラムダ式はどう変化するのか 要は、ラムダ式の内部でバーチャルスレッドをサポートしないのであれば、ラムダ式で の並列処理ではなくThreadを明示的に呼び出したほうが性能が良くなるのでは。 →後に回答が載っている
続き 4. なんで型推論できているの? JavaSE 10でvar(型推論)の宣言が可能になっているのにも関わらず、どうして JavaSE8の時からラムダ式は型の省略が許されていたんだろう。 5. 関数型と宣言型の違い JVMがいい感じに処理してくれるように記載するのが宣言型という意味なんだと思いま すが、上手くこの部分を説明できませんでした。
@zinbe(杉山さん)さんからの補足 1. 関数型インターフェースだから定数以外の参照が禁止されている訳ではない。 元々の匿名クラスのクラスファイルの後方互換性を保つための仕様 2. Virtual Threadについて StreamのParallel処理とVitual Thread、順次処理の使い分けが現状最適 virtual threadを生で作ったほうが早くなる可能性がある。 virtual threadは今後標準のライブラリに反映されていく 処理効率が上昇できる場合のみ バーチャルスレッドが解消するのはサーバー側のI/Oの処理のボトルネック 大量のデータはコレクションを使う方が効率が良い。 3. 宣言型について パラレルストリームは、並列処理と並行処理をJVMが判断するという点で、宣言型に近 い 4. streamAPIの表現としてラムダ式が導入されている