HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

参照はあいまい

いわゆるJavaの初心者がどつぼにはまりがちな、「参照はあいまい」と言われて戸惑うケース。

(NGコード)

package t2014_1008_01;

import java.util.*;
import java.sql.*;

public class Main {

    public static void main(String[] args) {
        Date now = new Date();
        System.out.println(now);
        
        // なにかDBへアクセスする処理をこの後に書く想定・・
    }

}

これをコンパイルすると、次のようなエラーメッセージが出力されます。

  • 出力言語(環境)が日本語の場合
$ LANG=ja_JP.UTF-8 javac -d classes src/t2014_1008_01/Main.java 
src/t2014_1008_01/Main.java:9: Date の参照はあいまいです。java.sql の クラス java.sql.Date と java.util の クラス java.util.Date が両方適合します。
                Date now = new Date();
                ^
src/t2014_1008_01/Main.java:9: Date の参照はあいまいです。java.sql の クラス java.sql.Date と java.util の クラス java.util.Date が両方適合します。
                Date now = new Date();
                               ^
エラー 2 個
  • 出力言語(環境)が英語の場合
$ LANG=C javac -encoding UTF-8 -d classes src/t2014_1008_01/Main.java 
src/t2014_1008_01/Main.java:9: reference to Date is ambiguous, both class java.sql.Date in java.sql and class java.util.Date in java.util match
                Date now = new Date();
                ^
src/t2014_1008_01/Main.java:9: reference to Date is ambiguous, both class java.sql.Date in java.sql and class java.util.Date in java.util match
                Date now = new Date();
                               ^
2 errors

(Java6で試していますが、それ以前のバージョンだともっとメッセージが不親切だったりするかもしないかも・・)
(CentOS 6.5で試していますが、WindowsでもMacOSでも理屈は同じ。)

で、仮に、「java.util.Date」を使うことを想定しているとして、ネットでちゃちゃっと調べて、「あぁ、『java.util.』をつければいいんだな」と、以下のように修正します。

(NGコード)

package t2014_1008_01;

import java.util.*;
import java.sql.*;

public class Main {

    public static void main(String[] args) {
        java.util.Date now = new Date();
        System.out.println(now);
        
        // なにかDBへアクセスする処理をこの後に書く想定・・
    }

}

気づく人はすぐに気づくでしょうが、これだけでは解決しません。
「new Date()」の方の型名がまだ「あいまい」ですから・・
(コンパイラは、好意的な解釈はせず、あくまで機械的に解釈しますから・・)

(OKコード)

package t2014_1008_01;

import java.util.*;
import java.sql.*;

public class Main {

    public static void main(String[] args) {
        java.util.Date now = new java.util.Date();
        System.out.println(now);
        
        // なにかDBへアクセスする処理をこの後に書く想定・・
    }

}

このようにしないといけません。


「わぁ、エラーメッセージだ、ググってみよう」と一つの流れのように機械的に対処しようとすると、探し当てたサンプルが自分のはまっている問題にピッタリ合致しないものだった場合に、このような罠にはまります。

辛くても、最初のコードをコンパイルしたときのエラーメッセージを「しっかり」見てみると、「2個のエラーが出ている」ことが分かります。

  • 一つは、変数宣言で型を指定するときの「Date」があいまい。
  • もう一つは、オブジェクトをnewする際に指定する「Date」があいまい。


まぁ、そもそも、import文には、「ワイルドカード指定よりも個別指定の方が優先される」という仕様があるので、「java.util.Date」と「java.sql.Date」の両方を使う必要性がない限り、以下のように書けば十分なんですが・・

(OKコード)

package t2014_1008_01;

import java.util.Date; // 個別指定
import java.util.*;    // ワイルドカード指定
import java.sql.*;     // ワイルドカード指定

public class Main {

    public static void main(String[] args) {
        Date now = new Date();
        System.out.println(now);
        
        // なにかDBへアクセスする処理をこの後に書く想定・・
    }

}

そもそも、IDEも普及している現代では、私はワイルドカード指定「非」推奨派ですが・・
・・と言いながらも、時々めんどくさくなって、テキストエディタでコードを書くときがある奴(ぇ