現代的なJavaを書こう

この記事はAizu Advent Calender 13日目の記事です。 12日の記事はこちら 

qiita.com

14日の記事は公開され次第追加します。

はじめに

入学してから授業でプログラミングを行う弊学生徒向けです。 弊学では2年前期くらいに授業がありますが、(記憶の限りでは)授業は1年次にやるC言語がある程度のベースになっていたと思います。今回は学校の授業で紹介された方法以外にこういう書き方もできるよ〜ということを紹介していきたいと思います。 環境は学内WSに合わせてJava8を想定して紹介していきます。授業で書くときにはチャレンジしてみてはどうでしょうか。

これだけは覚えよう

以下の2つは基本情報処理技術者試験の午後問にはよく目にするものですし、学校でも取り上げることは(僕が受けたなかでは)なかったので紹介させていただきます。 プログラミング言語の問題でJava以外の言語を選択するも思った以上に解けず、最終手段でJavaを選択したとしてもエスパー的な方法で解くのではなく、きちんと理解した上で解きましょう。

for-each(拡張for文)

awkPerlにもありますね。 配列の中身を順に出力するforループを例に取り上げます。

int[] number = {0,1,2,3,4};

for(int i=0;i<number;++i)
  System.out.println(number[i]);

こういった書き方もできますが、線形リストや配列の出力の順が問わない場合は以下の書き方が有用です。

int[] number = {0,1,2,3,4};

for(int value:number)
  System.out.println(value);

1.変数(value)にリスト(number)の中のある要素への参照を代入
2.処理を実行
3.リストの全要素を参照していない場合、1に戻る

といった手順で実行されます。これを行う目的としては

  • 可読性
  • 処理時間の短縮

があります。 特に処理時間についてはリストを使用していると顕著に表れます。

三項演算式

たまにCのサンプルにもあります。 結論から言うと

if(age<20) status="未成年";
else status="成年";

status = age<20 ? "未成年":"成年";

の結果は同じです。
(age<20)と、真の場合:の左辺未成年が代入され、 偽の場合成年が代入されます。

構文は以下のようになります。
(条件式)?(真式):(偽式)
三つの項を?:で結ぶため三項演算子と呼ばれています。

if(条件式1) 処理1
else if(条件式2) 処理2 
else 処理3 

といった場合には

条件式1?処理1  
:条件式2?処理2  
:処理3;  

のように書くことが可能です。

Java8から実装された機能

これから紹介するものはJava8でもメインで取り上げられた新機能(当時)です Java以外でも使用する機会は多いです

Lambda式

Thread threadExec =new Thread(new Runnable(){
    @Override
    public void run(){
        //do something
    }
});

というJavaって感じのThread処理があるとすると

Thread threadExec = new Thread(()->{
  //do someting
});

と簡略化できます

構文も

(引数)->{処理}

とシンプルなものになります
また、それに併せてjava.util.function以下に関数型インターフェースが追加されました
https://docs.oracle.com/javase/jp/8/docs/api/java/util/function/package-summary.html
関数型インターフェースは単一のメソッドをもつインターフェースです。lambdaを実装するには都合の良いものになりますね
また、特定の場合にはかなり省略することができるので具体例を出してみます
掛け算をするインターフェース

@FunctionalInterface
public inteface Multiplication{
  abstract public int culculate(int x,int y);
}

があるとすると,以前は

Multiplication kakezan = new Multiplication{
    @Override
    public int culculate(int x,int y){
      return x*y;
    }
}

これを置きかえると

Multiplication kakezan 
  = (int x,int y) -> {return x*y;};

引数が一つ、または引数の型が一致する場合、引数の型を省略できます

Multiplication kakezan = (x,y)->{return x*y;};

{}の中身が単一の場合は{}とreturnも省略できます

Muitplication kakezan = (x,y) -> x*y;

引数が一つの場合は括弧も省略できます

Stream

最後です
Collection に対する処理をパイプライン処理を行います

実行する流れは以下のようになります 1. CollectionからStreamを取得 2. 中間操作を欲しい結果が得られるまで実行 3. 終端操作を実行

どの処理が中間、終端なのかはJava Docを参照してみてください

Stream (Java Platform SE 8)

九九の結果が格納されたlistの中の5の倍数の合計を取得するとします

Mulitplication kakezan = (x,y)-> x*y;
ArrayList<Integer> list = new ArrayList<>();

for(int i=1;i<10;++i)
  for(int j=1;j<10;++j) 
    list.add(kakezan.culculate(i,j));

従来では

int count=0;
for(int n:list){
    if(n%5==0) ++count; 
}

ここでStreamを使用すると以下のようになります

long sum = list.stream()
               .filter(n-> n%5!=0)
               .count();

ここでは中間操作にfilter,終端操作にcountを使用しています。
Streamを使わない場合では5で割り切れる場合にカウントを行う
使う場合では5で割り切れる数のみを含むStreamを生成→合計を生成 と、使う場合では2つの処理に分かれていて、可読性や抽象度高いです

おわりに

いろんな方面から忌み嫌われたり 自分から恨みを買ったり(Oracle) しているJavaではありますが、オブジェクト指向を学ぶ上で一番取っ掛かりやすいものだと思います。RPELが導入されたり、実行までの煩雑さを解消したJShell,Java10だと型推論使えますし、使われている幅もまま拾いので決して馬鹿にはできない存在だということは認知していただきたいと思います。

業務以外では書かないけど