Edited at

【Java】Java 10で拡張された型推論について注意すべきこと

6qwm567z1m.jpg

SwiftScalaKotlinGoRustで開発している方はいわゆる「型推論」を普段使い慣れていることでしょう。型推論は、静的型付け言語における言語機能であり、明示的に型を記述しなくてもコンパイラが自動的に型を決定してくれる機能ですので、Kotlinの型推論のように、

var a = 1

と記載するだけで、コンパイラが右辺の型から自動的に左辺の変数aが整数型であると推論してくれるわけです。ついにJavaでもJava10から『ローカル変数の型推論』が導されたことは記憶に新しい出来事でもありました。

たとえば、Java 10よりも前では以下のように変数の型を明示的に書く必応がありました。

String name = "tamito0201";

Java 10 になるとvarキーワードを付けることによって、次のようにローカル変数の宣言で必須の明示的な型を削除することができます。

var name = "tamito0201";

上記の場合は、ただStringがvarに置き換わっただけなので、あまりメリットを感じないかもしれません。しかし、Javaの場合コレクションAPIを使用する際に、うんざりするほど冗長なコードを書かなくてはいけませんよね。

HashMap<Integer, String> map = new HashMap<Integer, String>();

Java10以降ではこれを、

var map = new HashMap<Integer, String>();

のように記載することができます。なんてエレガントなのでしょう。

ただし、型推論は便利な反面思わぬバグを生んでしまう諸刃の剣でもあります。ですので、プロジェクトによってはコーディング規約によりvarの仕様を禁じ、明示的に型を指定するプロジェクトも存在します。大規模プロジェクトになればなるほど、思わぬバグを生むリスクを考えて、旧来通りの明示的な型指定を行うプロジェクトも多いのです。

なぜか?たとえば以下のコードが記述されているとしましょう。

var distance = getDistance();

System.out.println(distance * 100);

関数getDistanceの戻り値はなんでしょうか。ここで感の鋭い方は気づいたかもしれませんね。そうです。ラッパーオブジェクトであるIntegerを返却する可能性があるのであれば、nullが返ってくることもあります。その結果次のdistance * 100でヌルポが発生するのは自明ですので、nullチェックを入れる必要があります。

Integer distance = getDistance();

if (distance != null) {
System.out.println(distance * 100);
}

Java 8以降ではOptionalを返せばよいですね。

Optional<Integer> distance = Optional.ofNullable(getDistance());

distance.ifPresent(d -> System.out.println(d * 100));

つまりそのプロジェクトやチームでvarを使いわけるため、コーディング指針を決めておく必要があります。


  • varを使わない

  • ローカル変数は原則varを使用する。

  • 右辺に明示的な型が指定されている場合のみvarを使用する

  • コーディング規約で明示する型を決めてそれ以外はvarを使う

  • 単体テストでカバーするためプロジェクトではvarを使用する

どれも正解ですが、現実的には1 or 2が妥当であると考えられます。コーディング規約でいくら明示する型を指定しても大規模プロジェクトでは、コーディング規約をすべてのエンジニアが遵守できるなんて到底考えられませんからね。コーディング規約で決めて、CheckstyleSpotBugsPMD等でしばっておいたほうがいいかと思います。

何事にもメリット、デメリットはあります。

この辺りは、プロジェクトでどうするか必ず決めておいて下さい。決めないと出来上がったプロジェクトのソースはカオスになってしまいますからね。