Java1.4との付き合い方

こんにちは。kkです。

現在関わっているプロジェクトでは、Javaのバージョンが1.4となっています。
そのため、普段当然のように使っている機能が使えなかったりします。
今回は、Java1.4に無く1.5以上で追加された機能について、いくつか復習も兼ねて再確認するのと、1.4で実装していく上での注意点を書いてみたいと思います。

ジェネリクス

型を指定するアレです。
ListやMapでよく見かけますね。クラスに使うと汎用的な物を作成出来たりしますが、私自身はあまりそういう実装経験がありません。

[sourcecode language="java"]
    List<String> strList = new ArrayList<String>();
    strList.add("test");
    
    String getStr = strList.get(0);
[/sourcecode]

【メリットと1.4の注意点】
ジェネリクスを使わずに書くとどうなるでしょう。

[sourcecode language="java"]
    List strList = new ArrayList();
    strList.add("test");
    
    String getStr = (String) strList.get(0);
[/sourcecode]

この様な形になります。
get メソッドはオブジェクト型で返却しますので、扱うには適宜キャストが必要です。

では、こうしたら?

[sourcecode language="java"]
    List strList = new ArrayList();
    strList.add(123);

    String getStr = (String) strList.get(0);
[/sourcecode]

文法上は間違っていないのでコンパイルは通りますが、int型をString型に変換しようとしているので、実行時に ClassCastException が発生します。
ジェネリクスを使う事で、指定した型でしか扱えないようになるため、この様な事態はコンパイル時に回避出来ます。
副次的な効果ですが、いちいちキャストをしなくて良いので、見た目がスッキリして可読性が向上します。

for-each文(拡張for文)

こちらもListを処理する場面でよく見かけます。

[sourcecode language="java"]
    List<String> strList = new ArrayList<String>();
    strList.add("test");
    strList.add("test2");

    for (String str : strList) {
        System.out.println(str);
    }
[/sourcecode]

Listに限らず、複数の要素を持つもの(配列とか)に対し、要素を1つずつ取り出して繰り返し処理を行います。

【1.4で実装すると…】
多くの場合こんな形になると思います。

[sourcecode language="java"]
    List strList = new ArrayList();
    strList.add("test");
    strList.add("test2");

    for (int i = 0; i < strList.size(); i++) {
        System.out.println(strList.get(i));
    }
[/sourcecode]

for文に対し、初期値;評価式;増分値 を与え、評価式がTrueの間繰り返します。
要素を取り出す処理は別途書く必要があるので、ちょっと面倒ですね。
また、IndexOutOfBoundsException が発生する可能性も無きにしも非ずです。(大抵は何かしらのコーディングミスですが)

余談ですが、最近のコーディングミスとして
0 < strList.size()
とか書いていて、無限ループで IndexOutOfBoundsException が発生して一人で苦笑してました。

enum(列挙型)

値の一覧を定義する文法です。
使い方の例としては、、、

[sourcecode language="java"]
    enum targetType {
        target1,
        target2
    }

    public void test() {
        enumTest(targetType.target1);
    }

    public void enumTest(targetType target) {

        switch (target) {
            case target1:
                // 処理A
                break;
            case target2:
                // 処理B
                break;
        }
    }
[/sourcecode]

この様に、「意味のある(意味を表す)値の集まり」を定義します。(という自分の中での理解)
enumを型として引数に指定することで、それ以外の値を許さず、想定外の入力を防ぐことが出来ます。
また、使用する側も何を指定すれば良いか分かりやすく、明確になります。
他にもenumでは色々な利用方法がありますが、とりあえず一番単純な使い方ですね。

【一方1.4では】

[sourcecode language="java"]
    public static String TARGET_1 = "1";
    public static String TARGET_2 = "2";

    public void test() {
        enumTest(TARGET_1);
    }

    public void enumTest(String target) {

        if (TARGET_1.equals(target)) {
            // 処理A
        } else if (TARGET_2.equals(target)) {
            // 処理B
        } else {
            // 想定外
        }
    }
[/sourcecode]

String型(何でもいいんですけど)で値を定義し、if文で判定しています。
※主題とは関係無いですが、Java7からStringでもswitch文が使えるらしいですね。

入ってくる値は使う側に完全に依存しますし、使う側も一見して何を指定すれば良いか分かりません。
サンプルは短くて単純ですが、実際に業務で作成されるようなシステムだと、実装コードも長く定数定義も多いですし、大抵は別の定数クラスにまとめられてるので探しにくいです。

古くから使われているシステムでは、未だにJavaのバージョンを上げずに開発を続けている所もあります。
理由は様々だと思われますが、聞いた話のひとつでは、「バージョンを上げた事による影響調査と動作確認に掛けるお金は無い」という事です。
※ちなみに現在のプロジェクトでは無く、以前関わっていたものです。

システムを使う側から見れば、動作は変わらないのに費用を掛ける理由は無い…というのはまぁ至極最もではあります。
が、それを続けた場合の数年~数十年単位でのリスクやランニングコストは増えていくんじゃないかなぁと思ったりします。
私の勝手な想像ですけれど。

そんなところで今回はこの辺りで失礼します。