読者です 読者をやめる 読者になる 読者になる

Hedgehog Note

日々の開発メモです。

Java8 関数型インタフェースとラムダ式

Java8の関数型インタフェースとラムダ式を自分用にまとめ。

関数型インタフェース

インタフェース 主な用途 説明
Supplier<T> T get() 引数なし、戻り値あり
Consumer<T> void accept(T t) 引数あり、戻り値なし
Function<T, R> R apply(T t) 引数あり、戻り値あり
Predicate<T> boolean test(T t) 引数1つの述語

上記以外のメソッドも定義あり。
他にも多数のインタフェースが存在するが基本形はこの4つ。

ラムダ式

文脈 構文
引数なし () -> 処理
単一引数 x -> 処理
複数引数 (x, y) -> 処理
複数行の処理・戻り値なし x -> { 行1; 行2; }
複数行の処理・戻り値あり x -> { 行1; 行2; return 値; }

処理が1行の場合は{ }不要。returnも不要。
単一引数の場合は引数の( )省略可能。
引数の型は省略可能(型推論)。

関数型インタフェースとラムダ式

インタフェース メソッド ラムダ式
Supplier<T> T get() () -> t
Consumer<T> void accept(T t) t -> void
Function<T, R> R apply(T t) t -> r
Predicate<T> boolean test(T t) t -> boolean

基本形の対応表。まずはこの変換を理解する。

メソッド参照とコンストラクタ参照

構文 ラムダ式 関数型インタフェース
A::new () -> new A() Supplier<A>
A::method () -> A.method()
x -> A.method(x)
Supplier<T>
Consumer<X>, Function<X, R>, Predicate<X>
A::method a -> a.method() Consumer<A>, Function<A, R>, Predicate<A>
a::method () -> a.method()
x -> a.method(x)
Supplier<T>
Consumer<X>, Function<X, R>, Predicate<X>

インスタンスメソッドであればthis::methodのようにthisも使える。
上表のラムダ式と関数型インタフェースはあくまで基本形の例示。
シグネチャさえ一致していれば、引数の数に特に制限はない。

※上表の各記載は下記の通り。
“A"はクラス名、"a"はAクラスのインスタンス
"X"はクラス名、"x"はXクラスのインスタンス
methodはインスタンスメソッド、methodは静的メソッド。
"T”, “R"は任意型。

サンプルソース

参考

下記サイトを参考にしました。

http://www.ne.jp/asahi/hishidama/home/tech/java/uptodate.html#JDK1.8

JSFの勉強4 selectBooleanCheckbox

JSFの勉強です。今回はselectBooleanCheckboxを使ってみます。

作るもの

チェックボックスレンダリング結果を表示するだけです。

f:id:yoshiyuki9026:20170409134234p:plain

実装

まずは画面から。

index.xhtml

選択値はvalueのEL式でバッキングBeanと紐付けます。サブミットするとバッキングBeanにはtrue/falseが格納され、逆にバッキングBean側の値をtrueに設定してレスポンスを返すとチェック状態でレンダリングされます。

h:selectBooleanCheckboxだけだと寂しいので今回はh:outputLabelと併用しています。

続いてバッキングBeanです。

IndexBean.java

画面項目を定義しているだけです。checkbox3は初期値にtrueを設定しているため、画面の初回表示でチェック状態となります。

稼働確認

アプリを起動し、実際に出力されたHTMLソースを確認します(整形済)。

チェックボックスが出力されています。checkbox3はちゃんとcheckedとなっています。
タグは下記のような対応で出力されます。

Facelets HTML
h:selectBooleanCheckbox input type=“checkbox”

JSFの勉強3 selectMany系タグ

JSFの勉強です。前回はselectOne系のタグを使いましたが、今回はselectMany系のタグを使ってみます。名前の通り単一選択か複数選択かの違いですが、言ってしまえばそれだけなので使い方はほぼ同じです。

今回の内容

  • h:selectManyListbox
  • h:selectManyMenu
  • h:selectManyCheckbox

作るもの

複数選択可能なリスト、プルダウン、チェックボックスレンダリング結果を表示します。各タグそれぞれでf:selectItemとf:selectItemsのバリエーションを確認します。

f:id:yoshiyuki9026:20170408141558p:plain

実装

画面の実装です。h:selectMany系タグを順に書いていきます。

index.xhtml

リスト、プルダウン、チェックボックスのいずれも選択値はvalueのEL式でバッキングBeanと紐付けます。selectOne系のタグとは異なり、バッキングBean側は配列・コレクションを用意する必要があります。各要素に表示する項目はf:selectItemかf:selectItemsで指定します。ここは前回のselectOne系と同じです。(コードもほぼコピペ)

続いてバッキングBeanです。

IndexBean.java

前述の通り、画面項目と紐づくプロパティは配列・コレクションとします。この例では配列、List、Setを使用しています。

稼働確認

アプリを起動し、実際に出力されたHTMLソースを確認します(整形済)。

リストとプルダウンに"multiple"属性が出力されています。プルダウンの複数選択がどこまで実用的かはさておき、期待通りの結果です。チェックボックスはラジオと同様tableタグでレンダリングされます。レイアウトは通常横並びですが、layoutにpageDirectionを指定していれば縦並びとなります。(上記ではcheckbox2が該当)

タグは下記のような対応で出力されます。

Facelets HTML
h:selectManyListbox リスト形式(size=n)のselect+multipleとoption
h:selectManyMenu プルダウン形式(size=1)のselect+multipleとoption
h:selectManyCheckbox input type=“checkbox”

JSFの勉強2 selectOne系タグ

JSFの勉強です。今回はselectOne系のタグを使ってみます。

今回の内容

  • h:selectOneListbox
  • h:selectOneMenu
  • h:selectOneRadio
  • f:selectItem
  • f:selectItems

作るもの

リスト、プルダウン、ラジオのレンダリング結果を表示するだけです。h:selectOneListbox、h:selectOneMenu、h:selectOneRadioのそれぞれに対し、f:selectItemとf:selectItemsのバリエーションを確認します。

f:id:yoshiyuki9026:20170406232745p:plain

実装

FaceletsとバッキングBeanを1ファイルずつ作ります。

まずはFaceletsです。h:selectOne系タグをつらつらと書いていきます。基本的な使い方はどれも同じです。

index.xhtml

リスト、プルダウン、ラジオのいずれも選択値はvalueのEL式でバッキングBeanと紐付きます。各要素に表示する項目はf:selectItemかf:selectItemsで指定し、前者は項目を1つずつ定義し、後者はコレクションや配列を渡します。f:selectItemsを使用する場合、必要に応じてvarを定義し、itemLabelやitemValue、itemDisabled等を使用できます。

続いてバッキングBeanです。

IndexBean.java

各画面項目に紐づくフィールドの他、f:selectItems用のコレクションと配列を定義しています。products、getFruits()、getMyItems()がそれにあたります。送信ボタン用にsend()を定義していますが、今回は特に何も実装していません。

稼働確認

アプリを起動し、実際に出力されたHTMLソースを確認します(整形済)。

リストとプルダウンは素直な出力です。一方、ラジオはtableタグを使ってレンダリングするところが少し意外でした(ドキュメントを見ると仕様のようです)。 タグは下記のような対応で出力されます。

Facelets HTML
h:selectOneListbox リスト形式(size=n)のselectとoption
h:selectOneMenu プルダウン形式(size=1)のselectとoption
h:selectOneRadio input type=“radio”

JSFの勉強1 使ってみる

JavaServer Faces(JSF)、JavaEE標準のWebアプリケーションフレームワークです。

最近JSFの勉強はじめました。
知識の整理を兼ねてやったことをまとめていきます。
JSF 2.2を対象としています。

今回の内容

  • 画面遷移
  • h:outputLabel
  • h:inputText
  • h:inputSecret
  • h:commandButton
  • h:button

作るもの

ログインするとウェルカムしてくれる超簡易画面。
ログイン画面とトップ画面の2画面構成です。

f:id:yoshiyuki9026:20170401111653p:plain

f:id:yoshiyuki9026:20170401111700p:plain

実装

画面はxhtmlのテンプレート(Facelets)で作成し、ロジックはPOJOクラス(バッキングBean)に実装します。そしてそれらをEL式によりバインドします。

まずはログイン画面です。

index.xhtml

13行目と17行目のvalue、19行目のactionはそれぞれバッキングBeanと紐付けています。全体を見るとh:から始まるFaceletsタグがもりもり出てきていますが、HTMLタグとほぼ同じなのですぐに理解できます。

続いてバッキングBeanです。

IndexBean.java

入力項目用にuserIdとpasswordを定義し(getter/setterはLombokを使用)、ボタンアクション用にlogin()を定義します。ログインロジックはユーザーに"user"、パスワードに"pass"を入力した場合にトップ画面へリダイレクトし、それ以外の場合はログイン画面に留まるというものです。なお、Bean管理はCDIです。

最後にトップ画面です。必要な部分のみ抜き出していますが、ウェルカム文字と戻るボタンを表示するだけの簡単なものです。

top.xhtml

3行目のoutcomeでログイン画面への遷移を指定します。ログイン画面で使用したh:commandButtonはPOST通信ですが、こちらのh:buttonはGET通信をするという違いがあります。

稼働確認

アプリを起動し、ブラウザで表示するとログイン画面が表示されます。
画面遷移も期待通り動きます。

では、実際に出力されたHTMLソースを見てみます(整形済)。

Faceletsタグが正しく変換されています。

Facelets HTML
h:outputLabel label
h:inputText input type=“text”
h:inputSecret input type=“password”
h:commandButton input
h:button input type=“button” onclick=“~”

Faceletsを書く時のEL式まとめ

Faceletsを書く時の自分用のEL式まとめです。
EL3.0を対象にしています。

評価式

  • #{}

エスケープする場合は#の前に\をつける。#{#{}}のネスト不可。

リテラル

  • #{true}
  • #{false}
  • #{100}
  • #{100.1}
  • #{‘abc’}
  • #{“abc”}
  • #{null}

文字列内で囲い文字と同じ引用符を使う場合は\でエスケープ。
\は\でエスケープ。

[]演算子と.演算子

  • #{bean.property}
  • #{array[index]}
  • #{map.key}
  • #{map[‘key’]}

一番上の書式はgetter/setter経由で操作される。
getterはis~メソッドがある場合はそちらが呼ばれる。

算術演算子

  • #{x + y}
  • #{x - y}
  • #{x * y}
  • #{x / y}
  • #{x div y}
  • #{x % y}
  • #{x mod y}
  • #{-x}

文字列連結演算子

  • #{x += y}

文字列の連結はこれを使う。
算術演算子の+は使えない。

関係演算子

  • #{x == y}
  • #{x != y}
  • #{x < y}
  • #{x > y}
  • #{x <= y}
  • #{x >= y}
  • #{x eq y}
  • #{x ne y}
  • #{x lt y}
  • #{x gt y}
  • #{x le y}
  • #{x ge y}

‘<’, ‘<='は素で書くと事故る。

論理演算子

  • #{x == y && z == w}
  • #{x == y || z == w}
  • #{x == y and z == w}
  • #{x == y or z == w}
  • #{!x}
  • #{not x}

‘&&'は素で書くと事故る。

Empty演算子

  • #{empty x}

null、空文字、空配列、空マップ、空コレクションの場合にtrue。

三項演算子

  • #{x == y ? a : b}

代入演算子

  • #{x = y}

ページスコープの属性xにyを代入する。
xが存在しない場合はxを作成する。

セミコロン演算子

  • #{x = 100; y = 200; x + y}

C言語のカンマ演算子と同様。

予約語

  • and, or, not
  • eq, ne, lt, gt, le, ge
  • true, false
  • null
  • instanceof
  • empty
  • div, mod

ラムダ式

  • #{x -> x +1}
  • #{(x, y) -> x + y}
  • #{() -> 64}
  • #{(x, y)(3, 4)} : 結果7
  • #{v = (x, y) -> x + y; v(3, 4)} : 結果7
  • #{fact = n -> n == 0 ? 1 : n * fact(n-1); fact(5)} : 結果120

上記はEL3.0の仕様書に載っていたサンプル。

Enum

  • #{bean.enumProperty == ‘ENUM_VALUE’}

Enum.valueOf(EnumProperty.class, ‘ENUM_VALUE’)で評価する。

静的フィールドと静的メソッドの参照

  • #{Boolean.TRUE}
  • #{MyBean.CONST_VALUE}

java.lang.*配下は何もせずに使える。
他のパッケージは手動でインポートが必要。

が、自分の環境では色々試しても参照できない。なぜだ。

コレクション

  • #{{1, 2, 3}}
  • #{[1, 2, 3]}
  • #{{‘one’:1, ‘two’:2, ‘three’:3}}

上から順にセット、リスト、マップを定義。

ストリーム

  • #{[‘a’, ‘c’, ‘x’, ‘b’].stream().sorted().toList()} : 結果[a, b, c, x]
  • #{[‘a’, ‘bb’, ‘c’, ‘dd’].stream().filter(s->s.length() == 1).toList()} : 結果[a, c]

かなり色々できるが、使い所が難しい。

FacesServletのurl-patternに何を設定するか

タイトルの件について書いてみます。

JSFを使ってみようと思って、初心者の自分はここで少し混乱しました。知ってる人からすると当たり前なんでしょうけど、入門者には意外とわかりづらい。色々調べた結果をまとめました。間違いがあればご指摘ください。

何がわかりづらい?

試しに「JSF web.xml」や「FacesServlet url-pattern」等、ありがちなキーワードでGoogle先生に聞いてみます。するとどうでしょう。

  • <url-pattern>には*.facesを指定する
  • <url-pattern>には/faces/*を指定する
  • <url-pattern>には*.jsfを指定する
  • <url-pattern>には*.xhtmlを指定する

色々出てくるけど違いは何なんだ・・・。
接頭辞の/faces/*はともかく、他はただの好みの問題なのか?

と、少なくとも自分はそうなりました。
Servletの登録自体はどれも間違っていないことが事態をより深刻化させます。

原因

なぜこんなにやり方がいっぱいあるのか。
その疑問を解消してくれた記事がこちらです。

stackoverflow.com

どうやら古いやり方(JSF 1.x)と今のやり方(JSF 2.x)がごっちゃになっているということのようです。歴史を知らない新参には混沌とした状態ですね。

この記事と前述の設定を突き合わせると、

  • <url-pattern>には*.facesを指定する
  • <url-pattern>には/faces/*を指定する

JSF 1.x時代のもの。使えるけど今風ではないっぽい。
また、前者であればURLの.facesを.xhtmlに変えてアクセスすると生のFaceletsが見えてしまうのでその対策が必要です。後者もURLから/facesを抜くと直接Faceletsへアクセスできてしまうので注意。
※/faces/*の指定はJSFのサンプル等で今も使われている様子。

  • <url-pattern>には*.jsfを指定する

広く使われているらしい。
これも*.facesと同様にFaceletsへの直アクセス対策が必要です。ちなみに*.facesと*.jsfの違いはわかりませんでした。どこかで時代の節目があったのか、ただの派閥的な話なのか・・・。

  • <url-pattern>には*.xhtmlを指定する

JSF 2.x時代のもの。昔はFacesServletの仕組み上指定できなかったらしい。
この方法であれば生のFaceletsを見られることはない。

結論

*.xhtmlを指定するのが良さそうですね。

補足

*.facesや*.jsfを使用する場合にFaceletsへの直接アクセスを防ぐ方法は下記で紹介されています。

den2sn.hatenablog.com