20200327のJavaに関する記事は15件です。

【Java】Listの使い方【ArrayList】

Listとは?

複数の要素を含み、順序のつけられたコレクションのこと。
インターフェースのため、実装することで使用可能となる。

コレクションとはなんぞや?ってことで調べてます。

コレクション:オブジェクトの集合を扱うための仕組み。以下の種類がある。

  • List系

    • ArrayList  配列を扱う。
    • LinkedList  配列を扱う。挿入・削除が高速。
    • Vector   配列を扱う。パフォーマンスが悪いため現在ではあまり推奨されない。
  • Set系

    • HashSet   値の重複を許さない順不同の要素集合。
    • TreeSet  値の重複を許さないソートされたの要素集合。
  • Map系

    • HashMap  キーと値の組からなる要素の集合。
    • TreeMap   キーと値の組からなる要素の集合。キーでソートされている。

今回はArrayListを使用してみます。

ArrayListの使用例

listtest.java
package listtest;

import java.util.ArrayList;
import java.util.List;

public class Listtest {

    public static void main(String[] args) {

        List<String> list = new ArrayList<String>();

        list.add("りんご");
        list.add("みかん");
        list.add("メロン");

        System.out.println(list);

    }
}
実行結果.
[りんご, みかん, メロン]

ArrayListは実装クラスのため、以下のようにインスタンスが作成できます。

List<String> list = new ArrayList<String>();

実は以下のものと同様の内容となっています。
listにStringクラスのオブジェクトを詰め込んでるということですね。

listtest.java
        List<String> list = new ArrayList<String>();

        list.add(new String("りんご"));
        list.add(new String("みかん"));
        list.add(new String("メロン"));

        System.out.println(list);

ある一つの要素を取り出したい場合はgetメソッドを利用します。引数にはリストの取り出したい要素の番号を入れます。

listtest.java
        List<String> list = new ArrayList<String>();

        list.add(new String("りんご"));
        list.add(new String("みかん"));
        list.add(new String("メロン"));

        System.out.println(list.get(1));
実行結果.
みかん

要素が取り出せました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Scope(スコープ)

Scope(スコープ)

インスタンスを保存できる領域。
スコープを経由させることにより、サーブレットクラスとJSPファイルの間でインスタンスを共有させることが可能になる。

JavaBeans

スコープに保存する再利用しやすくするルールに基づいたクラス(インスタンス)

直列化

プロパティ

getter/setter

リクエストスコープ

レスポンスが返されるまで利用可能
HttpServletRequest

リクエストスコープにインスタンス生成

記述
request.setAttribute("属性名", インスタンス);

リクエストスコープからインスタンスを取得

記述
取得するインスタンスの型 変数名 = (取得するインスタンスの型) request.getAttribute("属性名");

セッションスコープ

保存インスタンスの有効期限は開発者が設定
リクエストをまたいでの使用可能

セッションスコープの取得

記述
HttpSession session = request.getSession();

セッションスコープに保存

記述
session.setAttribute("属性名", インスタンス);

セッションスコープからインスタンスを取得

記述
取得するインスタンスの型 変数名 = (取得するインスタンスの型) session.getAttribute("属性名");

セッションスコープからインスタンスを削除

記述
session.removeAttribute("属性名");

セッションスコープを破棄

記述
session.invalidate();

アプリケーションスコープ

アプリケーション終了まで利用可能
高速アクセス

アプリケーションスコープの取得

記述
ServletContext application = this.getServletContext();

アプリケーションスコープに保存

記述
appliction.setAttribute("属性名", インスタンス);

アプリケーションスコープからインスタンスを取得

記述
取得するインスタンスの型 変数名 = (取得するインスタンスの型) application.getAttribute("属性名");

アプリケーションスコープからインスタンスを削除

記述
application.removeAttribute("属性名");
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MVCモデル

MVCモデル

M...Model(モデル)

Javaクラス
アプリケーションの処理(計算処理等)、データ格納

V...View(ビュー)

JPSファイル
画面表示

C...Controller(コントローラー)

サーブレットクラス
要求の受け取り、処理実行依頼(Model)、結果表示依頼(View)

転送処理

フォワード

処理を他のサーブレットクラス、JPSクラスに移す(内部)
URLがリクエスト時の状態

記述
RequestDispatcher dispatcher = request.getRequestDispatcher("フォワード先");
dispatcher.forward(request, response);

リダイレクト

処理を他のサーブレットクラス、JPSクラスに移す(外部)
URLがリダイレクト先のものに変更

記述
response.sendReddirect("リダイレクト先のURL");
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

From(フォーム)

From(フォーム)

webページに入力したデータをサーバーサイドプログラムに送信
入力項目のまとまり

記述
<form action="送信先" method="リクエストメソッド">...</from>

リクエストメソッド

GETリクエスト
新しい情報(Webページ等)を取得

POSTリクエスト
フォームに入力した情報を登録

テキストボックス

記述
<input type="text" name="部品名">

パスワード

記述
<input type="password" name="部品名">

ラジオボタン

記述
<input type="radio" name="部品名" value="値">

送信ボタン

記述
<input type="submit" value="送信">

リクエストパラメータ取得方法

APサーバによってHttpServletRequestインスタンスに格納されて送信先のサーブレットクラスまたはJSPファイルに渡される

文字コードの指定

記述
request.setCharacterEncoding("送信元HTMLの文字コード");

取得

記述
String xxx = request.getParameter("リクエストパラメータの名前");
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gradleってなんだろう?

javaのビルド周りに興味をもったので、その時のメモ。

Gradleとは

javaのビルドツールで jarファイル(javaのバイトコードファイルや画像をzipファイル形式で一つにまとめたもの)
へのビルドをしてくれます。
mavenと違い、設定ファイルはgrovyという言語で書きます。

また、gradleを毎回インストールすることがないように、gradle wrapper というものを作り、
これを配布することで、gradleをインストールしていない環境でもgradle を使うことが出来ます。

Gradle 初期化

簡単なgradleプロジェクトを作ります。

gradle init

これで色々ファイルが作られます。具体的には以下の感じ

├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle

依存性解決

gradleはjavaの外部のパッケージを管理して、自身ののプロジェクトでそのパッケージを使えるようにしてくれます。

dependencies {
    implemention("パッケージ名")
}

タスク

gradleにおけるタスクとは,公式から引用すると、ビルドを行うための細かい作業とのことです。

組み込みのタスク

このタスクはgradleのプロジェクトが生成された時点でいくつか作られています。
例えば、テスト、ビルド、実行などです。

これらは、

gradle tasks

で一覧を見ることが出来ます。

タスクの定義

これは、自分で定義できるので、簡単なタスクを定義してみましょう。

task hello {
    println("hello")
}

実行してみよう。

gradle hello

#以下のが表示されるはず
#hello
#:hello UP-TO-DATE

こんな感じでタスクを作って、内部でプラグインの設定や特定のタスクのみを束にしておくことが出来ます。

タスクをアクションで構成する

タスクは自分が実行スべきタスクをアクションという単位で認識していて、それを配列として順次実行していきます。

アクションは
doFirst:先頭に追加する
doLast: 末尾に追加する

といった形で追加していきます。

task hello{
    hello.doFirst {
        println "hello1"
        println "hello2"
    }
    hello.doLast {
        println "hello3"
        println "hello4"
    }
}

これを実行すると

gradle hello

結果は

:hello
hello1
hello2
hello3
hello4

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed

となる。

こんな形で実行順序を制御して、処理順序をカスタマイズすることができます。

gradleのtips

これまででざっくりとgradleの使い方を触ってきましたが、今度は各々のタスクが早くなる設定や分析ための便利な機能を見ていきたいと思います。

並列化

graleは基本的にタスクを一つずつしか実行していきません。ですが、
並列実行を強制する設定があります。
なお、これは完全に独立しているタスクを並列実行するので、依存関係があるものに関してはこれまでどおり逐次実行されます。

gradle.propertiesに以下を追加します。

org.gradle.parallel=true

プロファイリング

gradleにはタスク実行時にどのくらい時間を使ったか、並列実行したかなどをプロファイリングして、視覚化してくれる機能があります。
これはタスクの実行時に --scan をつければ、実行後にCLIにURLが表示され、そこから確認ができます。

ただ、これにはgradleでメールアドレスの登録などが必要なため、情報管理的な部分を考える必要があります。

screenshot-scans.gradle.com-2020.03.20-12_00_39.png

このプロファイリングは結構便利で、テストの重い部分やサジェストなど、ビルドの効率化に重要な情報を得ることができます。

参考

https://qiita.com/opengl-8080/items/0a192b62ee87d8ac7578#%E3%82%BF%E3%82%B9%E3%82%AF%E3%81%AE%E5%8F%82%E7%85%A7

https://guides.gradle.org/performance/

最後に

gradleは設定ファイルが、独自言語だったりしますが、その言語にさえ慣れてしまえば設定は読みやすいし、
タスクの設定も細かく行える印象です。

また、ビルド時のプロファイリングなど、検証と改善の昨日も揃っているところもいいです。

それでは良いgradleライフを

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JPS(JavaServer pages)

JPS(JavaServer pages)

リクエストされるとサーブレットクラスに変換

スクリプトレット

Javaのコードを埋め込む

記述方法
<% Javaのコード %>

スクリプト式

変数・メソッド・戻り値などを出力する

記述方法
<%= Javaのコード %>

JSPコメント

コメントの記述

記述方法
<%-- コメント --%>

pageディレクティブ

JPSファイルの設定

記述方法
<%@ page 属性名="値" %>

URL

記述方法
http://<サーバ名>/<アプリケーション名>/<WebContentからのパス>

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

非staticネストクラスが握っちゃう、エンクロージングオブジェクトの暗黙的参照

この記事は、Javaにおける「非staticネストクラスが、エンクロージングオブジェクトを暗黙的参照として握っちゃうから、気を付けましょう」について述べます。

なぜ、気を付けなければならないのか、と言えば、「メモリーリークを引き起こす可能性を高めてしまうから」です。

そして結論は、「ネストクラスは、static修飾子を付けた方がイイ(さすれば、エンクロージングオブジェクトを暗黙的参照として握ることはないから)」です。エンクロージングオブジェクトは短命で、ネストクラスのインスタンスの方が長命である場合は特にです!

事のきっかけはAndroidアプリ開発時

(この記事は、Androidアプリ開発に特化したものではありません。が、読み進めていけばJavaに関わることになっていきますので、当初は我慢してお読みください)

ある日のことでございます。AndroidアプリをJavaで作っていた時に、Android Studio1が、こんな警告を出してきました。

thisasynctaskclassshouldbestaticorleaksmightoccur.png

This 'AsyncTask' class should be static or leaks might occur

CouponAsyncTaskと名付けたこのクラスは、ネストクラス(インナークラス・内部クラス)として定義しているのですが、「static修飾子を付けなさいよ、さもなければメモリリークを引き起こすかもよ」と吹き出しに書かれています。まあ、怖い。

Android Studioが備えているコードチェック機能「Lint」が教えてくれる詳しい説明を見ますと、

Static Field Leaks
A static field will leak contexts.
Non-static inner classes have an implicit reference to their outer class. If that outer class is for example a Fragment or Activity, then this reference means that the long-running handler/loader/task will hold a reference to the activity which prevents it from getting garbage collected.
Similarly, direct field references to activities and fragments from these longer running instances can cause leaks.
ViewModel classes should never point to Views or non-application Contexts.
Issue id: StaticFieldLeak

Google翻訳に頼んでみます。

静的フィールドリーク
静的フィールドはコンテキストをリークします。
非静的内部クラスには、外部クラスへの暗黙的な参照があります。 その外部クラスがたとえばフラグメントまたはアクティビティである場合、この参照は、長時間実行されるハンドラー/ローダー/タスクがガベージコレクションの取得を妨げるアクティビティへの参照を保持することを意味します。
同様に、これらのより長時間実行されているインスタンスからのアクティビティおよびフラグメントへの直接フィールド参照は、リークを引き起こす可能性があります。
ViewModelクラスは、ビューまたは非アプリケーションコンテキストを指すことはありません。
問題ID:StaticFieldLeak

難しい英文を、難しい日本語に翻訳されてもねえ...チンプンカンプンです。

開発者サイトに書いてありました

ちゃんとAndroidアプリ開発者向け公式サイトに「スレッド化されたオブジェクトを使ったコード設計によく見られる欠陥の例」として掲載されていました。あるあるなんでしょうね。
スレッド化によるパフォーマンスの向上[非明示的参照]

この公式サイトの日本語ページでは「非明示的参照」と訳されていますが、原文は「Implicit references」と書かれています。
当記事では「暗黙的参照」と五字熟語で語っていきます。

ネストクラス

いきなり「非staticネストクラスが握っちゃう、エンクロージングオブジェクトの暗黙的参照」について語ろうにも、このお題目に乗っかっている情報量が多いので、前提知識を一つ一つ説明していきます。
急がば回れ、です。ただ、ちょっともどかしいかもしれませんが。

「ネストクラス(nested class)」は、「インナークラス(inner class)」とも呼ばれます。当記事は「ネストクラス」で統一します。

Fooクラスには2つのネストクラスがある
public class Foo {
    class Bar {
        void methodBar() {
            System.out.println("methodBar");
        }
    }

    static class Baz {
        void methodBaz() {
            System.out.println("methodBaz");
        }
    }

    void methodFoo() {
        System.out.println("methodFoo");
    }
}

この場合、

  • BarクラスとBazクラスは、「Fooクラスのネストクラス」と言います。
  • Fooのことは、「Barクラスのエンクロージングクラス」と言います。

「エンクロージングクラス(enclosing class)」は、「アウタークラス(outer class)」とも呼ばれます。当記事は「エンクロージングクラス」で統一します。

BarとBazの違いは、static修飾子が付いているか否かなので、

  • Barクラスは、「Fooクラスの非staticネストクラス」と言います。面倒くさいと思う人は「Fooクラスのネストクラス」で済ませてしまいますが。
  • Bazクラスは、「Fooクラスのstaticネストクラス」と言います。

ネストクラスのインスタンスを生成するには

Fooの2つのネストクラスのインスタンスを生成する
class Main {
    public static void main(String[] args) {
        Foo foo = new Foo();
        Foo.Bar bar = foo.new Bar();

        Foo.Baz baz = new Foo.Baz();
    }
}

非staticネストクラスのインスタンスを生成する場合

Barクラスのインスタンスを生成するには、2行を要しました。どうしても1行で済ませたい場合は、以下のコードとなります。

1行でBarクラスのインスタンスを生成
Foo.Bar bar = new Foo().new Bar();

いずれにせよ、非staticネストクラスのインスタンスを生成する場合は、エンクロージングのインスタンスが必要です。
そして、new演算子の前に.が付くのも特徴的です。

staticネストクラスのインスタンスを生成する場合

事前にエンクロージングのインスタンスなんて要りません。いきなりnewできます。

特徴的なのは、クラス名が「Baz」ではなく、「Foo.Baz」という名前だということです。クラス名の合間に.が含まれているのが特徴的です。

書籍「Effective Java」に当たる

Effective javaは、第1版(2001年刊行)から「非staticのメンバークラスよりもstaticのメンバークラスを選ぶ」べしとのたまっております。それは2018年に刊行された第3版ですら残っている御成敗式目です。

メモリリークは壊滅的になり得ます。

とか 

メモリリークの検出がたいてい難しいです。

とか

余分な参照を保持しメモリと時間を無駄に使います。

など、怖いことが書かれています。なぜなら、

static修飾子を省略すると、個々のインスタンスはエンクロージングオブジェクトへの関係のない参照を持ってしまいます。その参照を保存することは時間とメモリを必要とします。深刻なのは、その参照がなければガベージコレクションの対象になる場合、そのエンクロージングインスタンスが残ってしまう可能性があることです。

と書かれてあります。

エンクロージングのインスタンスはもう不要なのに、天国に召されない

前出のプログラムをちょっと改変してみました。staticネストクラスであるBazはちょっと退かせました。

public class Main {
    public static void main(String[] args) {
        Foo foo = new Foo();
        Foo.Bar bar = foo.new Bar();

        foo = null;
        System.gc();
//        foo.methodFoo(); // NullPointerException発生
        bar.methodBar();
    }
}

エンクロージングクラスのFooのインスタンスにnullを代入してガベージコレクション(GC)を実行させてから、非staticネストクラスのBarインスタンスにメソッドを呼び出してみました。実行してみますと、以下の通りです。

実行結果
methodBar

へえ、ちゃんと動くんだぁ!と私は瞠目してしまいました。

Javaの仕様としては、私がいくらエンクロージングクラスのFooのインスタンスにnullを代入しても、その非staticネストクラスのBarインスタンスが動く限り、fooと名付けたオブジェクトはGCされない(GC対象と見做されず華麗にスルーされた)のです。なぜなら、 barfooを暗黙的参照として保持しているから です。

ファニーな例えとしてイラストにすると、こんなかんじでしょうか。

joubutudekinai.png

階段を一歩ずつ登っていってる女性が、barで、元気いっぱいです。

一方、成仏したがっているお爺ちゃんがfooなんですが、barが握ってるのでね、なかなか天国へ召されずにいるのですよ。地縛霊になりかねません。

  • 握っている=参照している
  • 天国へ召される=GCによりメモリから解放される
  • 地縛霊=メモリリーク

で読み替えてください。

では、短絡的にネストクラスにstaticを付けるだけで万事解決!といくのか?

FooとかBarとかBazの題材をやめて、以下のプログラムを見てください。

ビフォアー:ネストクラスが非staticだ
class Fizz {
    private Integer x = 12;
    private static Integer y = 34;

    class Buzz {
        void m() {
            x = 56;
            y = 78;
            System.out.println(x + y);
        }
    }
}

class FizzBuzzMain {
    public static void main(String[] args) {
        Fizz.Buzz fb = new Fizz().new Buzz();
        fb.m();
    }
}
ビフォアーの実行結果
134

(わざとらしく)やるなっつってんのに非staticネストクラスですよ。では、安直にこのBuzzと名付けたネストクラスにstatic修飾子を付けてあげましょう。

アフター
class Fizz {
    private Integer x = 12;
    private static Integer y = 34;

    static class Buzz {
        void m() {
            x = 56; // xがstaticフィールドではないのでコンパイル不可
            y = 78;
            System.out.println(x + y); // xがstaticフィールドではないのでコンパイル不可
        }
    }
}

class FizzBuzzMain {
    public static void main(String[] args) {
        Fizz.Buzz fb = new Fizz.Buzz();
        fb.m();
    }
}

途端にコンパイルできなくなりました。xがstaticフィールドではないために、ダメだとIDEが怒り出します。

なぜなのか。それは、 staticネストクラスは、エンクロージングのstaticメンバーにのみアクセス可能 だからです。

また再びのAndroidアプリ開発の話に戻ります

作りたかったAndroidアプリは、このアニメーションGIFをご覧ください。

countup.gif

起動したとたんに、勝手にカウントアップが始まります。このアニメーションGIFは、3まで至ったらまたSTART!を繰り返しているアニメーションですが、本当はこのアプリはSTART!から始まったら「9」まで数え上げたらお終い(「9」を出力したままキープ)なんです。

そしてそのプログラムは以下の通りです。Androidアプリ開発未経験者の方も我慢しておつきあいください。

画面のクラス
public class MainActivity extends AppCompatActivity {

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.text_view);
        new MyTask().execute();
    }

    class MyTask extends AsyncTask<Void, String, Void> {
        @Override
        protected Void doInBackground(Void... voids) {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(1000);
                    publishProgress(String.valueOf(i));
                } catch (InterruptedException e) {
                    Log.e("MyTask", e.getMessage(), e);
                }
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(String... values) {
            textView.setText(values[0]);
        }
    }
}

Androidで「画面のクラス」を作りたければ、AppCompatActivityを継承したクラスをコーディングしはじめます。

TextViewというクラスは、画面中央に配置して「START!」か「0」とか「1」とか表示している、文字列を表示する(ことしかできない)ビュー(部品)です。
このTextViewを非同期処理で「0」を「1」に、そして「2」へと1000ミリ秒きざみで変えさせたい。これがこのアプリの要件です。

非同期処理をしたければ、AsyncTaskのサブクラスを定義して、それにその処理をコーディングしておきます。

MainActivity_MyTask.png

その非同期処理をするMyTaskexecute()メソッドを呼び出すと、MyTaskが処理を始めます。
この画像の赤い囲みがTextViewです。MyTaskがこのTextViewに、"0"とか"1"とか"2"とか文字列を上書きします。これでカウントアップするように見せかけています。

それでは、MyTaskクラスにstaticを付けてみます。

Non-static field.png

IDEが真っ赤に怒り出しました。なぜならそのtextViewと名付けたフィールド、static修飾子を付けてませんからね!

MainActivityのインスタンスはもう不要なのに、天国に召されない

前出の階段を元気に登っている女性と地縛霊のお爺ちゃんのイラストを今一度見てください。

Androidでは、不要になった画面は、GCされます。「不要になった画面」とは、ユーザが戻るボタンを押すなどしてその画面を非表示にした(消しちゃう)時のことをいいます。
一般的にAndroid端末はPCと比較してメモリが少ないです2。なのでユーザが非表示にした画面=Activityクラスのインスタンスはランタイムにより速やかにGCされます。

ユーザが気軽に戻るボタンを押しちゃう、という所作ひとつで、その画面=Activityインスタンスは不要!んじゃGC!と短命なりにし運命になりがちです。
しかして、バックグラウンドで(否、「バックグラウンド」という用語は正鵠を射てなくて、正確には「ワーカースレッドにおいての非同期処理で」と言った方が適切です)MyTaskは、己の仕事(カウントアップ)を達成しきるまで生きることになります。

この、生存期間の比較で【 エンクロージングインスタンス < ネストクラスのインスタンス 】であることを危惧して警告を出してくれたのがAndroid StudioというIDEなのです。

この私が作ったアプリを起動して、カウントアップがMyTaskの仕業により始まっても、ユーザがもういいやと戻るボタンを押すなどして画面を非表示にする(消す、アプリをやめる)としても、 非staticネストクラスのインスタンスであるMyTaskオブジェクトは、MainActivityインスタンスの暗黙的参照を保持している ので、とっくに表示されてないんだからとっととGCされちゃえばいいのに、GCされない状態に陥ります。
ユーザが見えてないところで、MyTaskは脈々とカウントアップのお仕事をしているのです。
イラストの、女性であるMyTaskは1段1段階段を登って。でもその右手には、成仏できないお爺ちゃんMainActivityをしっかり握っちゃってて。ユーザはこのお爺ちゃんが見えてないのに。

解決したくとも、にっちもさっちも

私はここでジレンマに我が身が嘖(さいな)まれるのです。

  • このMyTaskは、このMainActivityでしか使われないクラスだから、ネストクラスにしたい。
  • しかしstaticネストクラスにせよとIDEが警告を出す。
  • ところがTextTview型のフィールドはstaticフィールドにはしたくない(すべきではないので)。
  • となると、このMyTaskとこのMainActivityは、別クラスに分けるか。
  • でもそうなると、TextTviewの参照の受け渡しが面倒だ(今度はこの受け渡しでヘタこくとTextTviewがメモリリークのタネとなりかねん)。
  • TextTviewを持っているのは(持つべきなのは)MainActivityだし、そのTextTviewの文字列を変えるのはMyTaskだ。
  • だったらやっぱり、このMyTaskは、このMainActivityのネストクラスにしたい。
  • java.lang.ref.WeakReference<T>を使うか...。

ずっとずっとこれに悩んでいます。どこかでなにかを妥協しなければいけないのかもしれません。

以上です。なんだかこれで終わりにしていいのか、良心の呵嘖(かしゃく)に悩む私に、福沢諭吉先生の御言葉でこの記事を綴じたいと思ひます。

されども不良の子に窘(くる)しめらるるの苦痛は、地獄の呵嘖よりも苦しくして、然(しか)も生前現在の身を以てこの呵嘖に当たらざるを得ず。

福沢諭吉「教育の事」1878(明治11)年

ちょっとユキチよ、「地獄の呵責よりも苦しくて」はシビアすぎて引くわ。
やめた。和辻先生に縋(すが)ることにします。

かく道元の説く慈悲の前には、「悪は必ずしも呵嘖すべきものでない」。

和辻哲郎「日本精神史研究」1922(大正11)年

以上です。


  1. IntelliJ IDEAを改造して作られたAndroidアプリ開発専用IDE 

  2. 10GBのRAMを搭載しているAndroid”ゲーミング”スマホとか市販されてますが、まあそれは置いておいて。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サーブレット

サーブレット

サーバーサイドプログラムを作るための技術
サーバレットクラスを作りAPサーバ上で実行する
ブラウザからのリクエストによって実行され、結果をHTMLで出力しAPサーバによってブラウザにレスポンスする

doGet()メソッド

サーブレットクラスがリクエストされると実行されるメソッド

HttpServletRequest

ブラウザから届いたリクエストに関する情報と機能を持つインスタンス

HttpServletResponse

サーバから送り出すレスポンスに関する情報と機能を持つインスタンス

Content-Typeヘッダ

URL

Java
http://<サーバー名>/<アプリケーション名>/<URLパターン>

@WebServlet("/URLパターン")で設定

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Servlet(サーブレット)

Servlet(サーブレット)

サーバーサイドプログラムを作るための技術
サーバレットクラスを作りAPサーバ上で実行する
ブラウザからのリクエストによって実行され、結果をHTMLで出力しAPサーバによってブラウザにレスポンスする

doGet()メソッド

サーブレットクラスがリクエストされると実行されるメソッド

HttpServletRequest

ブラウザから届いたリクエストに関する情報と機能を持つインスタンス

HttpServletResponse

サーバから送り出すレスポンスに関する情報と機能を持つインスタンス

Content-Typeヘッダ

URL

Java
http://<サーバー名>/<アプリケーション名>/<URLパターン>

@WebServlet("/URLパターン")で設定

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】範囲チェックの実装

範囲チェックの実装、どうしてる?

数値やら文字の範囲チェックって、obj.compareTo(value)の組み合わせで書いてあることが多いですよね。
ですがそのcompareToの戻り値のパターンがどうしても覚えられないんです…
覚えてるのは、一致が「0」ということだけ。> 0< 0のどっちが小さい/大きいなのか、毎回実行して確認しています…
でも、compareToメソッドや等号・不等号演算子による「最小値以上かつ最大値以下」の範囲チェックを書かなくて済む方法があったんです。

やはりApacheCommonsはぐう優秀

org.apache.commons.lang3.Rangeクラス(結構昔からあるのに無知でした)がポンコツSEの救世主でした…これは愛さざるを得ない。
「java 範囲 チェック」でググると、等号・不等号演算子やcompareToによる実装が検索結果の上位にいるので、「commons」とかキーワードをちょっと工夫しないとなかなか辿り着けないんですよね、Rangeクラスには。

サンプルコード

int num0 = 0;
int num1 = 1;
int num2 = 2;

// 範囲オブジェクトを作成
Range rng = Range.between(num0, num2);

System.out.println("範囲内チェック(" + num1 + ") = " + rng.contains(num1));
System.out.println("範囲内チェック(" + -1 + ") = " + rng.contains(-1));

// Rangeなら範囲チェックだけじゃなくて、いろいろできます
System.out.println("範囲の最小値 = " + rng.getMinimum() + "、範囲の最大値 = " + rng.getMaximum());
System.out.println("範囲が引数より後かチェック(" + -1 + ") = " + rng.isAfter(-1));
System.out.println("範囲が引数より後かチェック(" + num0 + ") = " + rng.isAfter(num0));

実行結果

範囲内チェック(1) = true
範囲内チェック(-1) = false
範囲の最小値 = 0、範囲の最大値 = 2
範囲が引数より後かチェック(-1) = true
範囲が引数より後かチェック(0) = false

上記サンプルの他、isBeforeやcompareTo的な戻り値を返すelementCompareTo等いろんなメソッドが実装されています。
ちなみにbetweenの最小と最大の値の型が不一致の場合はコンパイルエラー、片方でもnullの場合はIllegalArgumentExceptionが発生します。
範囲とした型とcontainsの引数の型が合わない場合、ClassCastExceptionが発生します。

ジェネリクスの恩恵

RangeクラスはcompareToからの脱却というだけで最高なんですけど、素晴らしいのはそれだけじゃないんです。
上記の例はプリミティブのint型(Integerへの変換不要)でしたが、例えばStringだってLocalDateだって渡せちゃう!Rangeまじ愛してる。

LocalDate ld0 = LocalDate.now().minusDays(1);
LocalDate ld1 = LocalDate.now();
LocalDate ld2 = LocalDate.now().plusDays(1);

Range rng = Range.between(ld0, ld2);
System.out.println(rng.contains(ld0)); // true
System.out.println(rng.contains(ld1)); // true
System.out.println(rng.contains(ld2)); // true
System.out.println(rng.contains(ld2.plusDays(1))); // false
System.out.println(rng.isAfter(ld0.minusDays(1))); // true
System.out.println(rng.contains(null)); // false

もっと早く知りたかった…最の高じゃん…

Range.betweenには、Comparatorクラスのオブジェクトを渡すこともできるので、自作オブジェクトの比較も可能です。
もうほんと最高of最高。ポンコツ具合がちょっと改善される気がします。

compareToの戻り値が覚えられないポンコツSEはそんなに多いわけではないと思うのですが、Rangeクラスを使うほうが見た目もすっきりわかりやすいと思います。contains使うと論理演算子も不要ですし。
なので、これからの範囲チェックの実装にはぜひRangeクラスを使っていただきたいなと思うのです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】compareToが不要な範囲チェック

範囲チェックの実装、どうしてる?

数値やら文字の範囲チェックって、obj.compareTo(value)の組み合わせで書いてあることが多いですよね。
ですがそのcompareToの戻り値のパターンがどうしても覚えられないんです…
覚えてるのは、一致が「0」ということだけ。> 0< 0のどっちが小さい/大きいなのか、毎回実行して確認しています…
でも、compareToメソッドや等号・不等号演算子による「最小値以上かつ最大値以下」の範囲チェックを書かなくて済む方法があったんです。

やはりApacheCommonsはぐう優秀

org.apache.commons.lang3.Rangeクラス(結構昔からあるのに無知でした)がポンコツSEの救世主でした…これは推せる。
「java 範囲 チェック」でググると、等号・不等号演算子やcompareToによる実装が検索結果の上位にいるので、「commons」とかキーワードをちょっと工夫しないとなかなか辿り着けないんですよね、Rangeクラスには。

サンプルコード

int num0 = 0;
int num1 = 1;
int num2 = 2;

// 範囲オブジェクトを作成
Range rng = Range.between(num0, num2);

System.out.println("範囲内チェック(" + num1 + ") = " + rng.contains(num1));
System.out.println("範囲内チェック(" + -1 + ") = " + rng.contains(-1));

// Rangeなら範囲チェックだけじゃなくて、いろいろできます
System.out.println("範囲の最小値 = " + rng.getMinimum() + "、範囲の最大値 = " + rng.getMaximum());
System.out.println("範囲が引数より後かチェック(" + -1 + ") = " + rng.isAfter(-1));
System.out.println("範囲が引数より後かチェック(" + num0 + ") = " + rng.isAfter(num0));

// 変数に格納しなくてもメソッドチェーンでも可
System.out.println("範囲内チェック(" + num1 + ") = " + Range.between(num0, num2).contains(num1));

実行結果

範囲内チェック(1) = true
範囲内チェック(-1) = false
範囲の最小値 = 0、範囲の最大値 = 2
範囲が引数より後かチェック(-1) = true
範囲が引数より後かチェック(0) = false
範囲内チェック(1) = true

上記サンプルの他、isBeforeやcompareTo的な戻り値を返すelementCompareTo等いろんなメソッドが実装されています。
ちなみにbetweenの最小と最大の値の型が不一致の場合はコンパイルエラー、片方でもnullの場合はIllegalArgumentExceptionが発生します。
また、betweenに渡す最小値と最大値の順番は関係なく、between(最大値, 最小値)でも、正しく範囲は設定されます。(getMaximumgetMinimumの値は崩れない)
範囲とした型とcontainsの引数の型が合わない場合は、ClassCastExceptionが発生します。

ジェネリクスの恩恵

RangeクラスはcompareToからの脱却というだけで最高なんですけど、素晴らしいのはそれだけじゃないんです。
上記の例はプリミティブのint型(Integerへの変換不要)でしたが、例えば文字列型だって日付や時刻型だって渡せちゃう!Rangeまじ愛してる。

// 日付型(LocalDate)のサンプル
LocalDate ld0 = LocalDate.now().minusDays(1);
LocalDate ld1 = LocalDate.now();
LocalDate ld2 = LocalDate.now().plusDays(1);

Range rng = Range.between(ld0, ld2);
System.out.println(rng.contains(ld0)); // true
System.out.println(rng.contains(ld1)); // true
System.out.println(rng.contains(ld2)); // true
System.out.println(rng.contains(ld2.plusDays(1))); // false
System.out.println(rng.isAfter(ld0.minusDays(1))); // true
System.out.println(rng.contains(null)); // false

もっと早く知りたかった…最の高じゃん…

betweenには、Comparatorクラスのオブジェクトを渡すこともできるので、自作オブジェクトの比較も可能です。
もうほんと最高of最高。ポンコツ具合がちょっと改善される気がします。

compareToの戻り値が覚えられないポンコツSEはそんなに多いわけではないと思うのですが、Rangeクラスを使うほうが見た目もすっきりわかりやすいと思います。contains使うと論理演算子も不要ですし。
なので、これからの範囲チェックの実装にはぜひRangeクラスを使っていただきたいなと思うのです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】「final変数」と「不変オブジェクト」の違い

どーも、ふぎとです。
今回はfinal変数と不変オブジェクトについて
まとめてみました。

final変数

final変数とは、final修飾子を指定した変数のこと。
その性質は「変更不可・読み込み専用」と呼ばれる
が、厳密には「再代入不可の変数」。

    final int i = 3;
    i = 5; //再代入によるコンパイルエラー

上では、基本型(int型)変数iにfinalを指定している。
基本型変数は、変数の値に「値そのもの」が保持される
変数だ(詳細は『基本型変数の代入と参照型変数の代入
の違い
』を参照)。そのため、「値そのもの」を入れ替
える再代入はコンパイルエラーになる。つまり、基本型
変数に限って言えば、final変数は「変更不可」である
ように見える。
話をややこしくしているのは、final指定の参照型変数だ。

    final StringBuilder sb = new StringBuilder("123");
    sb = new StringBuilder("456"); //コンパイルエラー

    final StringBuilder sb2 = new StringBuiler("789");
    sb.append("★");         //コンパイルエラーにならない!
    System.out.println(sb2); //"789★"が出力される

上の例では、参照型変数sbとsb2に対して操作をしている。
ここで、final変数の性質を「変更不可」と理解していると、
sbへの操作がエラーになり、sb2への操作がそうならない事
の意味がわからなくなる。
ここで、参照型変数が保持している値を思い出してみると、
これは「オブジェクトへの参照」であった。つまり、sbや
sb2が持っているのは、"123"や"789"という「値そのもの」
ではない。彼ら(?)が持っているのは、あくまでも「"1
23(456)"という状態となったオブジェクトへの参照」なのだ。
この仕組みと、final変数の厳密な定義「再代入不可」とを考
えに入れると、上の例を理解することができる。すなわち、
sbに対する "sb = new StringBuilder("456")"という操作、これ
は「新たな参照の値をsbに再代入する」という操作だ。よっ
て、final変数の性質により、コンパイルエラーがでる。一方、
"sb2.append("★")" の操作、これは「"789"という状態を持つ
オブジェクトの状態を"789★"という状態に変える
」という操
作となる。要するに、sb2への「オブジェクトへの参照値」の
再代入は起こっていない。そのためエラーとならないのである。

不変オブジェクト

final変数が「再代入不可」の性質を持つ一方で、不変オブジェ
クトは「オブジェクトの状態変更」をも禁じている。つまり、
上の例でいうところの "sb.append("★")" の操作もできないのが
不変オブジェクトなのだ。

まとめ

・final変数は「再代入不可」の性質を持つ
・参照型変数をfinal指定すると、「参照先の変更」は
 できないが「最初に参照したオブジェクトの状態変
 更」はできる
・「最初に参照したオブジェクトの状態変更」もできない
 オブジェクトを「不変オブジェクト」と呼ぶ

...
では今回はこの辺で。ふぎとでした。

(P.S.)記事の内容への指摘などあれば
   遠慮なくお知らせくださいm(__)m

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android 11で令和に標準で対応予定

Androidのバージョンはグングン上がります。
今やそろそろAndroid 11がリリースされつつあります。
そのAndroid 11にて、ついに日本の元号「令和」が標準APIで対応するようです。

結論

結論を真っ先に述べます。

  • Android 11(R)にて、ついに日本の元号「令和」が標準APIで対応するようです。
  • すでにAPIレベル24からAndroid標準APIはICUを取り込んでいます。
  • でもAPIレベル24~29の間は「令和」には対応していません。「平成」どまりです。

[閑話]コードネームとかAPIレベルとか

ところで、お菓子の名前のコードネームですが、「Q」からやんなくなりました。

KitKatとかLollipopとかMarshmallowとかNougatとかOreoとかPieとか。時折、登録商標も混ざりながらも。
Androidっ子たちは、「Q」から綴りが始まるお菓子が何になるのかワクワクしてたのに、Googleが「お菓子の名前はもうやめた」と言い出して。

Android 10は「Q」。そろそろリリースされる11は「R」です。

Androidの「R」で「令和」に対応...まっまさか!その「R」は、「Reiwa」のRか?!なんちゃって。

そして、この10とか11というバージョン番号、そしてコードネームとは別に、開発者にとって重要な番号は「APIレベル」です。
Android 8.0はOreoであり、APIレベルは26です。が、Android 8.1となると、同じくOreoなんですが、APIレベルは27です。ややこしい!

そして「R」に割り当てられるAPIレベルの番号は...?それは当記事の最後の方に記しましたので、お楽しみに。(じらしてスミマセン)

IBM社製のICUというライブラリ

http://site.icu-project.org/home

「International Components for Unicode」の略だそうです。とにかく色々と便利なAPIが収録されています。
オープンソースソフトウェアです。

ICUのJapaneseCalendarクラス

com.ibm.icu.util.JapaneseCalendarクラスは、日本の元号に対応しています。
最初の元号「大化」から現代まで。南北朝時代をどう扱っているか、というマニアックなところまで私は検証していませんが。

本能寺の変
import java.util.Locale;

import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DateFormatSymbols;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.JapaneseCalendar;

public class Honnoujinohen {
    public static void main(String[] args) {
        JapaneseCalendar jpCalendar = new JapaneseCalendar();
        DateFormatSymbols dfs = new DateFormatSymbols(jpCalendar, Locale.JAPANESE);
        DateFormat dateFormat = new SimpleDateFormat("Gyy年MM月dd日", dfs);
        dateFormat.setCalendar(jpCalendar);

        // 「本能寺の変」1582年6月2日
        int year = 1582;
        int month = 6;
        int day = 2;
        String s = dateFormat.format(new GregorianCalendar(year, month - 1, day).getTime());
        System.out.println(s);
    }
}

landmark_honnouji_fire.png

実行結果
天正10年06月02日

大河ドラマファンや歴女には堪りませんね。麒麟がくる。「天が正しい」と書く元号だったのか。
ああ、応仁の乱。天保の改革。安政の大獄。元号に思いを馳せるだけで脳内で時代を駆け巡れます。

このICUが「令和」に対応したのは、2019年4月17日リリースのバージョン64.2からです。

Android標準API

Android標準APIでは、APIレベル24(Android 7.0 Nougat)からICUを取り込みました。
取り込んだだけあって、パッケージも変わっております。例えば、前出のJapaneseCalendarクラスは、android.icu.util.JapaneseCalendarです。

そして。ついに。Android 11(R)のAPIレベルで、

public static final int REIWA

が追加されます!まさかのint enumパターーーン!!!いやーーーん!!!
しょうがねぇだろ、IBM社製のICUがそもそもint enumパターーーンなんだからーーーん!!!

サンプルアプリ

こんなアプリを作ってみました。

Screenshot_1585201324.png

「建武の新政」は、鎌倉幕府を倒した後醍醐天皇が、1333年7月17日からスタートさせた新政策ですが、当日はまだ元号が「元弘」だというヒッカケ問題ですね。今度の2学期中間試験に出そうだな。

nigaoe_godaigo_tennou.png

Screenshot_1585200766.png

平成から令和へ。

Screenshot_1585200774.png

開発環境は以下の通りです。

Android Studio 3.6.1
Build #AI-192.7142.36.36.6241897, built on February 27, 2020
Runtime version: 1.8.0_212-release-1586-b04 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
Windows 10 10.0
GC: ParNew, ConcurrentMarkSweep
Memory: 1237M
Cores: 8
Registry: ide.new.welcome.screen.force=true
Non-Bundled Plugins:

Gradleの設定

Android 11(R)のAPIレベルで作らなければなりません。
Gradle設定ファイルに以下のようにします(一部略)。

build.gradle
android {
    compileSdkVersion 'android-R'
    buildToolsVersion "29.0.2"

    defaultConfig {
        applicationId "com.example.gengou"
        minSdkVersion 24
    }

    compileOptions {
        sourceCompatibility = 1.8
        targetCompatibility = 1.8
    }
}

この記事執筆時点では、Android 11(R)のAPIレベルに番号が割り当てられていません。まだプレビュー版だからのようです。正式版がリリースされたら、おそらく30になると推測されます。

minSdkVersionは最低でも24にしなければなりません。なぜなら前述にありますように、ICUがAndroid標準APIに組み込まれたのが、APIレベル24からだからです。

Activity

MainActivity.java
package com.example.gengou;

import android.icu.text.DateFormat;
import android.icu.text.DateFormatSymbols;
import android.icu.text.SimpleDateFormat;
import android.icu.util.GregorianCalendar;
import android.icu.util.JapaneseCalendar;
import android.os.Bundle;
import android.widget.EditText;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.util.Locale;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        EditText year = findViewById(R.id.year);
        EditText month = findViewById(R.id.month);
        EditText day = findViewById(R.id.day);
        TextView gengo = findViewById(R.id.gengou);

        findViewById(R.id.button).setOnClickListener(v -> {
            int y = Integer.parseInt(year.getText().toString());
            int m = Integer.parseInt(month.getText().toString());
            int d = Integer.parseInt(day.getText().toString());
            JapaneseCalendar jpCalendar = new JapaneseCalendar();
            DateFormatSymbols dfs = new DateFormatSymbols(jpCalendar, Locale.JAPANESE);
            DateFormat dateFormat = new SimpleDateFormat("Gyy年MM月dd日", dfs);
            dateFormat.setCalendar(jpCalendar);
            String s = dateFormat.format(new GregorianCalendar(y, m - 1, d).getTime());
            gengo.setText(s);
        });
    }
}

Android標準APIではなく、IBMのICUで令和に対応する

せっかくAndroid標準APIで令和に対応するってのに!それを使わずに、IBMのICUで令和に対応するって、なんか本末転倒!
と思われるかもしれませんが、以下の理由でそんなコトもしてみたいのです。

  • Android標準APIで令和に対応するのは、「R」からだ。それはいい。
  • ICUがAndroid標準APIに組み込まれたのは、APIレベル24(Android 7.0「N」)だ。
  • であれば、Gradleの設定でminSdkVersionは最低でも24でないと、令和うんぬん以前に、android.icuパッケージが使えない。
  • だけど、自分の作るアプリは、Androidのバージョン6とか5とか4とかにも対応したい!

ということで、そのやり方です。

Gradleの設定

build.gradle
dependencies {
    implementation 'com.ibm.icu:icu4j:64.2'
}

依存ライブラリとして追記してSyncNowですね。令和対応の64.2以上を指定します。

プログラム

名前衝突を避けるためにもimport文はこうなります。

パッケージにご注意
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DateFormatSymbols;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.JapaneseCalendar;

import java.util.Locale;

Localeだけは、安定のjava.utilパッケージですね。
これでAndroid 6とか5とか4とかでも、令和対応できます。

[おまけ]当たり前ですが。

「光文」という元号は、ICUにはありませんので、ご安心を。

Screenshot_1585202068.png

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java 8のOptionalをSwiftのOptionalと比較する

概要

本記事ではOptionalのApple公式ドキュメントの概要に掲載されている項目を元にして、SwiftのOptional型とJava 8のOptional型を比較します。
なお本記事に掲載してあるスニペットは多少簡略化されているので、そのままコピペしても動かないものがあります。ご了承ください。

Nil結合演算子(Nil-Coalescing Operator)

Swift

中身があるならそれを、なければ指定したデフォルト値を返すようにOptinalをアンラップします。??という演算子を使います。複数のOptional型をオペランドとしてつなぐこともできます。

Swift
let wrappedValue: String? = nil
let defaultValue = "default"
let value = wrappedValue ?? defaultValue
print(value)
// "default"
Swift
let wrappedValue: String? = nil
let defaultValue = "default"

let otherWrappedValue: String? = nil
// 右オペランドがOptionalである限り??でつなぐことができる
let value = wrappedValue ?? otherWrappedValue ?? defaultValue
print(value)
// "default"

Java 8

Java 8では演算子ではなくorElseorElseGetというメソッドです。orElseではデフォルト値そのものを、orElseGetではデフォルト値を提供するSupplier<? extends T>型のオブジェクトを渡します。しかしいずれのメソッドもラップしている型を返すので、SwiftのようにorElse複数のOptional型をつなぐことはできません

Java
Optional<String> wrappedValue = Optional.empty();
String defaultValue = "default";
String value = wrappedValue.orElse(defaultValue);
System.out.println(value);
// "default"

String otherValue = wrappedValue.orElseGet( () -> defaultValue );
System.out.println(otherValue);
// "default"
Java
Optional<String> otherWrappedValue = Optional.of("other");
// これはできない
// String unwrappedValue = wrappedValue.orElse(otherWrappedValue).orElse(defaultValue);

短絡評価

Swift

SwiftのNil結合演算子は短絡評価します。したがって左オペランドがnilでない限り右オペランドが評価されることはありません。

Swift
func hoge() -> String {
    print("called")
    return "hoge"
}

let value = nil ?? hoge()
print(value)
// "called"
// "hoge"

// 短絡評価なのでhoge()は呼ばれない
let value = "non nil" ?? hoge()
print(value)
// "non nil"

Java 8

Java 8のorElseGetも短絡評価ですが、orElseはしません。デフォルト値そのものを渡しているので当然と言えば当然ですね。

Java
public String hoge() {
    System.out.println("called");
    return "hoge";
}

String value = Optional.<String>empty().orElseGet(this::hoge);
System.out.println(value);
// "called"
// "hoge"

// 短絡評価なのでhoge()は呼ばれない
String value = Optional.of("non null").orElseGet(this::hoge);
System.out.println(value);
// "non null"
Java
// orElseは短絡評価しない
String value = Optional.of("non null").orElse(hoge());
System.out.println(value);
// "called"
// "non null"

使い分けについてですが、デフォルト値生成にコストがかかる場合などには短絡評価するorElseGetを使うと良いでしょう。

Java
public String fetchValue() {
    // 時間のかかる処理
    return result;
}

// △ - 必ずfetchValue()が呼ばれるので少なくともパフォーマンスに影響
String value = wrappedValue.orElse(fetchValue());
// ◯ - 短絡評価なので中身があれば呼ばれない
String value = wrappedValue.orElseGet(this::fetchValue);
Java
// △ - 中身があると無駄なオブジェクト生成となる
Person person = wrappedPerson.orElse(new Person());
// ◯ - 短絡評価なので中身があればオブジェクトは生成されない
Person person = wrappedPerson.orElseGet(Person::new);

強制アンラップ(Unconditional Unwrapping)

Swift

中身があるかどうかにかかわらず値をアンラップします。演算子!を使います。もしnilであった場合は実行時エラーを引き起こします。

Swift
let number = Int("42")!
print(number)
// 42

Java 8

Java 8ではgetメソッドを使います。もしnullであった場合は非検査例外であるNoSuchElementExceptionが投げられます。
※ なお、intFromStringは説明の便宜上Optional<Integer>型を返していますが、通常整数を包む場合はOptionalInt型を使う方が良いでしょう。

Java
public Optional<Integer> intFromString(String string) {
    try {
        return Optional.of(Integer.parseInt(string));
    } catch (NumberFormatException ignored) {
        return Optional.empty();
    }
}

int number = intFromString("42").get();
System.out.println(number);
// 42

変換

Swift

値を変換します。mapというメソッドを使います。中身があれば与えられた(Wrapped) -> U型のクロージャに従って値を変換し、nilであればnilのまま流します。したがってOptional型を返します。

Swift
let intString: String? = "42"
let percentage: String? = intString.map { "\($0)%" }
print(String(describing: percentage))
// Optional("42%")

クロージャ内でOptional型を返したいときは、(Wrapped) -> Optional<U>型のクロージャを引数にとるflatMapメソッドを使います。

Swift
let intString: String? = "42"
// Int(String)はInt?を返す
let integer: Int? = intString.flatMap { Int($0) }
print(String(describing: integer))
// Optional(42)

Java 8

Java 8でも同様にmapflatMapというメソッドを使います。使い分けも同様です。

Java
Optional<String> intString = Optional.of("42");
Optional<String> percentage = intString.map( str -> str + "%" );
System.out.println(percentage);
// Optional[42%]
Java
Optional<String> intString = Optional.of("42");
Optional<Integer> integer = intString.flatMap(this::intFromString);
System.out.println(integer);
// Optional[42]

オプショナルバインディング(Optional Binding)

Swift

安全なアンラップ手段として馴染み深いことと思います。ifguardといったキーワードと併せることで、中身がある場合とない場合とで処理を分岐することができます。

Swift
let wrappedValue: String? = "value"
if let value: String = wrappedValue {
    print(value)
} else {
    print("no value")
}
// "value"
Swift
let wrappedValue: String? = "value"
guard let value: String = wrappedValue else {
    print("no value")
    return
}
print(value)
// "value"

Java 8

Java 8ではifPresentというメソッドを介して安全にアンラップすることができます。Consumer<? super T>型のオブジェクトを引数にとり、その中に中身があった場合の処理を記述します。

Java
Optional<String> wrappedValue = Optional.of("value");
wrappedValue.ifPresent( str -> {
    System.out.println(str);
});
// "value"

しかしながら中身がない場合の処理を記述できるメソッドはありません。したがって両方の処理を記述したい場合はisPresentを使って中身の有無を確認する方法をとる必要があります。ただし、中身がある場合明示的に強制アンラップgetを記述しなければいけないためSwiftと比較すると冗長です

Java
Optional<String> wrappedValue = Optional.of("value");
if (wrappedValue.isPresent()) {
    // 冗長だが明示的に強制アンラップする必要がある
    String value = wrappedValue.get();
    System.out.println(value);
} else {
    System.out.println("no value");
}
// "value"
Java
Optional<String> wrappedValue = Optional.of("value");
// カード節的に記述する
if (!wrappedValue.isPresent()) {
    System.out.println("no value");
    return;
}
// 冗長だが明示的に強制アンラップする必要がある
String value = wrappedValue.get();
System.out.println(value);
// "value"
Java
Optional<String> wrappedValue = Optional.of("value");
wrappedValue.ifPresent( str -> {
    System.out.println(str);
})// .orElse( () -> { // これはできない
//     System.out.println("no value");
// })
;

またifPresentに渡す処理のスコープはラムダ式内であるため、その処理内で呼び出し元のメソッドのリターンはできません。したがって早期リターンなどを行いたい場合はisPresentを使った方法をとる他ありません。

Java
public boolean foo(Optional<String> wrappedValue) {
    // 値があるなら早期リターンする
    if (wrappedValue.isPresent) {
        String str = wrappedValue.get();
        // 何かする
        return true;
    }
    // 値がない場合の処理
    return result;
}
Java
public boolean foo(Optional<String> wrappedValue) {
    // 値があるなら早期リターンする
    wrappedValue.ifPresent( str -> {
        // 何かする
//         return true; // スコープがこのラムダ式内にあるため、これはできない
    });
    // 値がない場合の処理
    return result;
}

オプショナルチェイニング(Optional Chaining)

Swift

中身があるときのみプロパティやメソッドへのアクセスを行います。後置演算子?を使います。

Swift
let wrappedValue: String? = "value"
let uppercased: String? = wrappedValue?.uppercased()
print(String(describing: uppercased))
// Optional("VALUE")

Java 8

Java 8では先に紹介したmapメソッドが使えるでしょう。メソッド参照を用いればある程度簡潔に書くことができます。

Java
Optional<String> wrappedValue = Optional.of("value");
Optional<String> uppercased = wrappedValue.map(String::toUpperCase);
System.out.println(uppercased);
// Optional[VALUE]

まとめ

本記事ではSwiftでのOptional型の扱いを元に、Java 8とOptional型との比較を行いました。特に、オプショナルバインディングについては両者の違いがよくあらわれていました。言語レベルでNullセーフであるSwiftと比べてしまうと扱いづらさが目立ってしまうものの、ラムダ式やメソッド参照のおかげでかなり書きやすくなっているなと感じています。
もし何か間違いなどございましたらコメントにてご指摘ください。

オマケ - Java 9のOptional

ここではJava 9で追加されたOptionalのメソッド、orifPresentOrElseを紹介します。Java 8の話は出てきません。完全なオマケです。

or

中身がない場合に与えられたSupplier<? extends Optional<? extends T>>型のオブジェクトを実行してOptional型のデフォルト値を得ます。これはちょうどSwiftのNil結合演算子で右オペランドにOptional型を持ってきた場合と対応しています。
また短絡評価をするため、中身がある限り与えられたSupplierオブジェクトが実行されることはありません。

Swift
let wrappedValue: String? = nil
let defaultValue = "default"

let otherWrappedValue: String? = nil
let value = wrappedValue ?? otherWrappedValue ?? defaultValue
print(value)
// "default"
Java
// available Java 9 or later
Optional<String> wrappedValue = Optional.empty();
String defaultValue = "default";

Optional<String> otherWrappedValue = Optional.empty();
// Java 9以降ならデフォルト値にOptional型を指定できる
String value = wrappedValue.or( () -> otherWrappedValue ).orElse(defaultValue);
System.out.println(value);
// "default"

ifPresentOrElse

中身がある場合とない場合とで処理を分岐することができます。中身がある場合にはConsumer<? super T>型のオブジェクトが、ない場合にはRunnable型のオブジェクトが実行されます。nullだった場合の処理も記述できるので、Java 8と比べるとSwiftのオプショナルバイディングにかなり近づいています。

Swift
let wrappedValue: String? = "value"
if let value = wrappedValue {
    print(value)
} else {
    print("no value")
}
// "value"
Java
// available Java 9 or later
Optional<String> wrappedValue = Optional.<String>empty();
wrappedValue.ifPresentOrElse( str -> {
    System.out.println(str);
}, () -> {
    System.out.println("no value");
});
// "no value"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java 8のOptionalをSwiftと比較する

概要

本記事ではOptionalのApple公式ドキュメントの概要に掲載されている項目を元にして、SwiftのOptional型とJava 8のOptional型を比較します。若干今更感のある内容にはなりますが個人的な備忘録も兼ねて投稿に至りました。
記事の最後にはそれぞれの記法の比較表も掲載しています。あわせてご覧ください。
なお本記事に掲載してあるスニペットは多少簡略化されているので、そのままコピペしても動かないものがあります。ご了承ください。

Nil結合演算子(Nil-Coalescing Operator)

Swift

中身があるならそれを、なければ指定したデフォルト値を返すようにOptinalをアンラップします。??という演算子を使います。複数のOptional型をオペランドとしてつなぐこともできます。

Swift
let wrappedValue: String? = nil
let defaultValue = "default"
let value = wrappedValue ?? defaultValue
print(value)
// "default"
Swift
let wrappedValue: String? = nil
let defaultValue = "default"

let otherWrappedValue: String? = nil
// 右オペランドがOptionalである限り??でつなぐことができる
let value = wrappedValue ?? otherWrappedValue ?? defaultValue
print(value)
// "default"

Java 8

Java 8では演算子ではなくorElseorElseGetというメソッドです。orElseではデフォルト値そのものを、orElseGetではデフォルト値を提供するSupplier<? extends T>型のオブジェクトを渡します。しかしいずれのメソッドもラップしている型を返すので、SwiftのようにorElse複数のOptional型をつなぐことはできません

Java
Optional<String> wrappedValue = Optional.empty();
String defaultValue = "default";
String value = wrappedValue.orElse(defaultValue);
System.out.println(value);
// "default"

String otherValue = wrappedValue.orElseGet( () -> defaultValue );
System.out.println(otherValue);
// "default"
Java
Optional<String> otherWrappedValue = Optional.of("other");
// これはできない
// String unwrappedValue = wrappedValue.orElse(otherWrappedValue).orElse(defaultValue);

短絡評価

Swift

SwiftのNil結合演算子は短絡評価します。したがって左オペランドがnilでない限り右オペランドが評価されることはありません。

Swift
func hoge() -> String {
    print("called")
    return "hoge"
}

let value = nil ?? hoge()
print(value)
// "called"
// "hoge"

// 短絡評価なのでhoge()は呼ばれない
let value = "non nil" ?? hoge()
print(value)
// "non nil"

Java 8

Java 8のorElseGetも短絡評価ですが、orElseはしません。デフォルト値そのものを渡しているので当然と言えば当然ですね。

Java
public String hoge() {
    System.out.println("called");
    return "hoge";
}

String value = Optional.<String>empty().orElseGet(this::hoge);
System.out.println(value);
// "called"
// "hoge"

// 短絡評価なのでhoge()は呼ばれない
String value = Optional.of("non null").orElseGet(this::hoge);
System.out.println(value);
// "non null"
Java
// orElseは短絡評価しない
String value = Optional.of("non null").orElse(hoge());
System.out.println(value);
// "called"
// "non null"

使い分けについてですが、デフォルト値生成にコストがかかる場合などには短絡評価するorElseGetを使うと良いでしょう。

Java
public String fetchValue() {
    // 時間のかかる処理
    return result;
}

// △ - 必ずfetchValue()が呼ばれるので少なくともパフォーマンスに影響
String value = wrappedValue.orElse(fetchValue());
// ◯ - 短絡評価なので中身があれば呼ばれない
String value = wrappedValue.orElseGet(this::fetchValue);
Java
// △ - 中身があると無駄なオブジェクト生成となる
Person person = wrappedPerson.orElse(new Person());
// ◯ - 短絡評価なので中身があればオブジェクトは生成されない
Person person = wrappedPerson.orElseGet(Person::new);

強制アンラップ(Unconditional Unwrapping)

Swift

中身があるかどうかにかかわらず値をアンラップします。演算子!を使います。もしnilであった場合は実行時エラーを引き起こします。

Swift
let number = Int("42")!
print(number)
// 42

Java 8

Java 8ではgetメソッドを使います。もしnullであった場合は非検査例外であるNoSuchElementExceptionが投げられます。
※ なお、intFromStringは説明の便宜上Optional<Integer>型を返していますが、通常整数を包む場合はOptionalInt型を使う方が良いでしょう。

Java
public Optional<Integer> intFromString(String string) {
    try {
        return Optional.of(Integer.parseInt(string));
    } catch (NumberFormatException ignored) {
        return Optional.empty();
    }
}

int number = intFromString("42").get();
System.out.println(number);
// 42

変換

Swift

値を変換します。mapというメソッドを使います。中身があれば与えられた(Wrapped) -> U型のクロージャに従って値を変換し、nilであればnilのまま流します。したがってOptional型を返します。

Swift
let intString: String? = "42"
let percentage: String? = intString.map { "\($0)%" }
print(String(describing: percentage))
// Optional("42%")

クロージャ内でOptional型を返したいときは、(Wrapped) -> Optional<U>型のクロージャを引数にとるflatMapメソッドを使います。

Swift
let intString: String? = "42"
// Int(String)はInt?を返す
let integer: Int? = intString.flatMap { Int($0) }
print(String(describing: integer))
// Optional(42)

Java 8

Java 8でも同様にmapflatMapというメソッドを使います。使い分けも同様です。

Java
Optional<String> intString = Optional.of("42");
Optional<String> percentage = intString.map( str -> str + "%" );
System.out.println(percentage);
// Optional[42%]
Java
Optional<String> intString = Optional.of("42");
Optional<Integer> integer = intString.flatMap(this::intFromString);
System.out.println(integer);
// Optional[42]

オプショナルバインディング(Optional Binding)

Swift

安全なアンラップ手段として馴染み深いことと思います。ifguardといったキーワードと併せることで、中身がある場合とない場合とで処理を分岐することができます。

Swift
let wrappedValue: String? = "value"
if let value: String = wrappedValue {
    print(value)
} else {
    print("no value")
}
// "value"
Swift
let wrappedValue: String? = "value"
guard let value: String = wrappedValue else {
    print("no value")
    return
}
print(value)
// "value"

Java 8

Java 8ではifPresentというメソッドを介して安全にアンラップすることができます。Consumer<? super T>型のオブジェクトを引数にとり、その中に中身があった場合の処理を記述します。

Java
Optional<String> wrappedValue = Optional.of("value");
wrappedValue.ifPresent( str -> {
    System.out.println(str);
});
// "value"

しかしながら中身がない場合の処理を記述できるメソッドはありません。したがって両方の処理を記述したい場合はisPresentを使って中身の有無を確認する方法をとる必要があります。ただし、中身がある場合明示的に強制アンラップgetを記述しなければいけないためSwiftと比較すると冗長です

Java
Optional<String> wrappedValue = Optional.of("value");
if (wrappedValue.isPresent()) {
    // 冗長だが明示的に強制アンラップする必要がある
    String value = wrappedValue.get();
    System.out.println(value);
} else {
    System.out.println("no value");
}
// "value"
Java
Optional<String> wrappedValue = Optional.of("value");
// カード節的に記述する
if (!wrappedValue.isPresent()) {
    System.out.println("no value");
    return;
}
// 冗長だが明示的に強制アンラップする必要がある
String value = wrappedValue.get();
System.out.println(value);
// "value"
Java
Optional<String> wrappedValue = Optional.of("value");
wrappedValue.ifPresent( str -> {
    System.out.println(str);
})// .orElse( () -> { // これはできない
//     System.out.println("no value");
// })
;

またifPresentに渡す処理のスコープはラムダ式内であるため、その処理内で呼び出し元のメソッドのリターンはできません。したがって早期リターンなどを行いたい場合はisPresentを使った方法をとる他ありません。

Java
public boolean foo(Optional<String> wrappedValue) {
    // 値があるなら早期リターンする
    if (wrappedValue.isPresent) {
        String str = wrappedValue.get();
        // 何かする
        return true;
    }
    // 値がない場合の処理
    return result;
}
Java
public boolean foo(Optional<String> wrappedValue) {
    // 値があるなら早期リターンする
    wrappedValue.ifPresent( str -> {
        // 何かする
//         return true; // スコープがこのラムダ式内にあるため、これはできない
    });
    // 値がない場合の処理
    return result;
}

オプショナルチェイニング(Optional Chaining)

Swift

中身があるときのみプロパティやメソッドへのアクセスを行います。後置演算子?を使います。

Swift
let wrappedValue: String? = "value"
let uppercased: String? = wrappedValue?.uppercased()
print(String(describing: uppercased))
// Optional("VALUE")
Swift
class Hoge {
    func hoge() { print("hoge") }
}

let wrappedValue: Hoge? = Hoge()
wrappedValue?.hoge()
// "hoge"

Java 8

Java 8では先に紹介したmapメソッドが使えます。対象がメソッドであればメソッド参照を用いて簡潔に書くことができます。ただし、対象がOptional型を返す場合にはflatMapを使う必要があります。

Java
Optional<String> wrappedValue = Optional.of("value");
Optional<String> uppercased = wrappedValue.map(String::toUpperCase);
System.out.println(uppercased);
// Optional[VALUE]
Java
class User {
    final String id;
    Optional<String> mail = Optional.empty();
    User(String id) { this.id = id; }
}

Optional<User> wrappedUser = Optional.of(new User("user1"));
Optional<String> mail = wrappedUser.flatMap( user -> user.mail );
System.out.println(mail);
// Optional.empty

使い分けを含め変換のときと全く同じ記法ですね。Java 8ではラップしている値のフィールドやメソッドにアクセスするためだけの特別な記法やメソッドが用意されているわけではありません。
値を返さないメソッドへのアクセスは先に紹介したifPresentを使います。

Java
class Hoge {
    void hoge() { System.out.println("hoge"); }
}

Optional<Hoge> wrappedValue = Optional.of(new Hoge());
wrappedValue.ifPresent(Hoge::hoge);
// "hoge"

まとめ

本記事ではSwiftでのOptional型の扱いを元に、Java 8とOptional型の比較を行いました。以下にそれぞれの記法の比較表を掲載します。特にオプショナルバインディングとオプショナルチェイニングについては両者の違いがよくあらわれています。Java 8のオプショナルバインディングでは分岐と同時にアンラップできない分Swiftより少し冗長です。またJava 8にはSwiftの?演算子のようなラップしている値にアクセスするための専用手段は用意されていません。

Swift Java
Nil結合演算子 ?? orElse ※ 非短絡評価
orElseGet
強制アンラップ ! get
変換 map
flatMap
map
flatMap
オプショナルバインディング if let
guard let
ifPresent
もしくはisPresentで分岐した後にgetで強制アンラップ
オプショナルチェイニング ? Optional型を返すならflatMap
それ以外を返すならmap
値を返さないならifPresent

もし何か間違いなどございましたらコメントにてご指摘ください。

オマケ - Java 9のOptional

ここではJava 9で追加されたOptionalのメソッド、orifPresentOrElseを紹介します。Java 8の話は出てきません。完全なオマケです。

or

中身がない場合に与えられたSupplier<? extends Optional<? extends T>>型のオブジェクトを実行してOptional型のデフォルト値を得ます。これはちょうどSwiftのNil結合演算子で右オペランドにOptional型を持ってきた場合と対応しています。
また短絡評価をするため、中身がある限り与えられたSupplierオブジェクトが実行されることはありません。

Swift
let wrappedValue: String? = nil
let defaultValue = "default"

let otherWrappedValue: String? = nil
let value = wrappedValue ?? otherWrappedValue ?? defaultValue
print(value)
// "default"
Java
// available Java 9 or later
Optional<String> wrappedValue = Optional.empty();
String defaultValue = "default";

Optional<String> otherWrappedValue = Optional.empty();
// Java 9以降ならデフォルト値にOptional型を指定できる
String value = wrappedValue.or( () -> otherWrappedValue ).orElse(defaultValue);
System.out.println(value);
// "default"

ifPresentOrElse

中身がある場合とない場合とで処理を分岐することができます。中身がある場合にはConsumer<? super T>型のオブジェクトが、ない場合にはRunnable型のオブジェクトが実行されます。nullだった場合の処理も記述できるので、Java 8と比べるとSwiftのオプショナルバイディングにかなり近づいています。

Swift
let wrappedValue: String? = "value"
if let value = wrappedValue {
    print(value)
} else {
    print("no value")
}
// "value"
Java
// available Java 9 or later
Optional<String> wrappedValue = Optional.<String>empty();
wrappedValue.ifPresentOrElse( str -> {
    System.out.println(str);
}, () -> {
    System.out.println("no value");
});
// "no value"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む