<?xml version="1.0" encoding="utf-8" ?>


<?xml-stylesheet href="http://d.hatena.ne.jp/nowokay/rssxsl" type="text/xsl" media="screen"?>


<rdf:RDF
xmlns="http://purl.org/rss/1.0/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xml:lang="ja">
<channel rdf:about="http://d.hatena.ne.jp/nowokay/rss">
<title>きしだのはてな</title>
<link>http://d.hatena.ne.jp/nowokay/</link>
<description>きしだのはてな</description>

<dc:creator>nowokay</dc:creator>
<dc:date>2013-06-17T08:56:06+09:00</dc:date>
<items>
<rdf:Seq>
<rdf:li rdf:resource="http://d.hatena.ne.jp/nowokay/20130613#1371083728"/>
<rdf:li rdf:resource="http://d.hatena.ne.jp/nowokay/20130610#1370852474"/>
<rdf:li rdf:resource="http://d.hatena.ne.jp/nowokay/20130524#1369358126"/>
<rdf:li rdf:resource="http://d.hatena.ne.jp/nowokay/20130523#1369294833"/>
<rdf:li rdf:resource="http://d.hatena.ne.jp/nowokay/20130522#1369202662"/>
</rdf:Seq>
</items>
</channel>



<item rdf:about="http://d.hatena.ne.jp/nowokay/20130613#1371083728">
<title>[java][javaee7]WebSocketをネタにJava EE 7正式版を試してみる</title>
<link>http://d.hatena.ne.jp/nowokay/20130613#1371083728</link>
<description> Java EE 7がリリースされて、それに対応したGlassFish 4もリリースされました。 ついでに、Java EE 7やGlassFish 4に対応したNetBeans 7.3.1もリリースされています。 ということで、NetBeansとGlassFishを使ってJava EE 7を試してみようと思います。 今回は、Java EE 7の中</description>

<content:encoded><![CDATA[
<div class="section">
<p>Java EE 7がリリースされて、それに対応したGlassFish 4もリリースされました。</p>
<p>ついでに、Java EE 7やGlassFish 4に対応したNetBeans 7.3.1もリリースされています。</p>
<br>

<p>ということで、NetBeansとGlassFishを使ってJava EE 7を試してみようと思います。</p>
<p>今回は、Java EE 7の中でも簡単なコードで動きの派手なWebSocket対応を試してみます。</p>
<a name="seemore"></a>

<br>

<h4>ダウンロード</h4>
<p>まずNetBeansをダウンロードします。</p>
<p><a href="https://netbeans.org/downloads/" target="_blank">https://netbeans.org/downloads/</a></p>
<br>

<p>NetBeansは、ちょっと試すにしてもずっと使うにしても、インストーラーではなくzipで落とすほうが楽なので、ここでは「プラットフォーム」に「OSに依存しないZIP」を選択します。</p>
<p>「サポートテクノロジー」が「Java EE」か「すべて」のものをダウンロードしてください。</p>
<br>

<p>インストーラ版をダウンロードした場合にはバンドルされているので不要ですが、今回はGlassFishもダウンロードします。</p>
<p><a href="https://glassfish.java.net/download.html" target="_blank">https://glassfish.java.net/download.html</a></p>
<br>

<p>ここでは、「Full Java EE platform」で、「Zip」の「Multilingual」版をダウンロードします。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084740" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084740.png" alt="f:id:nowokay:20130613084740p:image" title="f:id:nowokay:20130613084740p:image" class="hatena-fotolife"></a></p>
<p>ダウンロードしたら適当なフォルダに解凍してください。</p>
<br>

<h4>NetBeansの起動</h4>
<p>JDKを複数インストールしている人は、NetBeansを起動する前に「/etc/netbeans.conf」の「netbeans_jdkhome」でJDKのフォルダを設定しておくほうがいいと思います。</p>
<br>

<p>/binフォルダにある、環境にあわせた実行ファイルを起動すると、NetBeansが起動します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084741" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084741.png" alt="f:id:nowokay:20130613084741p:image" title="f:id:nowokay:20130613084741p:image" class="hatena-fotolife"></a></p>
<br>

<p>NetBeansの起動後の画面です。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084742" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084742.png" alt="f:id:nowokay:20130613084742p:image" title="f:id:nowokay:20130613084742p:image" class="hatena-fotolife"></a></p>
<br>

<p>「最新情報」でなんかネタが拾われててこっぱずかしいですね。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084743" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084743.png" alt="f:id:nowokay:20130613084743p:image" title="f:id:nowokay:20130613084743p:image" class="hatena-fotolife"></a></p>
<p>このブログは、「すべてのブログ」からたどれるPlanet NetBeansに登録されたものが表示されます。</p>
<p><a href="http://planetnetbeans.org/ja/" target="_blank">http://planetnetbeans.org/ja/</a></p>
<p>NetBeansに関するブログを書く人は登録しておくといいと思います。</p>
<br>

<h4> NetBeansへGlassFishの組み込み</h4>
<p>インストーラ版でNetBeansを起動した場合にはすでにGlassFishが設定されているはずですが、ここでは先ほどダウンロードしたGlassFishを登録する必要があります。</p>
<p>メインメニューから「ウィンドウ &#62; サービス」を選択します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084744" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084744.png" alt="f:id:nowokay:20130613084744p:image" title="f:id:nowokay:20130613084744p:image" class="hatena-fotolife"></a></p>
<br>

<p>「サービス」ウィンドウが開くので、「サーバー」を右クリックしてメニューから「サーバーの追加」を選択します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084745" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084745.png" alt="f:id:nowokay:20130613084745p:image" title="f:id:nowokay:20130613084745p:image" class="hatena-fotolife"></a></p>
<br>

<p>「サーバー・インスタンスの追加」ダイアログが開くので、サーバーで「GlassFish Server」を選択します。名前はそのままでもかまいませんが、ここでは「GlassFish Server 4.0」として「次へ」ボタンを押します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084746" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084746.png" alt="f:id:nowokay:20130613084746p:image" title="f:id:nowokay:20130613084746p:image" class="hatena-fotolife"></a></p>
<br>

<p>NetBeansでは使わないプラグインは無効になっています。「Java WebおよびEE」の機能がここでアクティブ化します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084747" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084747.png" alt="f:id:nowokay:20130613084747p:image" title="f:id:nowokay:20130613084747p:image" class="hatena-fotolife"></a></p>
<br>

<p>GlassFishを解凍したフォルダを「インストール場所」に入力します。GlassFishが検出されると、下部に検出メッセージが表示されるので、「次へ」ボタンを押します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084749" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084749.png" alt="f:id:nowokay:20130613084749p:image" title="f:id:nowokay:20130613084749p:image" class="hatena-fotolife"></a></p>
<br>

<p>ドメインなどの設定ができますが、そのまま「終了」ボタンを押します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084750" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084750.png" alt="f:id:nowokay:20130613084750p:image" title="f:id:nowokay:20130613084750p:image" class="hatena-fotolife"></a></p>
<br>

<p>サーバーとしてGlassFishが追加されました。ここからGlassFishを起動・停止することもできます。ここでは、確認だけ。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084751" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084751.png" alt="f:id:nowokay:20130613084751p:image" title="f:id:nowokay:20130613084751p:image" class="hatena-fotolife"></a></p>
<br>

<h4> Webプロジェクトの作成</h4>
<p>まずはWebアプリケーション用のプロジェクトを作成します。</p>
<br>

<p>メインメニューから「ファイル &#62; 新規プロジェクト」を選択します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084752" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084752.png" alt="f:id:nowokay:20130613084752p:image" title="f:id:nowokay:20130613084752p:image" class="hatena-fotolife"></a></p>
<br>

<p>「Java Web」カテゴリから「Webアプリケーション」を選んで「次へ」ボタンを押します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084753" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084753.png" alt="f:id:nowokay:20130613084753p:image" title="f:id:nowokay:20130613084753p:image" class="hatena-fotolife"></a></p>
<br>

<p>プロジェクト名やプロジェクトの場所を選択します。ここではプロジェクト名に「WebApplicationEE7」として「次へ」ボタンを押します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084754" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084754.png" alt="f:id:nowokay:20130613084754p:image" title="f:id:nowokay:20130613084754p:image" class="hatena-fotolife"></a></p>
<br>

<p>この画面ではサーバーやコンテキストパスの設定ができます。「Java EEバージョン」が「Java EE 7 Web」になっていますね。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084755" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084755.png" alt="f:id:nowokay:20130613084755p:image" title="f:id:nowokay:20130613084755p:image" class="hatena-fotolife"></a></p>
<p>そのまま「次へ」ボタンを押します。</p>
<br>

<p>JSFなどのフレームワークを選ぶことができますが、ここではそのまま「終了」ボタンを押します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084756" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084756.png" alt="f:id:nowokay:20130613084756p:image" title="f:id:nowokay:20130613084756p:image" class="hatena-fotolife"></a></p>
<br>

<h4>WebSocketエントリポイントの作成</h4>
<p>Java EE 7でWebSocketと通信するためのWebSocketエントリポイントを作成します。</p>
<p>「プロジェクト」ウィンドウの「ソース・パッケージ」で右クリックして、メニューから「新規 &#62; その他」を選択します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084757" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084757.png" alt="f:id:nowokay:20130613084757p:image" title="f:id:nowokay:20130613084757p:image" class="hatena-fotolife"></a></p>
<br>

<p>「Web」カテゴりから、「ファイル・タイプ」として「WebSocketエンドポイント」を選択して「次へ」ボタンを押します</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084758" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084758.png" alt="f:id:nowokay:20130613084758p:image" title="f:id:nowokay:20130613084758p:image" class="hatena-fotolife"></a></p>
<br>

<p>ここでは、「クラス名」や「WebSocket URI」などはそのまま、パッケージ名だけ設定しておきます。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084759" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084759.png" alt="f:id:nowokay:20130613084759p:image" title="f:id:nowokay:20130613084759p:image" class="hatena-fotolife"></a></p>
<p>「終了」ボタンを押すと、WebSocketエンドポイントとなるクラスが生成されます。</p>
<br>

<p>処理を加えて、次のように編集します。</p>
<pre class="syntax-highlight">
<span class="synPreProc">package</span> wsock;

<span class="synPreProc">import</span> java.util.Collections;
<span class="synPreProc">import</span> java.util.HashSet;
<span class="synPreProc">import</span> java.util.Set;
<span class="synPreProc">import</span> javax.websocket.OnClose;
<span class="synPreProc">import</span> javax.websocket.OnMessage;
<span class="synPreProc">import</span> javax.websocket.OnOpen;
<span class="synPreProc">import</span> javax.websocket.Session;
<span class="synPreProc">import</span> javax.websocket.server.ServerEndpoint;

<span class="synComment">/**</span>
<span class="synComment"> *</span>
<span class="synComment"> * </span><span class="synSpecial">@author</span><span class="synComment"> naoki</span>
<span class="synComment"> */</span>
<span class="synPreProc">@ServerEndpoint</span>(<span class="synConstant">&#34;/endpoint&#34;</span>)
<span class="synType">public</span> <span class="synType">class</span> NewWSEndpoint {

    <span class="synType">static</span> Set&#60;Session&#62; sessions = Collections.synchronizedSet(<span class="synStatement">new</span> HashSet&#60;Session&#62;());
    
    <span class="synPreProc">@OnMessage</span>
    <span class="synType">public</span> <span class="synType">void</span> onMessage(String message) {
        <span class="synStatement">for</span>(Session s : sessions){
            s.getAsyncRemote().sendText(message);
        }
    }
    
    <span class="synPreProc">@OnOpen</span>
    <span class="synType">public</span> <span class="synType">void</span> open(Session sess){
        sessions.add(sess);
    }
    <span class="synPreProc">@OnClose</span>
    <span class="synType">public</span> <span class="synType">void</span> close(Session sess){
        sessions.remove(sess);
    }
}
</pre>

<br>

<h4>表示ページの作成</h4>
<p>表示ページを作ります。</p>
<p>ここでは、プロジェクト作成時に同時に生成されたindex.htmlファイルをそのまま編集します。</p>
<p>URLは、プロジェクト作成時に指定したコンテキストパスにあわせて修正してください。</p>
<pre class="syntax-highlight">
<span class="synComment">&#60;!DOCTYPE html&#62;</span>
<span class="synIdentifier">&#60;</span><span class="synStatement">html</span><span class="synIdentifier">&#62;</span>
    <span class="synIdentifier">&#60;</span><span class="synStatement">head</span><span class="synIdentifier">&#62;</span>
<span class="synPreProc">        </span><span class="synIdentifier">&#60;</span><span class="synStatement">title</span><span class="synIdentifier">&#62;</span>WebSocketテスト<span class="synIdentifier">&#60;/</span><span class="synStatement">title</span><span class="synIdentifier">&#62;</span>
<span class="synPreProc">        </span><span class="synIdentifier">&#60;</span><span class="synStatement">meta</span><span class="synIdentifier"> </span><span class="synType">http-equiv</span><span class="synIdentifier">=</span><span class="synConstant">&#34;Content-Type&#34;</span><span class="synIdentifier"> </span><span class="synType">content</span><span class="synIdentifier">=</span><span class="synConstant">&#34;text/html; charset=UTF-8&#34;</span><span class="synIdentifier">&#62;</span>
<span class="synPreProc">        </span><span class="synIdentifier">&#60;</span><span class="synStatement">script</span><span class="synIdentifier"> </span><span class="synType">src</span><span class="synIdentifier">=</span><span class="synConstant">&#34;//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js&#34;</span><span class="synIdentifier">&#62;&#60;/</span><span class="synStatement">script</span><span class="synIdentifier">&#62;</span>
<span class="synPreProc">        </span><span class="synIdentifier">&#60;</span><span class="synStatement">script</span><span class="synIdentifier"> </span><span class="synType">type</span><span class="synIdentifier">=</span><span class="synConstant">&#34;text/javascript&#34;</span><span class="synIdentifier">&#62;</span>
<span class="synSpecial">            </span><span class="synIdentifier">var</span><span class="synSpecial"> socket;</span>
<span class="synSpecial">            $</span>(<span class="synStatement">document</span>)<span class="synSpecial">.ready</span>(<span class="synIdentifier">function</span>()<span class="synIdentifier">{</span>
<span class="synSpecial">                </span><span class="synIdentifier">var</span><span class="synSpecial"> host=</span><span class="synConstant">&#34;ws://localhost:8080/WebApplicationEE7/endpoint&#34;</span><span class="synSpecial">;</span>
<span class="synSpecial">                socket = </span><span class="synStatement">new</span><span class="synSpecial"> WebSocket</span>(<span class="synSpecial">host</span>)<span class="synSpecial">;</span>
<span class="synSpecial">                </span>
<span class="synSpecial">                socket.onmessage = </span><span class="synIdentifier">function</span>(<span class="synSpecial">message</span>)<span class="synIdentifier">{</span>
<span class="synSpecial">                    $</span>(<span class="synConstant">'#log'</span>)<span class="synSpecial">.append</span>(<span class="synSpecial">message.data + </span><span class="synConstant">&#34;&#60;br/&#62;&#34;</span>)<span class="synSpecial">;</span>
<span class="synSpecial">                </span><span class="synIdentifier">}</span>

<span class="synSpecial">                $</span>(<span class="synConstant">'#send'</span>)<span class="synSpecial">.click</span>(<span class="synIdentifier">function</span>()<span class="synIdentifier">{</span>
<span class="synSpecial">                    </span><span class="synIdentifier">var</span><span class="synSpecial"> text = $</span>(<span class="synConstant">'#msg'</span>)<span class="synSpecial">.val</span>()<span class="synSpecial">;</span>
<span class="synSpecial">                    socket.send</span>(<span class="synSpecial">text</span>)<span class="synSpecial">;</span>
<span class="synSpecial">                    $</span>(<span class="synConstant">'#msg'</span>)<span class="synSpecial">.val</span>(<span class="synConstant">''</span>)<span class="synSpecial">;</span>
<span class="synSpecial">                </span><span class="synIdentifier">}</span>)
<span class="synSpecial">    </span>
<span class="synSpecial">            </span><span class="synIdentifier">}</span>)<span class="synSpecial">;</span>
<span class="synSpecial">        </span><span class="synIdentifier">&#60;/</span><span class="synStatement">script</span><span class="synIdentifier">&#62;</span><span class="synPreProc">        </span>
<span class="synPreProc">    </span><span class="synIdentifier">&#60;/</span><span class="synStatement">head</span><span class="synIdentifier">&#62;</span>
    <span class="synIdentifier">&#60;</span><span class="synStatement">body</span><span class="synIdentifier">&#62;</span>
        <span class="synIdentifier">&#60;</span><span class="synStatement">h1</span><span class="synIdentifier">&#62;</span>WebSocketテスト<span class="synIdentifier">&#60;/</span><span class="synStatement">h1</span><span class="synIdentifier">&#62;</span>
        <span class="synIdentifier">&#60;</span><span class="synStatement">div</span><span class="synIdentifier"> </span><span class="synType">id</span><span class="synIdentifier">=</span><span class="synConstant">&#34;log&#34;</span><span class="synIdentifier">&#62;</span>
        <span class="synIdentifier">&#60;/</span><span class="synStatement">div</span><span class="synIdentifier">&#62;</span>
        <span class="synIdentifier">&#60;</span><span class="synStatement">input</span><span class="synIdentifier"> </span><span class="synType">id</span><span class="synIdentifier">=</span><span class="synConstant">&#34;msg&#34;</span><span class="synIdentifier"> </span><span class="synType">type</span><span class="synIdentifier">=</span><span class="synConstant">&#34;text&#34;</span><span class="synIdentifier">/&#62;</span>
        <span class="synIdentifier">&#60;</span><span class="synStatement">button</span><span class="synIdentifier"> </span><span class="synType">id</span><span class="synIdentifier">=</span><span class="synConstant">&#34;send&#34;</span><span class="synIdentifier">&#62;</span>送信<span class="synIdentifier">&#60;/</span><span class="synStatement">button</span><span class="synIdentifier">&#62;</span>
    <span class="synIdentifier">&#60;/</span><span class="synStatement">body</span><span class="synIdentifier">&#62;</span>
<span class="synIdentifier">&#60;/</span><span class="synStatement">html</span><span class="synIdentifier">&#62;</span>
</pre>

<br>

<h4> 実行</h4>
<p>それでは実行してみましょう。</p>
<p>プロジェクトを右クリックしてメニューから「実行」を選択します。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084800" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084800.png" alt="f:id:nowokay:20130613084800p:image" title="f:id:nowokay:20130613084800p:image" class="hatena-fotolife"></a></p>
<br>

<p>GlassFishが起動して、プロジェクトが配備されます。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084801" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084801.png" alt="f:id:nowokay:20130613084801p:image" title="f:id:nowokay:20130613084801p:image" class="hatena-fotolife"></a></p>
<br>

<p>テキストボックスになにか入力して「送信」ボタンを押すと、その文字列が表示されます。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130613084802" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130613/20130613084802.png" alt="f:id:nowokay:20130613084802p:image" title="f:id:nowokay:20130613084802p:image" class="hatena-fotolife"></a></p>
<p>複数のブラウザを開いておくと、すべてのブラウザで文字列が表示されます。</p>
<p>WebSocketがちゃんと動いてますね。</p>
<br>

<p>これで、簡単にチャットなどを作ることができそうです。</p>
</div>
]]></content:encoded>
<dc:creator>nowokay</dc:creator>
<dc:date>2013-06-13T09:35:28+09:00</dc:date>
<dc:subject>java</dc:subject>
<dc:subject>javaee7</dc:subject>
</item>
<item rdf:about="http://d.hatena.ne.jp/nowokay/20130610#1370852474">
<title>[java8][java]Java8で最もインパクトのある構文拡張、デフォルトメソッド</title>
<link>http://d.hatena.ne.jp/nowokay/20130610#1370852474</link>
<description> Java8でのラムダの使い方などを説明してきたのですが、構文拡張自体には触れていなかったので、改めてここで簡単に説明しておこうと思います。 まずは、Java8で実際に最もインパクトがある言語拡張、インタフェースのデフォルトメソッドです。 デフォルトメソッドとデフォル</description>

<content:encoded><![CDATA[
<div class="section">
<p>Java8でのラムダの使い方などを説明してきたのですが、構文拡張自体には触れていなかったので、改めてここで簡単に説明しておこうと思います。</p>
<p>まずは、Java8で実際に最もインパクトがある言語拡張、インタフェースのデフォルトメソッドです。</p>
<br>

<h4> デフォルトメソッドとデフォルト実装</h4>
<p>いままでインタフェースには実装をもつことができませんでしたが、Java 8からはインタフェースが実装をもてるようになります。</p>
<p>実装をもつメソッドを定義するときには、キーワードdefaultをメソッドの前につけます。</p>
<pre>
interface Foo{
    void print(String s);
    default void twice(String s){
        print(s);
        print(s);
    }
}
</pre>
<br>

<p>twiceメソッドが実装をもっています。この実装をデフォルト実装といいます。</p>
<a name="seemore"></a>

<p>デフォルトメソッドを実装するクラスで、デフォルトメソッドを実装していない場合は、デフォルト実装が使われます。</p>
<pre>
static class FooImpl implements Foo{
    @Override
    public void print(String s) {
        System.out.println(s);
    }
}
</pre>
<br>

<p>次のtwiceメソッドの呼び出しでは、Fooインタフェースでのデフォルト実装が使われます。</p>
<pre>
public static void main(String... args){
    Foo f = new FooImpl();
    f.twice("yeah!");
}
</pre>
<br>

<p>ただし、toStringなどObjectクラスがもつメソッドのデフォルト実装をもつことはできません。</p>
<br>

<h4> デフォルトメソッドでの多重継承</h4>
<p>デフォルトメソッドは、Javaに多重継承を持ち込みます。</p>
<p>インタフェースは複数のインタフェースを継承することができ、またクラスでは複数のインタフェースを実装することができます。そうすると、インタフェースが実装をもつことによって、複数のインタフェースで同じシグネチャのメソッドがあったときに衝突が発生します。</p>
<br>

<p>たとえば、次のようにtwiceというデフォルトメソッドをもったインタフェースがあるとします。</p>
<pre>
interface Bar{
    void put(String s);
    default void twice(String s){
        put(s + s);
    }
}
</pre>
<br>

<p>単体での実装は問題ありません。</p>
<pre>
static class BarImpl implements Bar{
    @Override
    public void put(String s) {
        System.out.println(s);
    }
}
</pre>
<p>次のように呼び出すことができます。</p>
<pre>
Bar b = new BarImpl();
b.twice("ゴゴ");
</pre>
<br>

<p>ところが、次のように、それぞれデフォルトメソッドとしてtwiceメソッドを持ったFoo、Barインタフェースを両方実装するクラスを定義しようとすると、コンパイルエラーになります。</p>
<pre>
static class FooBarImpl implements Foo, Bar{
    @Override
    public void print(String s) {
        System.out.println(s);
    }

    @Override
    public void put(String s) {
        System.out.println(s);
    }
}
</pre>
<br>

<p>次のようなコンパイルエラーになります。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130610172002" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130610/20130610172002.png" alt="f:id:nowokay:20130610172002p:image" title="f:id:nowokay:20130610172002p:image" class="hatena-fotolife"></a></p>
<br>

<p>この場合、改めてデフォルトメソッドを実装してしまえば、問題なくコンパイルすることができます。どちらかのインタフェースのデフォルト実装を利用したい場合は、キーワードsuperを使ってインタフェースを指定したデフォルト実装を呼び出すことができます。</p>
<pre>
static class FooBarImpl implements Foo, Bar{

    @Override
    public void print(String s) {
        System.out.println(s);
    }

    @Override
    public void put(String s) {
        System.out.println(s);
    }
    @Override
    public void twice(String s){
        Bar.super.twice(s);
    }
}
</pre>
</div>
]]></content:encoded>
<dc:creator>nowokay</dc:creator>
<dc:date>2013-06-10T17:21:14+09:00</dc:date>
<dc:subject>java8</dc:subject>
<dc:subject>java</dc:subject>
</item>
<item rdf:about="http://d.hatena.ne.jp/nowokay/20130524#1369358126">
<title>[java][java8]Java8でのプログラムの構造を変えるOptional、ただしモナドではない</title>
<link>http://d.hatena.ne.jp/nowokay/20130524#1369358126</link>
<description> ※ 5/29 3:23 追記：なんかモナドになったかも。最下部参照 さて、Java8での拡張をいろいろ見てきたわけですが、ではアプリケーションプログラムでFunctionを受け取るメソッドをがんがん定義するかというとそういうことはあまりなく、フレームワーク的な部分で数個定義する</description>

<content:encoded><![CDATA[
<div class="section">
<p>※ 5/29 3:23 追記：なんかモナドになったかも。最下部参照</p>
<br>

<p>さて、Java8での拡張をいろいろ見てきたわけですが、ではアプリケーションプログラムでFunctionを受け取るメソッドをがんがん定義するかというとそういうことはあまりなく、フレームワーク的な部分で数個定義する感じになると思います。もちろん数個でも効果はでかいのですが。</p>
<p>また、おそらくStreamを受け取ったり返したりするメソッドを定義することは、めったにないのではないかと思います。</p>
<p>Mapでの拡張も、メソッド内部での処理記述がかわる話で、メソッドの引数や戻り値はMapのまま変わりありません。</p>
<br>

<p>Javaでのプログラムの構造というのは、メソッドの引数や戻り値の型がなんであるかで決まると言うことができます。その意味では、lambdaやStreamというのは処理の記述は変わるけどプログラムの構造は変わらないとなります。</p>
<p>けれどもOptionalは、おそらく戻り値としてアプリケーションプログラムの中でも頻出することになるのではないかと思います。その意味でOptionalは、Javaのプログラムの構造を変えると言えます。</p>
<br>

<h4> Optionalの基本的な使い方</h4>
<p>Optionalとはなにかというと、nullの可能性がある値をラップして、より安全なプログラムが書けるようにする仕組みです。</p>
<pre class="syntax-highlight">
Optional&#60;String&#62; name = Optional.of(<span class="synConstant">&#34;きしだ&#34;</span>);
</pre>

<p>のように使います。</p>
<a name="seemore"></a>

<p>ここでofメソッドにnullを与えると例外NullPointerExceptionが発生するので、値をもたないOptionalオブジェクトを得るときには次のようにemptyメソッドを使います。</p>
<pre class="syntax-highlight">
Optional&#60;String&#62; name = Optional.empty();
</pre>

<p>なので、この時点では、nullによる例外が発生しにくくなるというよりは、nullによる例外をより早く発生させる、フェイルファスト的な安全性といえます。</p>
<br>

<p>Optionalから値を取り出すときには、getメソッドが使えます。</p>
<pre class="syntax-highlight">
System.out.println( name.get());
</pre>

<p>ただし、emptyオブジェクトに対してgetメソッドを呼び出すと例外java.util.NoSuchElementExceptionが発生します。</p>
<p>そのため、getメソッドを使うときには、isPresentメソッドによるチェックが必須になります。</p>
<pre class="syntax-highlight">
<span class="synStatement">if</span>(name.isPresent()){
  System.out.println( name.get() );
}
</pre>

<br>

<p>getメソッドを使う限りでは、nullチェックがisPresentチェックに、チェックを忘れたときの例外がNullPointerExceptionからNoSuchElementExceptionに変わっただけということになって、あまりメリットはないと言えます。</p>
<br>

<p>そこで、Optionalに対しては、値があるときだけ処理を行うという場合にはifPresentメソッドで処理を行います。</p>
<pre class="syntax-highlight">
name.ifPresent(s<span class="synError"> -&#62;</span> {
  System.out.println(s);
});
</pre>

<p>記述量としてはifでisPresentを判定した場合とそう変わりませんが、記述を強制しやすくなると思います。</p>
<br>

<p>値を持たない場合のデフォルト値を使うという場合には、orElseメソッドが使えます。</p>
<pre class="syntax-highlight">
System.out.println(<span class="synConstant">&#34;名前:&#34;</span> + name.orElse(<span class="synConstant">&#34;指定なし&#34;</span>));
</pre>

<br>

<p>デフォルト値として使うオブジェクトの生成コストが高い場合には、orElseGetメソッドを使います。</p>
<pre class="syntax-highlight">
System.out.println(name.orElseGet(()<span class="synError"> -&#62;</span> 
    ResourceBundle.getBundle(<span class="synConstant">&#34;messages&#34;</span>).getString(<span class="synConstant">&#34;name.default&#34;</span>)));
</pre>

<br>

<p>また、値がない場合は例外を発生させるという場合には、orElseThrowメソッドを使います。</p>
<pre class="syntax-highlight">
System.out.println(name.orElseThrow(()<span class="synError"> -&#62;</span> <span class="synStatement">new</span> Exception(<span class="synConstant">&#34;名前がないよー&#34;</span>)));
</pre>

<p>例外オブジェクトの生成はコストが高いので、最初からSupplier経由になっているのだと思います。</p>
<br>

<p>ここでorElseThrowのシグネチャは</p>
<pre class="syntax-highlight">
&#60;E&#62; T orElseThrow(Supplier&#60;E&#62; sup) <span class="synType">throws</span> E
</pre>

<p>のような感じになっているので、検査例外でも投げることができます。あまり見ないシグネチャなのでおもしろいなと思いました。</p>
<p>実際は、extendsなどが入った型指定になっていますが、省略しています。</p>
<br>

<p>Optionalに用意されているメソッドはこれだけですが、nullの可能性がある値が少し扱いやすくなります。</p>
<p>ただ、そもそもOptionalを扱う変数にnullを割り当てることができて、その場合にはifPresentメソッドやorEleseメソッドを使ったとしてもそこでNullPointerExceptionが発生するので、あまり強い仕組みではありません。そこは言語としてOptionalを持っているわけではないJava言語の限界ではないかと思います。</p>
<pre class="syntax-highlight">
Optional&#60;String&#62; name = <span class="synConstant">null</span>;
name.ifPresent(System.out::println); <span class="synComment">//ぬるぽ</span>
</pre>

<br>

<p>nullの可能性があるというのをシグネチャとして示せるだけでもありがたいという程度ですが、それでもやはりより安全なコードを書くためには大切な仕組みだと思います。</p>
<p>同様の仕組みは、外部ライブラリで用意することも可能ですが、こうやって標準ライブラリに入ることで、ほかの標準ライブラリでもOptionalが使われるようになっていくと思います。その点では標準になったというのは大きいのではないでしょうか。</p>
<p>JPAやJAXBなどマッピング系のライブラリで使えるようになるとありがたいです。</p>
<br>

<h4> Optionalはモナドではない</h4>
<p>ところでOptionalというと、たとえばScalaのOptionやHaskellのMaybeなど他言語での同様の仕組みはモナドの例としてよく取り上げられています。</p>
<p>でも、JavaのOptionalは残念ながらモナドではありません。</p>
<p>Optionalがモナドであるための条件のひとつとして、次のflatMapメソッドのようなシグネチャで、値を別の値に変換してOptionalでくるんで返すメソッドが必要です。</p>
<pre class="syntax-highlight">
&#60;U&#62; Optional&#60;U&#62; flatMap(Function&#60;T, Optional&#60;U&#62;&#62; mappingFunc);
</pre>

<p>このようなメソッドはOptionalクラスにはないので、Optionalはモナドではないということになります。</p>
<br>

<p>たとえば、StringがOptionalでくるまれたfirstNameとlastNameというオブジェクトがあって、両方が値をもつ場合だけ両方を連結した文字列をOptionalでくるんだfullNameを作りたいとします。どちらかが値をもたなければemptyになるとします。</p>
<p>その場合、いま予定されているJava8のOptionalでは次のようなコードになります。</p>
<pre class="syntax-highlight">
Optional&#60;String&#62; fullName = (lastName.isPresent() &#38;&#38; firstName.isPresent()) ? 
        Optional.of(String.join(<span class="synConstant">&#34; &#34;</span>, lastName.get(), firstName.get())) : 
        Optional.empty();
</pre>

<p>こうやって、isPresentで判定してgetで値をとるというのでは、Optionalを使わない場合とあまり変わりません。</p>
<br>

<br>

<p>もしOptionalクラスに上記のflatMapメソッドがあれば、次のように書けます。</p>
<pre class="syntax-highlight">
Optional&#60;String&#62; fullName = 
  lastName.flatMap(ln<span class="synError"> -&#62;</span> 
  firstName.flatMap(fn<span class="synError"> -&#62;</span> 
    Optional.of(String.join(<span class="synConstant">&#34; &#34;</span>, ln, fn))));
</pre>

<br>

<p>変換した値をOptionalでくるんでくれる、次のようなmapメソッドがあれば、もっと記述がすっきりします。</p>
<pre class="syntax-highlight">
&#60;U&#62; Optional&#60;U&#62; map(Function&#60;T, U&#62; mappingFunc);
</pre>

<br>

<p>そうすると、次のようになりますね。</p>
<pre class="syntax-highlight">
Optional&#60;String&#62; fullName = 
  lastName.flatMap(ln<span class="synError"> -&#62;</span> 
  firstName.map(fn<span class="synError"> -&#62;</span> 
    String.join(<span class="synConstant">&#34; &#34;</span>, ln, fn)));
</pre>

<br>

<p>どうなっているかというと、Optionalの中ではOptionalを気にせずに処理が記述ができて、それでいて外部からはOptionalで包まれているという風になっていて、Optionalの外と中が分離されたコードになっています。モナドというのは、モナドでくるまれた中の世界ではモナドを気にせずに処理が記述でき、外からみるとモナドでくるまれているという、外と中を分離するための仕組みです。</p>
<p>Optionalの役割を考えると、そのように分離されているほうが都合がよく、そのためScalaやHaskellでの同様の仕組みはモナドになっているわけです。</p>
<p>Java8でも、Streamはモナドになっているので、Streamの中の処理はStreamを意識せずに記述でき、外からみると常にStreamにくるまれているという風になっています。</p>
<br>

<p>でも、Java8のOptionalはそうなっていないので、Optionalにくるまれた値を処理するときには、一旦Optionalの外に値を持ってくる必要がでてきます。これはちょっと残念です。</p>
<br>

<h4> Optionalの議論</h4>
<p>さて、じゃあOptionalがモナドになっていないことを、だれも気にしてないんでしょうか？</p>
<p>もちろんそんなことはなくて、たとえばMario Fuscoという人が、次のようなブログを書いてJava8でのOptionモナドの実装について記述しています。</p>
<p><a href="http://java.dzone.com/articles/no-more-excuses-use-null" target="_blank">No more excuses to use null references in Java 8 | Javalobby</a></p>
<br>

<p>その数日後、次のようにメーリングリストに投稿して、Optionalをモナドにしてほしいと要望を出しています。</p>
<p><a href="http://openjdk.5641.n7.nabble.com/Option-in-Java-8-td59242.html" target="_blank">OpenJDK Lambda Development - Option in Java 8</a></p>
<p>それに対して、Javaの設計者のひとりBrian Goetzは「JavaのOptionalの目的はOptionモナドの実装ではない」と答えています。</p>
<br>

<p>そして、Brian Goetzは次の投稿で、Optionalの議論にこれ以上時間をとらせないでくれというようなことを言っていますね。</p>
<p><a href="http://mail.openjdk.java.net/pipermail/lambda-dev/2012-October/006365.html" target="_blank">Optional require(s) NonNull</a></p>
<p>この投稿の中で、Optionalの狭い設計対象は、JDKの内部での利用であると述べています。また、Optionモナドの実装や、Optionモナドが解決しようとしている問題を解決することが目的でないと述べています。</p>
<p>flatmapの言語サポートやパターンマッチングなどもない状況では、Optinalをモナドにしても価値は非常に落ちる、と。</p>
<br>

<p>Mario Fuscoは、なぜOptionalをモナドにしないか、納得いってないようですけど。</p>
<p><a href="https://groups.google.com/forum/?fromgroups#!topic/javaposse/8tfGU2kORAU" target="_blank">java.util.Optional in Java 8 - Google グループ</a></p>
<br>

<p>まあ、今後のリリースで、Optionalクラスにぺろっとmapメソッドが実装されていたりすることはなさそうです。</p>
<p>※ 5/29 1:49 追記 なんか先ほどぺろっとmap/flatMapメソッドが実装されたっぽい</p>
<p><a href="http://hg.openjdk.java.net/lambda/lambda/jdk/rev/fde3666e6394" target="_blank">http://hg.openjdk.java.net/lambda/lambda/jdk/rev/fde3666e6394</a></p>
</div>
]]></content:encoded>
<dc:creator>nowokay</dc:creator>
<dc:date>2013-05-24T10:15:26+09:00</dc:date>
<dc:subject>java</dc:subject>
<dc:subject>java8</dc:subject>
</item>
<item rdf:about="http://d.hatena.ne.jp/nowokay/20130523#1369294833">
<title>[java][java8]Java8で強化されたMapと、書きやすくなったメモ化再帰</title>
<link>http://d.hatena.ne.jp/nowokay/20130523#1369294833</link>
<description> Java8のlambda構文の話を書くと、旧来の書き方でいいというコメントがつくのですが、それでも便利になったMapの恩恵を受けることは多いんじゃないかと思います。 Mapには、lambda式を使ったメソッドが多く追加されていますが、たとえばgetOrDefaultメソッドのようなlambda式</description>

<content:encoded><![CDATA[
<div class="section">
<p>Java8のlambda構文の話を書くと、旧来の書き方でいいというコメントがつくのですが、それでも便利になったMapの恩恵を受けることは多いんじゃないかと思います。</p>
<br>

<p>Mapには、lambda式を使ったメソッドが多く追加されていますが、たとえばgetOrDefaultメソッドのようなlambda式を使わないメソッドも追加されていて、これも便利です。</p>
<p>そして、このようなlambda式を使わないメソッドも、間接的にはlambda構文サポートでの言語拡張のおかげです。</p>
<p>Mapはインタフェースなので、Java7までの構文でメソッドを追加しようとすると、Mapを実装しているすべてのクラスに新しいメソッドの実装を追加する必要がありました。そしてそれは現実的に不可能なので、今までMapなどのインタフェースに手がいれられることはありませんでした。それが、lambda構文サポートの一環として入れられた、インタフェースのデフォルト実装のおかげで、インタフェースを拡張することができるようになったわけです。</p>
<a name="seemore"></a>

<br>

<h4> forEachとgetOrDefault</h4>
<p>では、今回のMap拡張で一番使うことになりそうな、forEachとgetOrDefaultを見てみます。</p>
<p>まず、文字列のリストを集計して、文字列ごとの出現回数をカウントして表示するコードをJava7構文で書いてみます。</p>
<pre class="syntax-highlight">
List&#60;String&#62; strs = Arrays.asList(<span class="synConstant">&#34;blue&#34;</span>, <span class="synConstant">&#34;red&#34;</span>, <span class="synConstant">&#34;black&#34;</span>, <span class="synConstant">&#34;blue&#34;</span>, <span class="synConstant">&#34;white&#34;</span>, <span class="synConstant">&#34;black&#34;</span>, <span class="synConstant">&#34;blue&#34;</span>);

Map&#60;String, Integer&#62; counter = <span class="synStatement">new</span> HashMap<span class="synError">&#60;&#62;</span>();
<span class="synStatement">for</span>(String s : strs){
    Integer c = counter.get(s);
    <span class="synStatement">if</span>(c == <span class="synConstant">null</span>){
        c = <span class="synConstant">0</span>;
    }
    counter.put(s, c + <span class="synConstant">1</span>);
}
<span class="synStatement">for</span>(Map.Entry&#60;String, Integer&#62; me : counter.entrySet()){
    System.out.printf(<span class="synConstant">&#34;%s:%d%n&#34;</span>, me.getKey(), me.getValue());
}
</pre>

<br>

<p>先に、結果表示部分を見てみると、Map.Entryを取り出してループをまわしています。</p>
<p>実際にこのコードを書くとき、ここの型指定でいちいちMapが格納している型を確認するのが結構めんどくさかったりしました。Map.Entryって書くのも面倒です。</p>
<p>これが、forEachを使うと次のようになります。</p>
<pre class="syntax-highlight">
counter.forEach((k, v)<span class="synError"> -&#62;</span> {
  System.out.printf(<span class="synConstant">&#34;%s:%d%n&#34;</span>, k, v)
});
</pre>

<p>すっきり。</p>
<p>型推論してくれるので、counterが保持している型を改めて書く必要もないし、Map.Entryのように新しい型をもってくる必要もありません。</p>
<br>

<p>次に集計部分を見てみます。</p>
<p>ここでは、次のようにして、Mapから値をとりだして、値がなければ0を使うようにしています。</p>
<pre class="syntax-highlight">
Integer c = counter.get(s);
<span class="synStatement">if</span>(c == <span class="synConstant">null</span>){
    c = <span class="synConstant">0</span>;
}
</pre>

<br>

<p>これが、getOrDefaultを使うと次のように書けます。</p>
<pre class="syntax-highlight">
Integer c = counter.getOrDefault(s, <span class="synConstant">0</span>);
</pre>

<p>条件文がなくなりました！</p>
<br>

<p>では、for文を使っているところもforEachを使って書き直すと、結局こんなコードになります。</p>
<pre class="syntax-highlight">
Map&#60;String, Integer&#62; counter = <span class="synStatement">new</span> HashMap<span class="synError">&#60;&#62;</span>();
strs.forEach(s<span class="synError"> -&#62;</span> {
    Integer c = counter.getOrDefault(s, <span class="synConstant">0</span>);
    counter.put(s, c + <span class="synConstant">1</span>);
});
</pre>

<p>いい感じです。</p>
<br>

<p>ただ、Java8ではリストを集計するのにそもそもこんなコードは書く必要がなくて、コレクターを使って次のように書けます。</p>
<pre class="syntax-highlight">
Map&#60;String, Long&#62; counter = strs.stream()
    .collect(Collectors.groupingBy(s<span class="synError"> -&#62;</span> s, Collectors.counting()));
</pre>

<p>ここまでの話はなんだったんだろう？って感じですね。</p>
<br>

<h4> putIfAbsentとcomputeIfAbsent</h4>
<p>文字列を、頭文字ごとにリストに入れるというコードをJava7までの構文で書いてみると次のようになります。</p>
<pre class="syntax-highlight">
List&#60;String&#62; strs = Arrays.asList(<span class="synConstant">&#34;blue&#34;</span>, <span class="synConstant">&#34;red&#34;</span>, <span class="synConstant">&#34;black&#34;</span>, <span class="synConstant">&#34;blue&#34;</span>, <span class="synConstant">&#34;white&#34;</span>, <span class="synConstant">&#34;black&#34;</span>, <span class="synConstant">&#34;blue&#34;</span>);

Map&#60;String, List&#60;String&#62;&#62; words = <span class="synStatement">new</span> HashMap<span class="synError">&#60;&#62;</span>();
<span class="synStatement">for</span>(String str : strs){
    String initial = str.substring(<span class="synConstant">0</span>, <span class="synConstant">1</span>);
    <span class="synStatement">if</span>(!words.containsKey(initial)){
        words.put(initial, <span class="synStatement">new</span> ArrayList&#60;String&#62;());
    }
    List&#60;String&#62; ls = words.get(initial);
    ls.add(str);
}
<span class="synStatement">for</span>(Map.Entry&#60;String, List&#60;String&#62;&#62; me : words.entrySet()){
    System.out.println(me.getKey());
    <span class="synStatement">for</span>(String s : me.getValue()){
        System.out.println(<span class="synConstant">&#34;  &#34;</span> + s);
    }
}
</pre>

<br>

<p>ひとつ、Java8での目立たない改善点として、既存文法での型推論も強化されたということがあります。この部分、本当はArrayListに与える型は推論してほしいのですが、Java7ではメソッドの引数では型推論がきかないので、ダイヤモンド演算子ではなく型を明示する必要があります。</p>
<pre class="syntax-highlight">
words.put(initial, <span class="synStatement">new</span> ArrayList&#60;String&#62;());
</pre>

<p>ここが、Java8ではちゃんと型推論が効いて、ダイヤモンド演算子を使うことができるようになっています。</p>
<pre class="syntax-highlight">
words.put(initial, <span class="synStatement">new</span> ArrayList<span class="synError">&#60;&#62;</span>());
</pre>

<br>

<p>さて、putIfAbsentメソッド。これを使うと、Mapが値を保持していないときだけ値を設定するということが可能になります。</p>
<p>そうすると、次のように書けます。</p>
<pre class="syntax-highlight">
strs.forEach(str<span class="synError"> -&#62;</span> {
    String initial = str.substring(<span class="synConstant">0</span>, <span class="synConstant">1</span>);
    words.putIfAbsent(initial, <span class="synStatement">new</span> ArrayList<span class="synError">&#60;&#62;</span>());
    List&#60;String&#62; ls = words.get(initial);
    ls.add(str);
});
</pre>

<p>ついでにforEachを使っています。</p>
<br>

<p>ここで、ちょっと問題なのは、wordsがinitialに対応する値をもっている場合でも、ArrayListのオブジェクトが生成されてしまうことです。Javaは遅延評価ではないので、引数の値が実際には使われないとしても、メソッドを呼び出す前に評価されることになって、いちいちArrayListのオブジェクトが生成されるというわけです。</p>
<br>

<p>そこで、Mapの初期値を与えるというような場合には、computeIfAbsentメソッドを使うほうが適しています。</p>
<pre class="syntax-highlight">
strs.forEach(str<span class="synError"> -&#62;</span> {
    String initial = str.substring(<span class="synConstant">0</span>, <span class="synConstant">1</span>);
    List&#60;String&#62; ls = words.computeIfAbsent(initial, s<span class="synError"> -&#62;</span> <span class="synStatement">new</span> ArrayList<span class="synError">&#60;&#62;</span>());
    ls.add(str);
});
</pre>

<p>こうすると、wordsがinitialに対応する値を保持していないときだけlambda式で与えた関数が実行されて、必要なときにだけArrayListのオブジェクトが生成されます。</p>
<br>

<p>で、まあこういう風にリストを集計して個別のリストに振り分けるという場合も、コレクターが使えるので、そもそもこんなコードを書く必要はないのですね。</p>
<pre class="syntax-highlight">
Map&#60;String, List&#60;String&#62;&#62; words = strs.stream()
        .collect(Collectors.groupingBy(str<span class="synError"> -&#62;</span> str.substring(<span class="synConstant">0</span>, <span class="synConstant">1</span>)));
</pre>

<br>

<h4> computeIfAbsentを使ったメモ化再帰</h4>
<p>computeIfAbsentメソッドのドキュメントにも、よくある利用法のひとつとして「memoized result」つまりメモ化があげられてます。</p>
<p>で、まあメモ化というとメモ化再帰、メモ化再帰というとフィボナッチということで、フィボナッチ書いてみます。</p>
<pre class="syntax-highlight">
<span class="synType">public</span> <span class="synType">static</span> <span class="synType">void</span> main(String<span class="synIdentifier">...</span> args){
    IntStream.range(<span class="synConstant">1</span>, <span class="synConstant">101</span>).forEach(i<span class="synError"> -&#62;</span> {
        System.out.printf(<span class="synConstant">&#34;%d:%d%n&#34;</span>, i, fib(i))
    });
}
<span class="synType">public</span> <span class="synType">static</span> <span class="synType">long</span> fib(<span class="synType">int</span> n){
    <span class="synStatement">if</span>(n == <span class="synConstant">0</span>){
        <span class="synStatement">return</span> <span class="synConstant">0</span>;
    }<span class="synStatement">else</span> <span class="synStatement">if</span>(n == <span class="synConstant">1</span>){
        <span class="synStatement">return</span> <span class="synConstant">1</span>;
    }<span class="synStatement">else</span>{
        <span class="synStatement">return</span> fib(n - <span class="synConstant">2</span>) + fib(n - <span class="synConstant">1</span>);
    }
}
</pre>

<br>

<p>こうすると、40あたりで実行が遅くなって、50まではたどりつかないくらい重くなります。</p>
<p>これは、同じ値を何回も計算してるからなので、一度計算した値はキャッシュすることにして、次のように書き換えます。</p>
<pre class="syntax-highlight">
<span class="synType">private</span> <span class="synType">static</span> Map&#60;Integer, Long&#62; memo;

<span class="synType">public</span> <span class="synType">static</span> <span class="synType">void</span> main(String<span class="synIdentifier">...</span> args){
    memo = <span class="synStatement">new</span> HashMap<span class="synError">&#60;&#62;</span>();
    memo.put(<span class="synConstant">0</span>, <span class="synConstant">0L</span>);
    memo.put(<span class="synConstant">1</span>, <span class="synConstant">1L</span>);
    IntStream.range(<span class="synConstant">1</span>, <span class="synConstant">101</span>).forEach(i<span class="synError"> -&#62;</span> {
        System.out.printf(<span class="synConstant">&#34;%d:%d%n&#34;</span>, i, fib(i))
    });
}

<span class="synType">public</span> <span class="synType">static</span> <span class="synType">long</span> fib(<span class="synType">int</span> n){
    Long result = memo.get(n);
    <span class="synStatement">if</span>(result != <span class="synConstant">null</span>){
        <span class="synStatement">return</span> result;
    }
    result = fib(n - <span class="synConstant">2</span>) + fib(n - <span class="synConstant">1</span>);
    memo.put(n, result);
    <span class="synStatement">return</span> result;
}
</pre>

<br>

<p>このfibメソッドを、computeIfAbsentメソッドを使って書き換えると次のようになります。</p>
<pre class="syntax-highlight">
<span class="synType">public</span> <span class="synType">static</span> <span class="synType">long</span> fib(<span class="synType">int</span> n){
    <span class="synStatement">return</span> memo.computeIfAbsent(n, i<span class="synError"> -&#62;</span> fib(i - <span class="synConstant">2</span>) + fib(i - <span class="synConstant">1</span>));
}
</pre>

<p>いちぎょうだ！</p>
<p>computeIfAbsentメソッドのおかげで、メモ化がやりやすくなったことがわかります。</p>
<br>

<p>ところで、このフィボナッチ数列、93あたりで負の値がでてきて、なんだか怪しい感じです。longが桁あふれしてますね。</p>
<blockquote>
<p>・・・</p>
<p>90:2880067194370816120</p>
<p>91:4660046610375530309</p>
<p>92:7540113804746346429</p>
<p>93:-6246583658587674878</p>
<p>94:1293530146158671551</p>
<p>・・・</p>
</blockquote>
<br>

<p>Java8では、桁あふれで例外がでるような演算メソッドがMathクラスに用意されているので、これを使えば桁あふれが検出できるようになっています。</p>
<pre class="syntax-highlight">
<span class="synType">public</span> <span class="synType">static</span> <span class="synType">long</span> fib(<span class="synType">int</span> n){
    <span class="synStatement">return</span> memo.computeIfAbsent(n, i<span class="synError"> -&#62;</span> Math.addExact(fib(i - <span class="synConstant">2</span>),fib(i - <span class="synConstant">1</span>)));
}
</pre>

<br>

<p>こうすると、93を計算するときには、次のようにoverflowの例外が発生します。</p>
<blockquote>
<p>Exception in thread "main" java.lang.ArithmeticException: long overflow</p>
</blockquote>
<br>

<p>ほかにもJava8ではintやlongを符号なし整数として扱うメソッドもIntegerクラスやLongクラスにそれぞれ用意されています。</p>
<p>大きい数を計算するときにはありがたいですね。</p>
<br>

<p>メモ化フィボナッチの話は、こちらを参考にしています。</p>
<p><a href="http://blog.informatech.cr/2013/05/08/memoized-fibonacci-numbers-with-java-8/" target="_blank">Memoized Fibonacci Numbers with Java 8 | Informatech CR Blog</a></p>
<p>ここ、「そんなMapとか使わなくても変数２つあれば十分だよ」ってコメントついてて「たしかにそうだけど、これは再帰の効率化の例だからね」って返答してて、よくある光景だなーと思いました。</p>
</div>
]]></content:encoded>
<dc:creator>nowokay</dc:creator>
<dc:date>2013-05-23T16:40:33+09:00</dc:date>
<dc:subject>java</dc:subject>
<dc:subject>java8</dc:subject>
</item>
<item rdf:about="http://d.hatena.ne.jp/nowokay/20130522#1369202662">
<title>[java][java8]Java8のlambda構文がどのようにクロージャーではないか</title>
<link>http://d.hatena.ne.jp/nowokay/20130522#1369202662</link>
<description> Java8にlambda構文が入りましたが、これはクロージャーではない、とされています。 では、どのように「クロージャーではない」のか、ちょっと見てみます。 まず、lambdaを返すメソッドを定義します。 public static Supplier&#60;String&#62; createMessenger(String name, </description>

<content:encoded><![CDATA[
<div class="section">
<p>Java8にlambda構文が入りましたが、これはクロージャーではない、とされています。</p>
<p>では、どのように「クロージャーではない」のか、ちょっと見てみます。</p>
<br>

<p>まず、lambdaを返すメソッドを定義します。</p>
<pre class="syntax-highlight">
<span class="synType">public</span> <span class="synType">static</span> Supplier&#60;String&#62; createMessenger(String name, String address){
    <span class="synStatement">return</span> ()<span class="synError"> -&#62;</span> {
        <span class="synStatement">return</span> String.format(<span class="synConstant">&#34;私は%s、%sに住んでる&#34;</span>, name, address);
    };
}
</pre>

<a name="seemore"></a>

<br>

<p>呼び出すと、こんな感じでSupplierを受け取ります。</p>
<pre class="syntax-highlight">
Supplier&#60;String&#62; messenger = createMessenger(<span class="synConstant">&#34;きしだ&#34;</span>, <span class="synConstant">&#34;ふくおか&#34;</span>);
</pre>

<br>

<p>このSupplierを実行すると、次のようになります。</p>
<pre class="syntax-highlight">
System.out.println(messenger.get());
</pre>

<blockquote>
<p>私はきしだ、ふくおかに住んでる</p>
</blockquote>
<br>

<p>このSupplierが、name,addressの情報を保持しているように見えます。</p>
<br>

<p>lambda中では、次のようにして、lambdaの外側の変数を使っています。</p>
<pre class="syntax-highlight">
<span class="synStatement">return</span> String.format(<span class="synConstant">&#34;私は%s、%sに住んでる&#34;</span>, name, address);
</pre>

<br>

<p>lambdaがクロージャーであるかどうかという話では、どのようにしてlambdaの外側の変数を使っているかが大事になります。</p>
<p>クロージャーというのは、言語の実装でいえば、関数と変数環境をあわせたものです。変数環境というのは、変数を保持するテーブルだと考えるといいと思います。変数が使われるときに、変数の内容を取ってくるために使われるテーブルです。</p>
<p>具体的には、ここでのClosureケースクラスの定義がわかりやすいです。</p>
<p><a href="http://d.hatena.ne.jp/nowokay/20111115#1321333375" target="_blank">Scalaでパーサーを作ってみる〜10:レキシカルスコープとクロージャ - きしだのはてな</a></p>
<br>

<p>ここで、次のようにEnvironmentとFuncをまとめたものとしてClosureを定義しています。</p>
<pre class="syntax-highlight">
case class Closure(env: Environment, func: Func)
</pre>

<br>

<p>逆に言えば、関数と変数環境をあわせたものでなければ、クロージャーではないということになります。</p>
<p>つまり、Javaのlambdaは、関数と変数環境をあわせたものではないので、クロージャーではないということです。</p>
<br>

<p>ためしに、上記のlambdaを使ったコードのクラスファイルをjavapで逆コンパイルしてみると、次のようなメソッドが追加で定義されています。</p>
<pre class="syntax-highlight">
<span class="synType">private</span> <span class="synType">static</span> java.lang.String lambda$<span class="synConstant">0</span>(java.lang.String, java.lang.String);
</pre>

<br>

<p>lambda構文で引数をもたない関数を記述したのに、実際に生成されたメソッドは引数をふたつもつものになっています。そして、この引数に、lambdaの外側で定義した変数が渡されてきます。</p>
<p>Java8のlambdaでは、変数環境が渡されるわけではない、ということですね。</p>
<br>

<p>では、クロージャーじゃなければ何が困るかという話になります。</p>
<p>これは、もしlambda構文がクロージャーなら何ができるか考えてみるとわかります。クロージャーであれば変数環境が渡されてくるので、クロージャー内部から外側で定義した変数の内容を書き換えることができるはずです。</p>
<p>試しに先ほどのコードでlambda内部で変数nameの内容を書き換えてみます。</p>
<pre class="syntax-highlight">
<span class="synStatement">return</span> ()<span class="synError"> -&#62;</span> {
    name = name.trim();
    <span class="synStatement">return</span> String.format(<span class="synConstant">&#34;私は%s、%sに住んでる&#34;</span>, name, address);
};
</pre>

<p>そうすると、次のように「ラムダ式から参照されるローカル変数は、finalまたは事実上のfinalである必要があります」というエラーになります。</p>
<p><a href="http://f.hatena.ne.jp/nowokay/20130522144524" class="hatena-fotolife" target="_blank"><img src="http://cdn-ak.f.st-hatena.com/images/fotolife/n/nowokay/20130522/20130522144524.png" alt="f:id:nowokay:20130522144524p:image" title="f:id:nowokay:20130522144524p:image" class="hatena-fotolife"></a></p>
<br>

<p>lambdaで外側の変数を使うときには、final定義されているか、「事実上のfinal」つまり初期化以外では値が代入されていない変数である必要があります。</p>
<p>ということは、lambda内でlambda外の変数を使おうとすると、lambda内部だけではなくlambdaの外でも変数の値を書き換えてはいけないということです。</p>
<br>

<p>ただ、lambda内で使う外部の変数を書き換えてしまうと、感覚的ではない動きになることがあり、プログラムの不具合につながりがちです。そのため、実際問題としてはむしろlambda内で使う外部の変数は書きかえれないほうがよいとも言えます。</p>
<br>

<p>JavaScriptでよく見かける例に、次のようなものがあります。</p>
<pre class="syntax-highlight">
<span class="synStatement">for</span>(i = 1; i &#60;= 10; ++i)<span class="synIdentifier">{</span>
  button<span class="synIdentifier">[</span>i<span class="synIdentifier">]</span>.onclick = <span class="synIdentifier">function</span>()<span class="synIdentifier">{</span> <span class="synStatement">alert</span>(i + <span class="synConstant">&#34;番目のボタンがおされた&#34;</span>); <span class="synIdentifier">}</span>;
<span class="synIdentifier">}</span>
</pre>

<p>n番目のボタンが押されたとき「n番目のボタンがおされた」というメッセージを出したいのに、実際にはどのボタンを押しても「11番目のボタンがおされた」となってしまうものです。</p>
<br>

<p>Java8で書き換え可能な変数が使えたとすると次のような感じです。</p>
<pre class="syntax-highlight">
List&#60;Supplier&#60;String&#62;&#62; list = <span class="synStatement">new</span> ArrayList<span class="synError">&#60;&#62;</span>();
<span class="synStatement">for</span>(<span class="synType">int</span> i = <span class="synConstant">1</span>; i &#60;= <span class="synConstant">10</span>; ++i){
    list.add( ()<span class="synError"> -&#62;</span> String.format(<span class="synConstant">&#34;%d番目&#34;</span>, i));
}
list.forEach(s<span class="synError"> -&#62;</span> System.out.println(s.get()));
</pre>

<p>ぱっと見、「1番目」から「10番目」までが表示されそうです。</p>
<br>

<p>finalの制限は配列を使うことで回避可能なので、実際に実行可能なコードだと次のような感じになります。</p>
<pre class="syntax-highlight">
List&#60;Supplier&#60;String&#62;&#62; list = <span class="synStatement">new</span> ArrayList<span class="synError">&#60;&#62;</span>();
<span class="synStatement">for</span>(<span class="synType">int</span>[] i = {<span class="synConstant">1</span>}; i[<span class="synConstant">0</span>] &#60;= <span class="synConstant">10</span>; ++i[<span class="synConstant">0</span>]){
    list.add( ()<span class="synError"> -&#62;</span> String.format(<span class="synConstant">&#34;%d番目&#34;</span>, i[<span class="synConstant">0</span>]));
}
list.forEach(s<span class="synError"> -&#62;</span> System.out.println(s.get()));
</pre>

<p>実行してみると、「11番目」と10回表示されます。こういうコードが自然に書けてしまわないのは、いいことではないかと思います。</p>
<br>

<p>結論としては、「Java8のlambdaはクロージャーではないけど、クロージャーでやりたいことはできるし、やってはいけないことができないようになっているので、特に問題はない」と言えると思います。</p>
</div>
]]></content:encoded>
<dc:creator>nowokay</dc:creator>
<dc:date>2013-05-22T15:04:22+09:00</dc:date>
<dc:subject>java</dc:subject>
<dc:subject>java8</dc:subject>
</item>
</rdf:RDF>
