web-dev-qa-db-ja.com

Javaクラス名の大文字と小文字の区別

大文字と小文字を区別しない同じ名前の2つのpublic Javaクラスを異なるディレクトリに書き込むと、両方のクラスが実行時に使用できなくなります(Windows、Mac、およびLinuxで、いくつかのバージョンのHotSpot JVM。同時に使用できるJVMが他にあるとしても、私は驚かないでしょう。たとえば、aという名前のクラスとAという名前のクラスを作成すると、次のようになります。

// lowercase/src/testcase/a.Java
package testcase;
public class a {
    public static String myCase() {
        return "lower";
    }
}

// uppercase/src/testcase/A.Java 
package testcase;
public class A {
    public static String myCase() {
        return "upper";
    }
}

上記のコードを含む3つのEclipseプロジェクトは 私のWebサイトから入手可能 です。

両方のクラスでmyCaseを呼び出すと、次のようになります。

System.out.println(A.myCase());
System.out.println(a.myCase());

タイプチェッカーは成功しますが、すぐ上のコードで生成されたクラスファイルを実行すると、次のようになります。

スレッド「メイン」の例外Java.lang.NoClassDefFoundError:testcase/A(間違った名前:testcase/a)

Javaでは、名前は一般に大文字と小文字が区別されます。一部のファイルシステム(Windowsなど)では大文字と小文字が区別されないため、上記の動作が発生することは驚くことではありませんが、間違っているようです。残念ながら、Java仕様は、どのクラスが表示されるかについて奇妙に非コミットです。 Java言語仕様(JLS)、Java SE 7 Edition (166ページの6.6.1項)は次のように述べています。

クラスまたはインターフェース型がパブリックとして宣言されている場合、宣言されているコンパイル単位(7.3)が監視可能であれば、どのコードからでもアクセスできます。

セクション7.3で、JLSはコンパイル単位の可観測性を非常にあいまいな用語で定義しています。

事前定義パッケージのすべてのコンパイル単位Javaおよびそのサブパッケージlangとioは常に監視可能です。他のすべてのパッケージでは、観測可能

Java仮想マシン仕様 も同様にあいまいです(セクション5.3.1)。

次の手順は、bootstrapクラスローダー[...]を使用して、[バイナリ名] Nで示される非配列クラスまたはインターフェイスCをロードおよび作成するために使用されます。それ以外の場合は、Java仮想マシンは、引数Nをbootstrapクラスローダーのメソッドの呼び出しに渡し、プラットフォーム依存の方法でCの意図された表現を検索します。

これらすべてが、重要度の高い順に4つの質問につながります。

  1. すべてのJVMでデフォルトのクラスローダーによってロード可能なクラスについて保証はありますか?言い換えれば、Java.langとJava.io以外のクラスをロードしない、有効だが縮退したJVMを実装できますか?
  2. 保証がある場合、上記の例の動作は保証に違反していますか(つまり、動作はバグです)?
  3. HotSpotにaAを同時にロードさせる方法はありますか?カスタムクラスローダーを書くことはできますか?
47
Josh Sunshine
  • すべてのJVMのbootstrapクラスローダーによってロード可能なクラスについての保証はありますか?

言語のコアビットと断片、およびサポート実装クラス。作成したクラスを含めることは保証されていません。 (通常のJVMは、bootstrap oneから別のクラスローダーにクラスをロードします。実際、通常のbootstrapローダーは、JARからクラスを通常ロードします。これにより、クラスでいっぱいの古い古いディレクトリ構造よりも効率的な展開が可能になります。)

  • 保証がある場合、上記の例の動作は保証に違反していますか(つまり、動作はバグです)?
  • 「標準」のJVMにaとAを同時にロードさせる方法はありますか?カスタムクラスローダーを書くことはできますか?

Javaは、クラスの完全な名前をファイル名にマッピングしてクラスをロードします。ファイル名は、クラスパスで検索されます。したがって、testcase.a に行く testcase/a.classおよびtestcase.A に行く testcase/A.class。一部のファイルシステムはこれらのものを混同し、一方が要求されたときにもう一方を提供する場合があります。他の人はそれを正しく行います(特に、JARファイルで使用されるZip形式のバリアントは完全に大文字と小文字を区別し、移植可能です)。 Javaがこれについてできることは何もありません(IDEが.classファイルはネイティブFSから離れています。実際にそうであるかどうかはわかりません。JDKのjavacは確かにそれほどスマートではありません)。

ただし、ここで注意する必要があるのはそれだけではありません。クラスファイルは、どのクラスについて話しているかを内部的に認識しています。ファイルからexpectedクラスが存在しないということは、ロードが失敗し、受け取ったNoClassDefFoundErrorにつながることを意味します。あなたが得たのは、検出されてしっかりと対処された問題(少なくともある意味での誤ったデプロイメント)でした。理論的には、検索を続けることでそのようなことを処理できるクラスローダーを構築できますが、なぜわざわざ?JAR内にクラスファイルを置くと、はるかに堅牢に修正できます。それらは正しく処理されます。

より一般的には、この問題に実際に多く遭遇している場合は、大文字と小文字を区別するファイルシステム(JenkinsのようなCIシステムが推奨されます)を使用してUnixで本番環境のビルドを行い、開発者を見つけます大文字と小文字の違いだけでクラスに名前を付けています非常に混乱しているので停止させてください!

19
Donal Fellows

ドナルの細かい説明はほとんど追加することはできませんが、このフレーズについて簡単に説明します。

... Java大文字と小文字を区別しない同じ名前のクラス...

一般に、名前と文字列は大文字と小文字を区別しませんそれ自体、それがある解釈のみです。次に、Javaはそのような解釈を行いません。

したがって、あなたが考えていたものの正しい表現は次のようになります。

... Java大文字と小文字を区別しないファイルシステムでのファイル表現が同じ名前を持つクラス...

1
Tillmann