20200721のJavaに関する記事は8件です。

JSFのタグがHTMLにならないときの対応方法

  • 環境
    • CentOS Linux release 7.8.2003 (Core)
    • Payara Server 5.194
    • Eclipse IDE for Enterprise Java Developers.Version: 2020-03 (4.15.0)

事象 : JSFのタグがHTMLにならないでそのまま表示される

JSFのプロジェクトをつくって早速XHTMLを書いたけど・・・JSFのタグがそのまま出力されている
スクリーンショット 2020-07-21 22.53.37.png

index.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:f="http://xmlns.jcp.org/jsf/core">
<head>
    <title>JSFのタグがHTMLにならないときの対応方法</title>
</head>
<body>
  <h3>JSFのタグがHTMLにならないときの対応方法</h3>
  h:selectBooleanCheckboxでチェックボックスを書いてみた
  <div>
    <h:selectBooleanCheckbox id="checkBox" value="false" />
    <h:outputLabel for="checkBox" value="チェックボックス"/>
  </div>
</body>
</html>

原因 : web.xmlでのURL定義と使っているURLがあっていないから

servlet-mappingは、ウェブアプリケーションにアクセスするURLの指定方法を定めます。
わかりやすいJava EE ウェブシステム入門 川場隆 著

WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>tryJsf</display-name>
...省略...
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
  </servlet-mapping>
</web-app>

web.xmlの定義だとhttp://xxxxxx/{display-name}/faces/index.xhtmlで表示する
が、今回の事象はhttp://xxxxxx/{display-name}/index.xhtmlで表示したので失敗した。

対応1 : 使うURLをweb.xmlに合わせる

http://xxxxxx/{display-name}/index.xhtmlを使うとちゃんと表示される
スクリーンショット 2020-07-21 22.49.16.png

対応2 : web.xmlを使うURLに合わせる

web.xmlを修正して再ビルドして再実行する

WEB-INF/web.xml
...省略...
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>
</web-app>

スクリーンショット 2020-07-21 22.59.39.png

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

(JavaSE8Gold試験対策)ComparableやComparatorと仲良くなりたい人のメモ。

間違いを見つけた方がいらっしゃったら優しく指摘してくださると嬉しいです。

はじめに

私自身、JavaGold試験勉強中に全然頭の整理ができなくて非常に混乱したので、振り返りを兼ねて整理しました。これで少しだけ仲良くなれた気がする٩( 'ω' )و

そもそも何なのよ?

ComparableやComparatorって何?と言うところから入りますが、共に並び替えや最小最大値取得のメソッドにおいて、要素の相互比較をするために用いられるインターフェースです。まぁ、それは分かるよ...って感じですよね。

「自然順序付け」って聞いてピンときますか?

何となく昇順なのかな〜〜。くらいの理解で、私はピンときませんでした。
ただ、これが何なのかピンと来るだけで、理解へのハードルが格段に下がることになります。
そのため、まずはコチラの説明から入らせてください。

自然順序付けはComparableに従った並び替えのこと

JavaのAPIドキュメントを参照していると、この「自然順序付け」が頻繁に出てきます。
ただ、見ていくと分かるのですが、もはや「自然順序付け」と「コンパレータ」はセット商品みたいに紹介されてます。

スクリーンショット 2020-07-20 20.57.47.png
※Java APIドキュメント(Collectionsクラス)より抜粋

ここで何となくピンと来る方も居ると思うんですが、自然順序付けとはComparableで実装された順序に並び替える事を示しています。
下記にComparableのJava APIドキュメントを抜粋します。

public interface Comparable
このインタフェースを実装する各クラスのオブジェクトに全体順序付けを強制します。この順序付けはクラスの自然順序付けと呼ばれ、このクラスのcompareToメソッドは自然比較メソッドと呼ばれます。
Java APIドキュメント-Comparable

それではComparableとは何なのでしょうか。
位置付けとしてはクラス(オブジェクト)の共通メソッドのようです。
共通メソッドと言えばequalsやtoStringなどがパッと思い浮かんだのではないでしょうか?
ただし、他と違ってComparableはObjectには宣言されていないません。そのため、必要に応じてクラス作成時に実装する必要があります。
なお、Comparableを実装せずに自然順序付けに従ってソート実行しようとすると"ClassCastException"が発生するので、対象のクラスがComparableを実装しているか注意しておきましょう。

ちなみに、昇順でも降順でも実装可能なのですが、自然順序付けで並び替えるメソッドに明確に”昇順”と記載されているもの(Collectionsクラスのsortなど)がありますので、自然順序付けは、おそらく昇順実装が基本なのかと思います。

それじゃあ、Comparatorは??

Comparatorは、Comparableよりも後から出てきたインターフェースです。
主な利用用途はおそらく以下の感じだろうと推測。

・Comparableが実装されていないクラスまたはフィールドの並び替えを行いたい
・ちょっと特殊な並び替えを行いたい

そして、これらの期待に応えるためなのか、Comparatorは柔軟に実装可能な「関数型インターフェース」となっています。また、より実装を簡素化可能なstaticメソッドも提供されており、Comparableを実装するために、Comparatorのstaticメソッド使っちゃうとかもあるみたいです(笑)

また、Comparatorがnullの場合は、メソッドによってComparableが適用されたり、"NullPointerException"となったりするようです。

で?Comparableなのか?Comparatorなのか?

試験対策としてだけ考えるのであれば、極論は以下の通りに覚えておけば問題ないと思います。一部Comparableを許容していないメソッドもありましたが、基本はセットものとして覚えてOKかと。

スクリーンショット 2020-07-20 21.29.00.png

その上で、それぞれの実装の違い(メソッド名や引数の数の違い)をしっかり覚えればヨシ!

実装の違いについて

それぞれの実装の違いをまとめると以下のような感じになります。
オーバーライドすべきメソッドと引数の数は試験に必出なので覚えておきましょう。

パッケージ インタフェース 抽象メソッド 引数の数 戻り値の型 説明
java.lang Comparable compareTo 1 int 自身と引数オブジェクトを比較。自然順序付けに使用
java.util Comparator compare 2 int 引数同士を比較。任意の順序付けを行う

次項から具体的な実装例を紹介していこうと思います。

実装方法

それぞれの実装例を紹介いたします。
また、もし可能であれば是非実機で確認してみるのをオススメします。

↓↓紹介するサンプルを以下にコピペでサクッと動くはず
Javaクラウド実行環境:https://paiza.io/

Comparable

先に説明したとおり、該当クラスに対してComparableの実装を行います。
実装例は以下のとおりです。

①クラスに実装

class Book implements Comparable<Book> {     // 実装宣言。<>内は比較対象です。

    public Integer id;
    public String title;

    Book(Integer id, String title){
        this.id = id;
        this.title = title;
    }

    @Override
    public int comareTo(Book book){          // Comparableの抽象メソッド
        return this.id.compareTo(book.id);   // 自分自身と引数を比べる
    }
}

compareToメソッドでは、指定されたオブジェクトより小さい場合は負の整数、等しい場合はゼロ、大きい場合は正の整数を返します。
因み(?)にcompareToメソッドの中で、さらにcompareToて何なん?と思うのは私だけでしょうか。

それではComparableを使って並び替えをしてみましょう。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        // ソート対象の作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        // ソート実行
        bookList.sort();  // 引数指定してないのでComparableに従って自然順序づけされます

        // ソート対象を表示する
        bookList.forEach( s -> System.out.println(s.id + ":" + s.title));
    }
}

<実行結果>
1:ハリーポッター
2:ハウルの動く城
3:ハムスター飼育日記

これ見てるとジェネリックがあるからこその実装なんだろうなぁ〜と、ぼんやり思います。

そして、Comparableに興味が出てきたあなた。
JavaのAPIドキュメントでもっと詳しく(正確に)知ってください٩( 'ω' )و ←投げた

Java APIドキュメント - Comparableインターフェース

Comparator

それでは続いてComparatorにいきましょう。
こちらは「関数型インターフェース」なので様々な実装方法が取れます。
私が分かる範囲にはなりますが、それぞれサンプルを紹介しますね〜。

①クラスに実装

これはComparableとほぼ一緒な感じです。
Comparatorをこの形で使用するのは、あんまり意味ない気がします。個人的に。。

※後続の実装方法でもこのBookクラスを使用する前提にしたいので、紐付けできるようにコメントにどの実装方法に関連するコードかを番号付けしておきます。

class Book implements Comparator<Book> {         //①実装宣言。<>内は比較対象です。

    public Integer id;
    public String title;

    Book(){}                                     //①これがないとnewした時に怒られます

    Book(Integer id, String title){
        this.id = id;
        this.title = title;
    }

    public int getId(){                          // ②Comparator.comparingの引数に使う
        return this.id;
    }

    public String getTitle(){                    // ②Comparator.thenComparingの引数に使う
        return this.title;
    }

    @Override 
    public int compare(Book book1, Book book2){   // ①Comparatorインタフェースの抽象メソッド
        return book1.id - book2.id;              // idの昇順で並び替え
    }
}

compareメソッドでは、順序付けのために2つの引数を比較します。最初の引数が2番目の引数より小さい場合は負の整数、両方が等しい場合は0、最初の引数が2番目の引数より大きい場合は正の整数を返します。

それでは実装したComparatorで並び替えをしてみましょう。
私は初見で衝撃(?)を受けたのですが、クラスに実装の場合、Comparatorは「Comparatorを実装したクラスをnew」して引き渡します。
あー...並べ替え対象の型と相違する型のComparator渡したら怒られそう...あ、怒られた(何

import java.util.*;
public class Main {
    public static void main(String[] args) throws Exception {

        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        bookList.sort(new Book());  // ← 【注】Comparatorの渡し方が特殊なので必ず覚えて!Comparatorを含んだクラスのインスタンスを引き渡します!!!
        bookList.forEach( s -> System.out.println(s.id + ":" + s.title));
    }
}

<実行結果>
1:ハリーポッター
2:ハウルの動く城
3:ハムスター飼育日記

②ラムダ式(又は匿名クラス)で実装

関数型インターフェースであることを遺憾無く発揮できる書き方です。
ラムダ式で書ける言うことは、匿名クラスでも書けるので両方紹介します。

ラムダ式の実装サンプル

    Comparator<Book> comparator = 
        (b1, b2) -> b1.id.compareTo(b2.id);        // idの昇順

匿名クラスの実装サンプル

    Comparator<Book> comparator = new Comparator<Book>(){
        @Override
        public int compare(Book book1, Book book2) {
            return book1.id.compareTo(book2.id);   // idの昇順
        }
    };

※ComparableよりComparatorで実装すると多少遅くなるようです。
実装時はJavaのstaticインポート(static import)機構の使用も検討した方が良いらしいです。

それでは並び替えを実行してみましょう。
こちらは変数化せずに、直接Comparatorを実装しています。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //ソート対象を作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        //Comparator(匿名クラス)を使ったソート実行
        bookList.sort(new Comparator<Book>(){
            @Override
            public int compare(Book book1, Book book2) {
                return book1.id.compareTo(book2.id);         // idの昇順
            }
        });

        //ソート結果を表示
        System.out.println("匿名クラス(idの昇順)");
        bookList.forEach( s -> System.out.println(s.id + ":" + s.title));

        //Comparator(ラムダ式)を使ったソート実行 ※わかりやすい用に降順に並べる
        bookList.sort((b1, b2) -> b2.id.compareTo(b1.id));  // idの降順

        //ソート結果を表示
        System.out.println("ラムダ式(idの降順)");
        bookList.forEach( s -> System.out.println(s.id + ":" + s.title));
    }
}

<実行結果>
匿名クラス(idの昇順)
1:ハリーポッター
2:ハウルの動く城
3:ハムスター飼育日記
ラムダ式(idの降順)
3:ハムスター飼育日記
2:ハウルの動く城
1:ハリーポッター

③Comparatorのstaticメソッドで実装

Comparatorには、便利なstaticメソッドが提供されています。
このstaticメソッドを組み合わせることで並び替え対象のメンバ優先順位付けをすることも出来ます。
単純な並び替えであれば、コイツを使うのが一番楽チンぽんと思います。(何

StreamAPIを使用したサンプルは以下のとおり。

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //ソート対象を作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(2, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリーポッター"));

        //ストリーム化する
        Stream<Book> streamList = bookList.stream();

        //Comparator.comparingを使用して並び替えて表示 
        streamList.sorted(Comparator.comparing(Book::getId)  // idを第一優先で並び替え
                .thenComparing(Book::getTitle))              // titleを第二優先で並び替え
                .forEach(s -> System.out.println(s.id + ":" + s.title));
    }
}

<実行結果>
1:ハリーポッター
2:ハウルの動く城
2:ハムスター飼育日記

comparing()とthenComparing()はチェーンさせて使用します。
もちろん、comparing()だけでも利用可能ですし、thenComparing()を更にチェーンさせることも可能です。
また、訂正するとstaticと言っておきながら、thenComparing()はdefaltメソッドです^^;
他にもComparatorでは以下のようなメソッドが提供されています。

# メソッド名 説明
comparing ソート・キーを抽出する関数を受け取り、そのソート・キーで比較するコンパレータを返すよ
thenComparing 辞書式順序コンパレータをもう一方のコンパレータとともに返すよ
naturalOrder 自然な順序でComparableオブジェクトを比較するコンパレータを返すよ(defaltメソッド)
nullsFirst nullをnull以外より小さいとみなす、nullフレンドリのコンパレータを返すよ
nullsFirst nullをnull以外より大きいとみなす、nullフレンドリのコンパレータを返すよ
reversed このコンパレータの逆順を義務付けるコンパレータを返すよ(defaltメソッド)
reverseOrder 自然順序付けの逆を義務付けるコンパレータを返すよ

そして、興味を持ったら是非Java APIドキュメントをご参照ください。
私の説明は雑ですので(笑)
Java APIドキュメント - Comparatorインターフェース

④実はComparatorでなくても良い件

ここまでComparatorを使用した例をみてきましたが、Comparatorと同じ作りであれば、実は他のメソッドでも問題なく利用できちゃうぽいです。
何やねん、って感じましたが、恐らくComparatorのstaticメソッド使うのと同じノリなんだと思われます。

それでは実際に例を見てみましょう。
まず、Compartorの代わりになるなんちゃってクラスを作成します。

class LengthChecker {
    public static int check(Book b1, Book b2){         // Comparator実装時にオーバーライドするcompareメソッドと違いstaticメソッドにできる
        return b1.title.length() - b2.title.length();  // title文字数の昇順
    }
}

※軽くジェネリック使って書けると格好良いかもしれません。が、分かりにくくなるので直接的な表現にします。

作成したらComparatorの代わりにメソッドに引数として渡してみます。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //ソート対象を作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(2, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        // ソートを実行する
        bookList.sort(LengthChecker::check);  // ← Comparatorと違いメソッドまで指定が必要

        // ソート結果を表示する
        bookList.forEach(s -> System.out.println(s.id + ":" + s.title));
    }
}

<実行結果>
2:ハウルの動く城
1:ハリーポッター
2:ハムスター飼育日記

上手くいってまうんやぁ...えぇあxあぁsdf...。
基本的に引数でComparator指定となっている所は置き換えられるぽいです。
この辺の仕様について調べきれていませんが、問題として出ていたので「へ〜」くらいには覚えておくのが良いのかもしれません。
ただ、制約があるぽいです。色々試した結果、多分こんな感じ。

主な利用クラスやインターフェースのご紹介

ComparableやComparatorを使用して並び替えや大小要素取得などを行なっているメソッドを紹介します。超簡単に書いているので詳細はJavaのAPIドキュメントを参照ください。
と言うより、この期にJavaのAPIドキュメントに慣れておくのを推奨します!!!

Collection/Mapインターフェース

このインターフェースを実装したクラスやdefault・staticメソッドで非常によく使われています。
なお、前項で出てきたBookクラスが実装されている想定で読んでくさい〜。

①Listインターフェース(defaltメソッド)

デフォルトメソッドとして並び替えのメソッドが用意されています。

# メソッド名 Comparable Comparator 説明
sort - ソートするよ

実装サンプル

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //ソート対象を作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        // Comparatorにソート
        bookList.sort(new Book());

    }
}

Listインターフェースの詳細はコチラ

②Collectionsクラス(staticメソッド)

Collectionsインターフェースの実装クラスで、定義されているのは全てstaticメソッドです。

# メソッド名 Comparable Comparator 説明
sort ソートするよ
reverseOrder sortの引数に指定。逆順にソートするよ
reverse 逆順にソートするよ
min ソートした結果の最小要素を返却するよ
max ソートした結果の最大要素を返却するよ

実装サンプル

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //ソート対象を作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(3, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        // Comparableにソート
        Collections.sort(bookList);
        Collections.sort(bookList, Collections.reverseOrder());
        Collections.reverse(bookList);
        Collections.min(bookList);
        Collections.max(bookList);

        // Comparatorにソート
        Collections.sort(bookList, new Book());
        Collections.sort(bookList, Collections.reverseOrder(new Book()));
        Collections.min(bookList, new Book());
        Collections.max(bookList, new Book());
    }
}

Collectionsクラスの詳細はコチラ

③Arraysクラス

配列を操作するための様々なメソッドが提供されています。
紹介するメソッドは全てstaticメソッドです。

# メソッド名 Comparable Comparator 説明
sort ソートするよ
parallelSort ソートするよ?
binarySearch ソートした後、検索するよ?

実装サンプル
型ごとに、めちゃくちゃ一杯種類がありますが、はしょります。
詳細はJavaのAPIドキュメントをご参照くださいませ。

sample.java
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        // ソート対象の配列を作成
        String[] array = {"ハム助","ハム子","ハム二郎"};

        // Comparableにソート
        Arrays.sort(array);
        Arrays.parallelSort(array);
        Arrays.binarySearch(array, "ハム助");  //自然順序づけで並び替え後に、最初に出てくる"ハム助"のindex番号を教えてくれる

        // Comparatorを作成
        Comparator<String> comparator = new Comparator<String>(){
            @Override
            public int compare(String s1, String s2) {
                return s1.length() - s2.length();
            }
        };

        // Comparatorにソート
        Arrays.sort(array, comparator);
        Arrays.parallelSort(array, comparator);
        Arrays.binarySearch(array, "ハム助", comparator);  // comparatorで並び替えた後に最初に出てくる"ハム助"のindex番号を教えてくれる
    }
}

Arraysクラスの詳細はコチラ

④TreeSet/TreeMapクラス

標準で並び替えサポートされたコレクションであるため、コンストラクタ呼び出し時に並び替えが実行されます。その後も、要素の追加のたびにComparableやComparatorが作動します。

【ポイント】
自然順序付けに基づく場合、Comparableの影を全く感じませんが、該当クラスにComparableが実装されていないと例外が発生することに注意です!!!

実装サンプル
同じような感じになるのでTreeSetのみサンプル掲載。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {

        // Comparatorを作成
        Comparator<Book> comparator = new Comparator<Book>(){
            @Override
            public int compare(Book b1, Book b2) {
                return b1.getTitle().length() - b2.getTitle().length();  // タイトル文字数の長い順
            }
        };

        // Comparableに作成
        TreeSet<Book> book = new TreeSet<>();
        book.add(new Book(2, "とっとこハム二郎"));
        book.add(new Book(3, "ハム助の知らない世界"));
        book.add(new Book(1, "月曜からハムまつり"));
        System.out.println("<Comparable>");
        book.forEach( s -> System.out.println(s.getId() + ":" + s.getTitle()));

        // Comparatorに作成
        TreeSet<Book> book2 = new TreeSet<>(comparator);
        book2.add(new Book(2, "とっとこハム二郎"));
        book2.add(new Book(3, "ハム助の知らない世界"));
        book2.add(new Book(1, "月曜からハムまつり"));
        System.out.println("<Comparator>");
        book2.forEach( s -> System.out.println(s.getId() + ":" + s.getTitle()));
    }
}
実行結果
<Comparable>
1:月曜からハムまつり
2:とっとこハム二郎
3:ハム助の知らない世界
<Comparator>
2:とっとこハム二郎
1:月曜からハムまつり
3:ハム助の知らない世界

TreeSetの詳細はコチラ/TreeMapの詳細はコチラ

StreamAPIインターフェース

ストリーム化したオブジェクトに対して並び替えなどの操作を行うことが可能です。
この中で前出のCollectionやMapインターフェース軍も使うことも可能です。

# メソッド名 Comparable Comparator 説明
sort ソート
min ソートした結果の最小要素をOption型で返却
max ソートした結果の最大要素をOption型で返却

実装サンプル

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) throws Exception {

        //ソート対象を作成
        List<Book> bookList = new ArrayList<>();
        bookList.add(new Book(2, "ハムスター飼育日記"));   
        bookList.add(new Book(2, "ハウルの動く城"));
        bookList.add(new Book(1, "ハリー・ポッター"));

        // Comparatorを作成
        Comparator<Book> comparator = new Comparator<Book>(){
            @Override
            public int compare(Book b1, Book b2) {
                return b1.getTitle().length() - b2.getTitle().length();  // タイトル文字数の長い順
            }
        };

        // Comparableにソート
        System.out.println("<Comparable:タイトルの短い順>");
        bookList.stream().sorted().forEach(s -> System.out.println(s.getId() + ":" + s.getTitle()));

        // Comparatorにソート
        System.out.println("<Comparator:タイトルの短い順>");
        bookList.stream().sorted(comparator).forEach(s -> System.out.println(s.getId() + ":" + s.getTitle()));  // ←ココ
        Book minBook = bookList.stream().min(comparator).get();         // ←ココ
        System.out.println("<一番タイトルの短いBook>");
        System.out.println(minBook.getId() + ":" + minBook.getTitle());
        Book maxBook = bookList.stream().max(comparator).get();         // ←ココ
        System.out.println("<一番タイトルの長いBook>");
        System.out.println(maxBook.getId() + ":" + maxBook.getTitle()); 
    }
}
<実行結果>
Comparable:タイトルの短い順
2:ハムスター飼育日記
2:ハウルの動く城
1:ハリーポッター
Comparator:タイトルの短い順
2:ハウルの動く城
1:ハリーポッター
2:ハムスター飼育日記
一番タイトルの短いBook
2:ハウルの動く城
一番タイトルの長いBook
2:ハムスター飼育日記

StreamAPIの詳細はコチラ

最後に振り返りクイズ

JavaSE8Goldぽく問題出してみます。
まず、以下のコードがあります。

  List<Book> bookList = new ArrayList<>();
  bookList.add(new Book(1, "ハム助"));
  bookList.add(new Book(2, "ハム太郎"));
  bookList.add(new Book(3, "ハム次郎"));

  Collections.sort(bookList); // ← ここ。

このプログラムを実行させて、Book.idの昇順に並び替えるためにBookクラスに実装すべきコードはどれでしょう〜。

// ①
    public int compareTo(Book book){
        return this.id.compareTo(book.id);
    }

// ②
    public boolean compareTo(Book book){
        return this.id.compareTo(book.id);
    }

// ③
    public int compare(Book book1, Book book2){
        return book1.id - book2.id;
    }

// ④
    public boolean compare(Book book1, Book book2){
        return book1.id > book2.id;
    }




正解は①です!
引数にComparatorを渡していないので、自然順序付け(Comparable)によるソートですね。
Comparableの抽象メソッドはcompareToです。
また、引数は1つで、返り値はint型です。

まとめ

試験が終わった後だと、ゆっくり振り返えることができますが、試験前は黒本周回とインプットに忙しくて中々に余裕がないですよね^^;
...試験前にやってたら、もっと点数取れた気がしてならない(TvT)

参考文献

Java SE8 APIドキュメント達を参考にしました

Comparableインターフェース
Comparatorインターフェース
Collectionsクラス
Listインターフェース
TreeMapクラス
TreeSetクラス
Arraysクラス
StreamAPIクラス

また、試験勉強として紫本と黒本も読んでいたのでソチラの知識も融合しています。

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

【Java入門】演算子について(算術演算子、複合代入演算子、関係演算子、論理演算子)

目的

Java言語を含めたプログラミングの学習を始めたばかりの方、既学習者の方は復習用に、
今回は演算子について学ぶために書いています。

【Java入門目次】
変数と型
型変換
変数のスコープ
文字列の操作
配列の操作
・演算子(準備中) ←今ここ
条件分岐
・繰り返し処理(準備中)
・クラスについて(準備中)
・抽象クラス(準備中)
・インターフェース(準備中)
・カプセル化(準備中)
・モジュールについて(準備中)
例外処理について
ラムダ式について
Stream APIについて

演算子とは

ソースコード内で値に対して加算減算などの四則演算を行ったり、値の比較を行う場合に演算子を使います。
多くの演算子がありますが、いくつか見ていきましょう。

算術演算子

加減乗除を行う演算子です。
演算対象となる変数やリテラルの事をオペランドとも呼びます。

以下は算術演算子の演算子と記述例です。

演算子 記述例 説明
+ a + b aとbを加算する
- a - b aからbを減算する
* a * b aとbを乗算する
/ a / b aをbで除算する
% a % b aをbで除算した余りをだす

実際に整数と整数で演算を行った結果は以下になります。

算術演算子の例
int a = 100;
int b = 50;

System.out.println(a + b); // 150
System.out.println(a - b); // 50
System.out.println(a * b); // 500
System.out.println(a / b); // 2
System.out.println(a % b); // 0

違う型同士で演算を行った時

整数と整数で演算を行った結果は整数になります。
しかし、整数と浮動小数点数で演算を行った結果浮動小数点数になります。

Javaによって演算時に型変換が行われるためです。(型変換についての記事はこちら)

整数と浮動小数点数で演算をした例
int a = 3;
double b = 5.0;
System.out.println(a + b); // 8.0

0で除算を行った時

整数や浮動小数点数に0で除算を行うとエラーが発生したり、非数、無限大が出力されるので気をつけましょう。

0除算の例
int a = 8;
System.out.println(a / 0); // java.lang.ArithmeticException: / by zero エラー
System.out.println(a % 0); // java.lang.ArithmeticException: / by zero エラー

double b = 8.0;
System.out.println(b / 0); // Infinity
System.out.println(b % 0); // NaN

優先度

演算子には優先度があり、1行の式の中に複数演算子があった場合に優先度の高いものから処理がなされます。

*、 /、 %+、 -に比べて優先度は高いです。
決められた優先度より先に演算したい場合は、()で囲む事で優先的に演算をさせる事が出来ます。

優先度の例
// 乗算が先に行われる
int num = 2 * 5 + 1;
System.out.println(num); // 11

// +(加算) を先に行いたい場合
int num2 = 2 * (5 + 1);
System.out.println(num2); // 12

結合規則

また、優先度が同じ場合には結合規則でどの演算子から計算するか決められます。
右結合は、右から左へ計算していきます。
→単項演算子、三項演算子、代入演算子が該当します。

左結合は、左から右へ計算していきます。
→算術演算子や論理演算子が該当します。

結合規則の例
String str = "Hello";
int a = 50, b = 50;

// 文字列+数字で文字列結合が行われ、「Hello50」となり、+50の処理が行われる。
System.out.println(str + a + b); // Hello5050

// a+bの足し算が行われ、「100」となり、+Helloの処理が行われる。
System.out.println(a + b + str); // 100Hello

優先度結合規則を意識して扱わなければいけませんね。

単項演算子

++(インクリメント)--(デクリメント)と言います。
1つの値または変数の加算、減算を行う演算子です。

演算子 記述例 説明
++ ++a、 a++ aに1を加算する
-- --a、 a-- aに1を減算する

前置/後置の違い

演算子変数の前に配置するのか、後ろに配置するのかで実行結果が変わってきます。

まずは、前置の場合。

前置の場合
int a = 10;
int b = ++a;
System.out.println(a); // 11
System.out.println(b); // 11

a、b共に11になっています。
前置の場合他の処理よりも前にインクリメントが行われ、bに代入されるため両方とも11になります。

次に後置の場合。

後置の場合
int a = 10;
int b = a++;
System.out.println(a); // 11
System.out.println(b); // 10

aは11ですが、bは10になっています。
後置の場合他の処理が済んでからインクリメントが行われるため、10をbに代入した後にaが11になります。

前置、後置の挙動の違いには注意しなければいけませんね。

代入演算子と複合代入演算子

代入演算子は=を用いて右辺の値を左辺に代入する演算子です。
a = b (aにbを代入する)

また、複合代入演算子は、代入演算子と+-などの演算子を組み合わせた演算子です。

演算子 記述例 演算子 算術演算子の場合
+= a+=b aにbを加えた値をaに代入する a = a + b
-= a-=b aからbを引いた値をaに代入する a = a - b
*= a*=b aにbを乗じた値をaに代入する a = a * b
/= a/=b aをbで割った値をaに代入する a = a % b
%= a%=b aをbで割った余りをaに代入する a = a % b

複合代入演算子を用いた結果は以下になります。

複合演算子の例
int a = 10, b = 3;

a += b;
System.out.println(a); // 13

a -= b;
System.out.println(a); // 10

a *= b;
System.out.println(a); // 30

a /= b;
System.out.println(a); // 10

a %= b;
System.out.println(a) // 1

関係演算子

2つの値を比較する際に使用する演算子です。
関係演算子を使用した結果は、boolean値であるtrueまたはfalseが返ってきます。

演算子 記述例 説明
== a==b aとbの値が等しければtrue、異なればfalse
!= a!=b aとbの値が異なればtrue、等しければfalse
> a>b aの値がbの値より大きければtrue、以下ならばfalse
>= a>=b aの値がbの値以上であればtrue、小さければfalse
< a<b aのbの値より小さければtrue、以上ならばfalse
<= a<=b aの値がbの値以下であればtrue、大きければfalse

関係演算子を用いた例は以下になります。

関係演算子の例
int a = 50, b = 30, c = 50;

System.out.println(a == b); // false
System.out.println(a == c); // true

System.out.println(a != b); // true
System.out.println(a != c); // false

System.out.println(a < b); // false
System.out.println(a <= c); // true

System.out.println(a > b); // true
System.out.println(a >= c); // true

論理演算子

2つ以上の条件を元に評価を行う際に使用する演算子です。
論理演算子を使用した結果は、boolean値であるtrueまたはfalseが返ってきます。

演算子 記述例 説明
& a&b aとbが共にtrueの時にtrue、そうでなければfalse
aがfalseであったとしてもbは評価される
&& a&&b aとbの共にtrueの時にtrue、そうでなければfalse
aがfalseならbは評価されず結果がfalseとなり、aがtrueならbも評価され結果を返す
| a|b aとbいずれかがtrueならtrue、そうでなければfalse
aがtrueであったとしてもbは評価される
|| a||b aとbいずれかがtrueならtrue、そうでなければfalse
aがtrueならbは評価されず結果がtrueとなり、
aがfalseならbも評価され結果を返す
^ a^b aとbの値が異なる時true、そうでなければfalse
! !a aの値がtrueの時false、falseの時true

&、|を用いた例

and条件(&&)とor条件(||)は、今までの演算子に比べてやや複雑ですので1つずつ深掘りしていきます。
(ビット演算の例は今回割愛します)

&&の例

&&の例
boolean result = c++ > 50 && ++d > 50;
System.out.println(result); // false
System.out.println("c: " + c + ", d: " + d); // c: 51, d: 50

1つ目の条件は後置インクリメントなので、50 > 50 で比較が行われ、結果はfalseが返ります。その後、cはインクリメントされます。

&&の場合1つ目の条件がfalseであった場合、2つ目の条件が評価されません。そのためdのインクリメントの処理は実行されません。
1つ目の条件がfalseであったため、resultにはfalseが代入され、cは51、dは50となります。

||の例

||の例
int c = 50, d = 50;

boolean result = ++c > 50 || ++d > 50;
System.out.println(result); // true
System.out.println("c: " + c + ", d: " + d); // c: 51, d: 50

1つ目の条件は前置インクリメントなので、51 > 50で比較が行われ、結果はtrueが返ります。

||の場合1つ目の条件がtrueであった場合、2つ目の条件が評価されません。そのためdのインクリメントの処理は実行されません。
1つ目の条件がtrueであったため、resultにはtrueが代入され、cは51、dは50となります。

上記の通り、それぞれの動作の違いを把握して注意しなければいけませんね。

終わりに

演算子の一部を紹介してみました。演算子は使用頻度が高いためしっかりと理解しておきたいですね。
今回紹介出来なかったものについては別記事で紹介出来たらと思います。

参考サイト

算術演算子とは
【超基本】Javaの論理演算子の使い方

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

Java 13の新機能まとめ

Java 13が2019/9/17にリリースされています。
Java SE 13 Platform JSR 388

ダウンロード

OpenJDKサイトからダウンロードできていました。
https://jdk.java.net/13/

現在はアーカイブサイトからダウンロードできます。
https://jdk.java.net/archive/

LTSではないので、検証目的以外には今からダウンロードする必要もないと思いますけど。
MacやLinuxでのインストールにはSDKMAN!をお勧めします

Oracle OpenJDK以外に無償で商用利用できるディストリビューションとしては、次のようなものがあります。

LTSではないのでAmazon Correttoではリリースされないようです。Liberica JDKもいまはダウンロードできなくなっていますね。

Oracle JDKは開発用途には利用できますが、商用利用にはJava SE Subscriptionを購入する必要があります。

JEP

変更はJEPとしてまとまっています。
https://openjdk.java.net/projects/jdk/13/

今回は5つほどJEPが入っています。
JEP 350: Dynamic CDS Archives
JEP 351: ZGC: Uncommit Unused Memory (Experimental)
JEP 353: Reimplement the Legacy Socket API
JEP 354: Switch Expressions (Second Preview)
JEP 355: Text Blocks (Preview)

JEP 355: Text Blocks (Preview)

複数行の文字列リテラルがプレビューとして導入されました。

String html = """
              <html>
                  <body>
                      <p>Hello, world</p>
                  </body>
              </html>
              """;

スペースのエスケープなどが追加されて、Java 15で標準になります。
詳しい仕様はJava 14の説明のほうをみてください。
Java 14新機能まとめ - Qiita

JEP 354: Switch Expressions (Second Preview)

Switch文を式にします。

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    case THURSDAY, SATURDAY     -> 8;
    case WEDNESDAY              -> 9;
};

Java 12と比べると、switch式で値を返す構文がbreakからyieldになりました。

Java 14で標準化されています。
詳しい仕様はJava 14の説明のほうをみてください。
Java 14新機能まとめ - Qiita

JEP 350: Dynamic CDS Archives

-XX:ArchiveClassesAtExitをつけることで実行時にCDSアーカイブを作成します。

使い方はこちらが参考になると思います。

Java 13のDynamic CDSで想像以上に起動速度が速くなった - きしだのHatena

試した感じでは、実行時ではなくふつうにCDSアーカイブ作った方が起動が速かった。
Dynamic CDSよりJava10からある自力ダンプの方が起動が速い - きしだのHatena

JEP 351: ZGC: Uncommit Unused Memory (Experimental)

ZGCでつかってないメモリが解放されるようになります。

JEP 353: Reimplement the Legacy Socket API

TCPソケットAPIを実装しなおして、Project Loomでの非同期処理に対応しています。

API

APIもいくつか変更されています。

String

TextBlocks関連のAPIが追加されています。

ただし、Java 13の時点では@Deprecated指定されているだけでいつでも使えますが、Java 14では--enable-previewをつけたときにだけ使えるようになっています。

formatted

String.formatをインスタンスメソッドとして使えるようにする。

たとえば

callQuery(String.format("select * from table where id=%d", id));

と書いていたものが

callQuery("select * from table where id=%d".formatted(id));

のように書けて便利。
https://bugs.openjdk.java.net/browse/JDK-8203444

translateEscapes

\nなどをエスケープ文字として扱う。
コードを見るとエスケープが入り混じってまぎらわしいですが、文字列中の\nが改行に変換されています。

jshell> var s = "test\\ntest\\n"
s ==> "test\\ntest\\n"

jshell> System.out.println(s)
test\ntest\n

jshell> s.translateEscapes()
$18 ==> "test\ntest\n"

jshell> System.out.println(s.translateEscapes())
test
test

https://bugs.openjdk.java.net/browse/JDK-8223780

stripIndent

インデントを取り除きます。

https://bugs.openjdk.java.net/browse/JDK-8223775

Map.ofで要素数1のMapに対するget(null)がぬるぽ

JDK9でMap.ofが追加されています。
JDK9、JDK10では要素数0のときはMap0、要素数1のときMap1、要素数2以上のときMapNのインスタンスが返っていて、Map.of().get(null)nullを返していました。

C:\Users\naoki>java\jdk\jdk-10.0.1\bin\jshell
|  JShellへようこそ -- バージョン10.0.1
|  概要については、次を入力してください: /help intro

jshell> Map.of().get(null)
$1 ==> null

jshell> Map.of(1,2).get(null)
$2 ==> null

jshell> Map.of(1,2,3,4).get(null)
|  java.lang.NullPointerException thrown
|        at ImmutableCollections$MapN.probe (ImmutableCollections.java:779)
|        at ImmutableCollections$MapN.get (ImmutableCollections.java:721)
|        at (#3:1)

JDK11で要素数0の場合もMapNを使うようになってMap.of().get(null)がぬるぽになってます。

C:\Users\naoki>java\jdk\jdk-11.0.1\bin\jshell
|  JShellへようこそ -- バージョン11.0.1
|  概要については、次を入力してください: /help intro

jshell> Map.of().get(null)
|  例外java.lang.NullPointerException
|        at Objects.requireNonNull (Objects.java:221)
|        at ImmutableCollections$MapN.get (ImmutableCollections.java:843)
|        at (#1:1)

jshell> Map.of(1,2).get(null)
$2 ==> null

jshell> Map.of(1,2,3,4).get(null)
|  例外java.lang.NullPointerException
|        at ImmutableCollections$MapN.probe (ImmutableCollections.java:926)
|        at ImmutableCollections$MapN.get (ImmutableCollections.java:846)
|        at (#3:1)

Map.of(1,2).get(null)の場合はJDK9からJDK12までnullを返していました。
JDK13からはMap.of(1,2).get(null)も ぬるぽになります。

以前のバージョンもJDK12.0.2や、JDK11.0.4で修正されています。

http://mail.openjdk.java.net/pipermail/core-libs-dev/2019-April/059533.html

ここで、nullチェックのためにrequireNonNullじゃなくてequals呼び出しを使ってるところも面白いですね。

http://cr.openjdk.java.net/~smarks/reviews/8221924/webrev.0/src/java.base/share/classes/java/util/ImmutableCollections.java.patch

Numberのデフォルトコンストラクタ

http://mail.openjdk.java.net/pipermail/core-libs-dev/2019-May/060258.html

Numberクラスにデフォルトコンストラクタが追加されました。
といっても実装はこんな感じです。

public Number() {super();}

これ、定義しなくても暗黙で定義されるので不要ですよね。
なんかlintをだまらせるために追加したっぽい

JapaneseEraへの令和の追加

2019/5/1に新しい元号「令和」が始まりました。
Java 8のアップデートやJava 11、Java 12でもフォーマットやパースでは令和に対応していたのですが、JapaneseEraには正式には定義されていませんでした。
Java 13からJapaneseEra.REIWAが使えるようになりました。

Java 11では元号は決まっていなかったのでNewEraとして表示されていました。

jshell> java.time.chrono.JapaneseDate.now()
$1 ==> Japanese NewEra 2-07-21

11.0.3では元号が決まっていたのでREIWAという表示になりました。

jshell> java.time.chrono.JapaneseDate.of(2020,5,1)
$1 ==> Japanese Reiwa 2-05-01

これに対するJapaneseEra.NEWERAJapaneseEra.REIWAも定義されてはいたのですが、package privateになっていてコードから使えなかったのですが、Java 13からはpublicになって誰でも使えるようになりました。

ツール

rmicに少し変更が入っています。

rmicで--preview-featureがついたものを通さないようにする

http://mail.openjdk.java.net/pipermail/core-libs-dev/2019-April/059683.html

rmicをdeprecatedに。そしてそのうち消す

[JDK-8217412] deprecate rmic for removal - Java Bug System

Java 15で消えています。

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

Java 13新機能まとめ

Java 13が2019/9/17にリリースされています。
Java SE 13 Platform JSR 388

ダウンロード

OpenJDKサイトからダウンロードできていました。
https://jdk.java.net/13/

現在はアーカイブサイトからダウンロードできます。
https://jdk.java.net/archive/

LTSではないので、検証目的以外には今からダウンロードする必要もないと思いますけど。
MacやLinuxでのインストールにはSDKMAN!をお勧めします

Oracle OpenJDK以外に無償で商用利用できるディストリビューションとしては、次のようなものがあります。

LTSではないのでAmazon Correttoではリリースされないようです。

Oracle JDKは開発用途には利用できますが、商用利用にはJava SE Subscriptionを購入する必要があります。

JEP

変更はJEPとしてまとまっています。
https://openjdk.java.net/projects/jdk/13/

今回は5つほどJEPが入っています。
JEP 350: Dynamic CDS Archives
JEP 351: ZGC: Uncommit Unused Memory (Experimental)
JEP 353: Reimplement the Legacy Socket API
JEP 354: Switch Expressions (Second Preview)
JEP 355: Text Blocks (Preview)

JEP 355: Text Blocks (Preview)

複数行の文字列リテラルがプレビューとして導入されました。

String html = """
              <html>
                  <body>
                      <p>Hello, world</p>
                  </body>
              </html>
              """;

スペースのエスケープなどが追加されて、Java 15で標準になります。
詳しい仕様はJava 14の説明のほうをみてください。
Java 14新機能まとめ - Qiita

JEP 354: Switch Expressions (Second Preview)

Switch文を式にします。

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    case THURSDAY, SATURDAY     -> 8;
    case WEDNESDAY              -> 9;
};

Java 12と比べると、switch式で値を返す構文がbreakからyieldになりました。

Java 14で標準化されています。
詳しい仕様はJava 14の説明のほうをみてください。
Java 14新機能まとめ - Qiita

JEP 350: Dynamic CDS Archives

-XX:ArchiveClassesAtExitをつけることで実行時にCDSアーカイブを作成します。

使い方はこちらが参考になると思います。

Java 13のDynamic CDSで想像以上に起動速度が速くなった - きしだのHatena

試した感じでは、実行時ではなくふつうにCDSアーカイブ作った方が起動が速かった。
Dynamic CDSよりJava10からある自力ダンプの方が起動が速い - きしだのHatena

JEP 351: ZGC: Uncommit Unused Memory (Experimental)

ZGCでつかってないメモリが解放されるようになります。

JEP 353: Reimplement the Legacy Socket API

TCPソケットAPIを実装しなおして、Project Loomでの非同期処理に対応しています。

API

APIもいくつか変更されています。

String

TextBlocks関連のAPIが追加されています。

ただし、Java 13の時点では@Deprecated指定されているだけでいつでも使えますが、Java 14では--enable-previewをつけたときにだけ使えるようになっています。

formatted

String.formatをインスタンスメソッドとして使えるようにする。

たとえば

callQuery(String.format("select * from table where id=%d", id));

と書いていたものが

callQuery("select * from table where id=%d".formatted(id));

のように書けて便利。
https://bugs.openjdk.java.net/browse/JDK-8203444

translateEscapes

\nなどをエスケープ文字として扱う。
コードを見るとエスケープが入り混じってまぎらわしいですが、文字列中の\nが改行に変換されています。

jshell> var s = "test\\ntest\\n"
s ==> "test\\ntest\\n"

jshell> System.out.println(s)
test\ntest\n

jshell> s.translateEscapes()
$18 ==> "test\ntest\n"

jshell> System.out.println(s.translateEscapes())
test
test

https://bugs.openjdk.java.net/browse/JDK-8223780

stripIndent

インデントを取り除きます。

https://bugs.openjdk.java.net/browse/JDK-8223775

Map.ofで要素数1のMapに対するget(null)がぬるぽ

JDK9でMap.ofが追加されています。
JDK9、JDK10では要素数0のときはMap0、要素数1のときMap1、要素数2以上のときMapNのインスタンスが返っていて、Map.of().get(null)nullを返していました。

C:\Users\naoki>java\jdk\jdk-10.0.1\bin\jshell
|  JShellへようこそ -- バージョン10.0.1
|  概要については、次を入力してください: /help intro

jshell> Map.of().get(null)
$1 ==> null

jshell> Map.of(1,2).get(null)
$2 ==> null

jshell> Map.of(1,2,3,4).get(null)
|  java.lang.NullPointerException thrown
|        at ImmutableCollections$MapN.probe (ImmutableCollections.java:779)
|        at ImmutableCollections$MapN.get (ImmutableCollections.java:721)
|        at (#3:1)

JDK11で要素数0の場合もMapNを使うようになってMap.of().get(null)がぬるぽになってます。

C:\Users\naoki>java\jdk\jdk-11.0.1\bin\jshell
|  JShellへようこそ -- バージョン11.0.1
|  概要については、次を入力してください: /help intro

jshell> Map.of().get(null)
|  例外java.lang.NullPointerException
|        at Objects.requireNonNull (Objects.java:221)
|        at ImmutableCollections$MapN.get (ImmutableCollections.java:843)
|        at (#1:1)

jshell> Map.of(1,2).get(null)
$2 ==> null

jshell> Map.of(1,2,3,4).get(null)
|  例外java.lang.NullPointerException
|        at ImmutableCollections$MapN.probe (ImmutableCollections.java:926)
|        at ImmutableCollections$MapN.get (ImmutableCollections.java:846)
|        at (#3:1)

Map.of(1,2).get(null)の場合はJDK9からJDK12までnullを返していました。
JDK13からはMap.of(1,2).get(null)も ぬるぽになります。

以前のバージョンもJDK12.0.2や、JDK11.0.4で修正されています。

http://mail.openjdk.java.net/pipermail/core-libs-dev/2019-April/059533.html

ここで、nullチェックのためにrequireNonNullじゃなくてequals呼び出しを使ってるところも面白いですね。

http://cr.openjdk.java.net/~smarks/reviews/8221924/webrev.0/src/java.base/share/classes/java/util/ImmutableCollections.java.patch

Numberのデフォルトコンストラクタ

http://mail.openjdk.java.net/pipermail/core-libs-dev/2019-May/060258.html

Numberクラスにデフォルトコンストラクタが追加されました。
といっても実装はこんな感じです。

public Number() {super();}

これ、定義しなくても暗黙で定義されるので不要ですよね。
なんかlintをだまらせるために追加したっぽい

JapaneseEraへの令和の追加

2019/5/1に新しい元号「令和」が始まりました。
Java 8のアップデートやJava 11、Java 12でもフォーマットやパースでは令和に対応していたのですが、JapaneseEraには正式には定義されていませんでした。
Java 13からJapaneseEra.REIWAが使えるようになりました。

Java 11では元号は決まっていなかったのでNewEraとして表示されていました。

jshell> java.time.chrono.JapaneseDate.now()
$1 ==> Japanese NewEra 2-07-21

11.0.3では元号が決まっていたのでREIWAという表示になりました。

jshell> java.time.chrono.JapaneseDate.of(2020,5,1)
$1 ==> Japanese Reiwa 2-05-01

これに対するJapaneseEra.NEWERAJapaneseEra.REIWAも定義されてはいたのですが、package privateになっていてコードから使えなかったのですが、Java 13からはpublicになって誰でも使えるようになりました。

ツール

rmicに少し変更が入っています。

rmicで--preview-featureがついたものを通さないようにする

http://mail.openjdk.java.net/pipermail/core-libs-dev/2019-April/059683.html

rmicをdeprecatedに。そしてそのうち消す

[JDK-8217412] deprecate rmic for removal - Java Bug System

Java 15で消えています。

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

Effective Javaを読む 第2章 項目1 コンストラクタの代わりにstaticファクトリーメソッドを検討する

はじめに

Javaの理解を深めるならこれを読めということで、Effective Javaを自分なりに解釈しながら読んでいこうと思います。
読んでるのは積読していた第2版です。
https://www.amazon.co.jp/EFFECTIVE-JAVA-Java-Joshua-Bloch/dp/4621066056/ref=pd_sbs_14_3/355-5139262-7829161?_encoding=UTF8&pd_rd_i=4621066056&pd_rd_r=ac861412-beae-43a8-872a-8b853aa69980&pd_rd_w=oIuWA&pd_rd_wg=nhmjU&pf_rd_p=7642417c-6494-4d06-a2b0-fcb0e0b3c563&pf_rd_r=HAEC02ASTQVJ4SPSM92Q&psc=1&refRID=HAEC02ASTQVJ4SPSM92Q

コンストラクタの代わりにstaticファクトリーメソッドを検討する

今までコンストラクタを使って実装していた処理を、staticファクトリーメソッドに置き換えてみたら色々嬉しいことあるんじゃないですかねっていってる節

サンプルコード

例1
public static Boolean valueOf(boolean b){
    return b ? Boolean.TRUE : Boolean.FALSE;
}
例2
//サービスプロバイダーフレームワーク
public interface Service{
    //サービス固有のメソッドをここに
}

//サービスプロバイダーインターフェース
public interface Provider{
    Service newService();
}

//サービス登録とアクセスのためのインスタンス化不可能クラス
public class Service{
    private Service(){} //インスタンス化を抑制(項目4)

    //サービス名をサービスと対応づける
    private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
    public static final String DEFAULT_PROVIDER_NAME = "<def>";

    //プロバイダー登録API
    public static void registerDefaultProvider(Provider p){
        registerProvider(DEFAULT_PROVIDER_NAME,p);
    }
    public static void registerProvider(String name,Provider p){
        providers.put(name,p);
    }

    //サービスアクセスAPI
    public static Service newInstance(){
        return newInstance(DEFAULT_PROVIDER_NAME);
    }
    public static Service newInstance(String name){
        Provider p = providers.get(name);
        if (p == null)
            throw new IllegalArgumentException(
                    "Nn provider registered with name:" + name
            );
        return p.newService();
    }

}

用語集

コンストラクタ

・コンストラクタとは、クラスのインスタンスを作成するときに実行される処理のこと

SampleClass instanceA = new SampleClass();

↑newの後ろのSampleClasss()の記述がコンストラクタの呼び出し

・コンストラクタの書き方:クラス名と同じにする

public class SampleClass {
  //コンストラクタ
  public SampleClass(){
    System.out.println("コンストラクタだよー");
  }
}

↑これでnewした時に「コンストラクタだよー」が標準出力される

・メソッドと違ってコンストラクタは値を返さない(returnを書くとエラーになる)

static

・インスタンスに依存しないメソッドや変数を作るための修飾詞

staticファクトリーメソッド

・staticファクトリーメソッドとは、オブジェクトを返す単なるstaticのメソッドのこと
・例1ではboolean基本データ値(b)をBooleanオブジェクトに変換している

デザインパターンのファクトリーメソッド

・名前が似てるけどstaticファクトリーメソッドとは別物
・ここでは説明は割愛

インターフェース

・インターフェースとは、クラス内にあるメソッドの具体的な処理は書かずに「変数」または「メソッドの型」を記述したもの
・「すべてのメソッドが抽象メソッドであること」「基本的にフィールドを1つも持っていないこと」が条件
・処理内容を具体的に書いていないため、使いたいときに処理内容を実装すればい良いので、将来的に処理の変更が起こりうる場合などに便利(細かいところを後回しにできる)

staticファクトリーメソッドの4つの長所と2つの短所

長所1 コンストラクタと異なり名前を持つ

・コンストラクタはnewすると勝手に実行されちゃうけど、staticファクトリーメソッドならいい感じの名前が付けられるので可読性があがる

例えば確率的素数(probable prime)であるBigIntegerを返す処理があるとして、

-コンストラクタでの書き方

BigInteger(int, int, Random)

-staticファクトリーメソッドでの書き方

BigInteger.probablePrime

…どっちがわかり易い?

長所2 コンストラクタと異なりメソッドが呼び出されるごとに新たなオブジェクトを生成する必要がない

・当たり前だけど、staticなので何にもインスタンス化していない
 コンストラクタを呼び出すためにはnewしなくちゃいけない
・不必要に新しいオブジェクトをたくさん作っちゃうのはあんまりよろしくないよねって話

長所3 コンストラクタと異なりメソッドの戻り値型の任意のサブタイプのオブジェクトでも返すことができる

・コンストラクタは戻り値を返さないものだけど、staticファクトリーメソッドはあくまでメソッドなのでreturnすることができるので柔軟性がある
・例2のようなサービスプロバイダーフレームワーク(中身を隠蔽してAPIとして使ってもらう)もつくれちゃうぜ

長所4 パラメータ化された型のインスタンス生成の面倒さを低減する

・冗長な書き方を簡略化できる
例えばコンストラクタだとこう書かなきゃいけなかったものが

Map<String, List<String>> m =
  new HashMap<String, List<String>>();

staticファクトリーメソッドだとこう書ける

Map<String, List<String>> m = HashMap.newInstance();

簡潔ですよね

もちろん裏ではstaticファクトリーメソッドをこんな感じで実装している

public static <K, V> HashMap<K, V> newInstance() {
  return new HashMap<K. V>;
}

※P.S. Java7からは型類推をしてくれるのでstaticファクトリーメソッドは書かなくてもいい

短所1 publicあるいはprotectedのコンストラクタをもたないクラスのサブクラスを作れない

短所2 それらが容易に他のstaticメソッドと区別がつかない

・コンストラクタと違ってJavadocがうまく認識してくれない

staticファクトリーメソッドの一般的な名前

名前 役割
valueOf パラメータと同じ値を持つインスタンスを返す。実質的に型変換メソッドである。
of valueOfの代替。より簡潔にしたもの。
getInstance パラメータで指定されたインスタンスを返すが、同じ値を持つとは言えない。シングルトンの場合には、getInstanceは何も引数を取らず、その唯一のインスタンスが返される。
newInstance getInstanceに似ているが、newInstanceは返される個々のインスタンスは、すべて別々のインスタンスである点が異なる。
getType getInstanceに似ているが、ファクトリーメソッドが対象のクラスと異なるクラスにある場合に使用され。Typeはファクトリーメソッドから返されるオブジェクトの型を示す。
newType newInstanceに似ているが、ファクトリーメソッドが対象のクラスと異なるクラスにある場合に使用される。Typeはファクトリーメソッドから返されるオブジェクトの型を示す。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Effective Javaを読む】 第2章 項目1 『コンストラクタの代わりにstaticファクトリーメソッドを検討する』

はじめに

Javaの理解を深めるならこれを読めということで、Effective Javaを自分なりに解釈しながら読んでいこうと思います。
読んでるのは積読していた第2版です。
https://www.amazon.co.jp/EFFECTIVE-JAVA-Java-Joshua-Bloch/dp/4621066056/ref=pd_sbs_14_3/355-5139262-7829161?_encoding=UTF8&pd_rd_i=4621066056&pd_rd_r=ac861412-beae-43a8-872a-8b853aa69980&pd_rd_w=oIuWA&pd_rd_wg=nhmjU&pf_rd_p=7642417c-6494-4d06-a2b0-fcb0e0b3c563&pf_rd_r=HAEC02ASTQVJ4SPSM92Q&psc=1&refRID=HAEC02ASTQVJ4SPSM92Q

コンストラクタの代わりにstaticファクトリーメソッドを検討する

今までコンストラクタを使って実装していた処理を、staticファクトリーメソッドに置き換えてみたら色々嬉しいことあるんじゃないですかねっていってる節

サンプルコード

例1
public static Boolean valueOf(boolean b){
    return b ? Boolean.TRUE : Boolean.FALSE;
}
例2
//サービスプロバイダーフレームワーク
public interface Service{
    //サービス固有のメソッドをここに
}

//サービスプロバイダーインターフェース
public interface Provider{
    Service newService();
}

//サービス登録とアクセスのためのインスタンス化不可能クラス
public class Service{
    private Service(){} //インスタンス化を抑制(項目4)

    //サービス名をサービスと対応づける
    private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
    public static final String DEFAULT_PROVIDER_NAME = "<def>";

    //プロバイダー登録API
    public static void registerDefaultProvider(Provider p){
        registerProvider(DEFAULT_PROVIDER_NAME,p);
    }
    public static void registerProvider(String name,Provider p){
        providers.put(name,p);
    }

    //サービスアクセスAPI
    public static Service newInstance(){
        return newInstance(DEFAULT_PROVIDER_NAME);
    }
    public static Service newInstance(String name){
        Provider p = providers.get(name);
        if (p == null)
            throw new IllegalArgumentException(
                    "Nn provider registered with name:" + name
            );
        return p.newService();
    }

}

用語集

コンストラクタ

・コンストラクタとは、クラスのインスタンスを作成するときに実行される処理のこと

SampleClass instanceA = new SampleClass();

↑newの後ろのSampleClasss()の記述がコンストラクタの呼び出し

・コンストラクタの書き方:クラス名と同じにする

public class SampleClass {
  //コンストラクタ
  public SampleClass(){
    System.out.println("コンストラクタだよー");
  }
}

↑これでnewした時に「コンストラクタだよー」が標準出力される

・メソッドと違ってコンストラクタは値を返さない(returnを書くとエラーになる)

static

・インスタンスに依存しないメソッドや変数を作るための修飾詞

staticファクトリーメソッド

・staticファクトリーメソッドとは、オブジェクトを返す単なるstaticのメソッドのこと
・例1ではboolean基本データ値(b)をBooleanオブジェクトに変換している

デザインパターンのファクトリーメソッド

・名前が似てるけどstaticファクトリーメソッドとは別物
・ここでは説明は割愛

インターフェース

・インターフェースとは、クラス内にあるメソッドの具体的な処理は書かずに「変数」または「メソッドの型」を記述したもの
・「すべてのメソッドが抽象メソッドであること」「基本的にフィールドを1つも持っていないこと」が条件
・処理内容を具体的に書いていないため、使いたいときに処理内容を実装すればい良いので、将来的に処理の変更が起こりうる場合などに便利(細かいところを後回しにできる)

staticファクトリーメソッドの4つの長所と2つの短所

長所1 コンストラクタと異なり名前を持つ

・コンストラクタはnewすると勝手に実行されちゃうけど、staticファクトリーメソッドならいい感じの名前が付けられるので可読性があがる

例えば確率的素数(probable prime)であるBigIntegerを返す処理があるとして、

-コンストラクタでの書き方

BigInteger(int, int, Random)

-staticファクトリーメソッドでの書き方

BigInteger.probablePrime

…どっちがわかり易い?

長所2 コンストラクタと異なりメソッドが呼び出されるごとに新たなオブジェクトを生成する必要がない

・当たり前だけど、staticなので何にもインスタンス化していない
 コンストラクタを呼び出すためにはnewしなくちゃいけない
・不必要に新しいオブジェクトをたくさん作っちゃうのはあんまりよろしくないよねって話

長所3 コンストラクタと異なりメソッドの戻り値型の任意のサブタイプのオブジェクトでも返すことができる

・コンストラクタは戻り値を返さないものだけど、staticファクトリーメソッドはあくまでメソッドなのでreturnすることができるので柔軟性がある
・例2のようなサービスプロバイダーフレームワーク(中身を隠蔽してAPIとして使ってもらう)もつくれちゃうぜ

長所4 パラメータ化された型のインスタンス生成の面倒さを低減する

・冗長な書き方を簡略化できる
例えばコンストラクタだとこう書かなきゃいけなかったものが

Map<String, List<String>> m =
  new HashMap<String, List<String>>();

staticファクトリーメソッドだとこう書ける

Map<String, List<String>> m = HashMap.newInstance();

簡潔ですよね

もちろん裏ではstaticファクトリーメソッドをこんな感じで実装している

public static <K, V> HashMap<K, V> newInstance() {
  return new HashMap<K. V>;
}

※P.S. Java7からは型類推をしてくれるのでstaticファクトリーメソッドは書かなくてもいい

短所1 publicあるいはprotectedのコンストラクタをもたないクラスのサブクラスを作れない

短所2 それらが容易に他のstaticメソッドと区別がつかない

・コンストラクタと違ってJavadocがうまく認識してくれない

staticファクトリーメソッドの一般的な名前

名前 役割
valueOf パラメータと同じ値を持つインスタンスを返す。実質的に型変換メソッドである。
of valueOfの代替。より簡潔にしたもの。
getInstance パラメータで指定されたインスタンスを返すが、同じ値を持つとは言えない。シングルトンの場合には、getInstanceは何も引数を取らず、その唯一のインスタンスが返される。
newInstance getInstanceに似ているが、newInstanceは返される個々のインスタンスは、すべて別々のインスタンスである点が異なる。
getType getInstanceに似ているが、ファクトリーメソッドが対象のクラスと異なるクラスにある場合に使用され。Typeはファクトリーメソッドから返されるオブジェクトの型を示す。
newType newInstanceに似ているが、ファクトリーメソッドが対象のクラスと異なるクラスにある場合に使用される。Typeはファクトリーメソッドから返されるオブジェクトの型を示す。

続く

【Effective Javaを読む】 第2章 項目2 『数多くのコンストラクタパラメータに直面した時にはビルダーを検討する』
https://qiita.com/Natsukii/items/eb8fec0d8cae567f6647

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

【Java】Amazon SESを利用してメールを送信する

はじめに

AWSのSES(Simple Email Service)を使用する機会があったため、復習も兼ねて、Javaプログラムでメール送信する方法をまとめたいと思います。
SMTPを使用する方法とAWS SDKを使用する方法がありますが、ひとまず、SMTPの方法を記載します。
後日、AWS SDKについても追記します。

Amazon SESについて

  • Amazon SES(Simple Email Service)はAmazonが提供するメール配信サービス
  • 導入コストが低く、小規模で安価な運用が可能
  • 毎月最初の 62,000 通のメールについては無料で送信できる(SES料金)
  • AWSの他のサービスを使用することでメールログの保存や分析が可能

事前準備(SESコンソール)

プログラムを作成する前に下記をする必要があります。

送信元(From)アドレスの認証、送信先(To)アドレスの認証

アカウントはサンドボックスと呼ばれるテスト環境に新規ユーザーとして作成されるため、確認した E メールアドレスでの E メール送受信のみができます。
未確認の E メールアドレスに E メールを送信する、1 日あたりに送信できる E メールの数を増やす、高速で E メールを送信するためには、アカウントをサンドボックスの外に移動する必要があります。

手順

  1. AWSにログインし、SESコンソールを開く
  2. 「Email Addresses」をクリック
  3. 「Verify a New Email Address」ボタンをクリック
  4. 認証するメールアドレスを入力して、「Verify This Email Address」ボタンをクリック
  5. 受信したメール内のリンクをクリック
  6. 手順2.の画面を開くと指定したメールアドレスのVerification Statusがverified(認証済み)になっている

これでメールアドレスの認証は完了です。
「Send a Test Email」ボタンをクリックでメールが送受信できるか確認できます。

SMTPユーザーを作成して認証情報(ユーザー名とパスワード)を取得する

手順

  1. AWSにログインし、SESコンソールを開く
  2. 「SMTP Settings」をクリック
  3. 「Create My SMTP Credentials」ボタンをクリック
  4. SMTPユーザーの名前を入力して「作成」ボタンをクリック(名前はデフォルト値でも可)
  5. 「ユーザーの SMTP セキュリティ認証情報を表示」をクリック
  6. 表示された認証情報をコピーして安全な場所に保存しておく or 「認証情報のダウンロード」でcsvをダウンロード

IAMコンソール > アクセス管理 > ユーザー で作成したSMTPユーザーを確認できます。

動作環境

  • eclipse Version: 2020-06 (4.16.0)
  • javaバージョン
$ java -version
java version "1.8.0_231"
Java(TM) SE Runtime Environment (build 1.8.0_231-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode)

Javaプログラム

  1. eclipseでMavenプロジェクトを作成
  2. pom.xmlにJavaMailの依存関係を追加(MvnRepositoryでJavaMailを検索して最新バージョンのjarを追加)
  3. メール送信プログラム作成
pom.xml
  <!-- 依存関係 -->
  <dependencies>
    <dependency>
      <groupId>com.sun.mail</groupId>
      <artifactId>javax.mail</artifactId>
      <version>1.6.2</version>
    </dependency>
  </dependencies>
SesSmtpSample.java
public class SesSmtpSample {

    // 送信者アドレスと送信者名(SESコンソールで認証したメールアドレス)
    static final String FROM = "sender@example.com";
    static final String FROMNAME = "Sender Name";

    // 宛先アドレス(SESコンソールで認証したメールアドレス)
    static final String TO = "recipient@example.com";

    // SESコンソールで作成したSMTPユーザー名とパスワード
    static final String SMTP_USERNAME = "smtp_username";
    static final String SMTP_PASSWORD = "smtp_password";

    // SESコンソールで作成したConfig Setを指定。メールログの保存をするときなどに使用する。今回は不要なのでコメントアウト
    // static final String CONFIGSET = "ConfigSet";

    // Amazon SES SMTPのエンドポイント(リージョンがオレゴンの場合はus-west-2)
    static final String HOST = "email-smtp.us-west-2.amazonaws.com";

    // Amazon SES SMTPのエンドポイントのポート番号.
    static final int PORT = 587;

    // メール件名
    static final String SUBJECT = "Amazon SES test (SMTP interface accessed using Java)";
    // 本文
    static final String BODY = String.join(
            System.getProperty("line.separator"),
            "<h1>Amazon SES SMTP Email Test</h1>",
            "<p>This email was sent with Amazon SES using the ",
            "<a href='https://github.com/javaee/javamail'>Javamail Package</a>",
            " for <a href='https://www.java.com'>Java</a>."
        );

    public static void main(String[] args) throws Exception {

        // SMTPサーバーを定義
        Properties props = System.getProperties();
        props.put("mail.transport.protocol", "smtp");
        props.put("mail.smtp.port", PORT);
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.auth", "true");

        // メールセッションの確立
        Session session = Session.getDefaultInstance(props);

        // メール作成
        MimeMessage msg = new MimeMessage(session);
        msg.setFrom(new InternetAddress(FROM,FROMNAME));
        msg.setRecipient(Message.RecipientType.TO, new InternetAddress(TO));
        msg.setSubject(SUBJECT);
        msg.setContent(BODY,"text/html");

        // Configuration Setを設定。今回は使用しないのでコメントアウト
        //msg.setHeader("X-SES-CONFIGURATION-SET", CONFIGSET);

        Transport transport = session.getTransport();

        // メール送信
        try {
            System.out.println("Sending...");

            // SMTPサーバに接続
            transport.connect(HOST, SMTP_USERNAME, SMTP_PASSWORD);

            // メール送信
            transport.sendMessage(msg, msg.getAllRecipients());
            System.out.println("Email sent!");
        } catch (Exception ex) {
            System.out.println("The email was not sent.");
            System.out.println("Error message: " + ex.getMessage());
        } finally {
            // 接続終了
            transport.close();
        }
    }
}

SMTPのエンドポイントはリージョンによって変わります。
AWSのリファレンスで確認できます。

おわりに

Amazon SESの日本語ドキュメントがあるため、基本的にはドキュメント通りにやっていけば問題なくメール送信できると思います。

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