7

ジェネリック型またはメソッド(Javaだけでなく任意の言語)を変換する必要があるコンパイラーには、原則として2つの選択肢があります。

コードの専門化。コンパイラーは、ジェネリック型またはメソッドのインスタンス化ごとに新しい表現を生成します。たとえば、コンパイラは整数のリストのコードと、文字列のリスト、日付のリスト、バッファのリストなどの追加の異なるコードを生成します。

コードシェア。コンパイラーは、ジェネリック型またはメソッドの1つの表現のみのコードを生成し、ジェネリック型またはメソッドのすべてのインスタンス化を一意の表現にマップし、必要に応じて型チェックと型変換を実行します。

Javaはコードシェア方式を使用しています。C#はコードの特殊化方法に従っていると思うので、以下のすべてのコードは、C#を使用している私によれば論理的です。

このJavaコードスニペットを想定すると:

public class Test {

    public static void main(String[] args) {
        Test t = new Test();
        String[] newArray = t.toArray(new String[4]);
    }

    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        //5 as static size for the sample...
        return (T[]) Arrays.copyOf(a, 5, a.getClass());
    }
}

コード共有方法は、型消去が発生した後にこのコードにつながります:

public class Test {

    public static void main(String[] args) {
       Test t = new Test();
       //Notice the cast added by the compiler here
       String[] newArray = (String[])t.toArray(new String[4]);
    }

    @SuppressWarnings("unchecked")
    public Object[] toArray(Object[] a) {
       //5 as static size for the sample...
       return Arrays.copyOf(a, 5, a.getClass());
    }
}

だから私の質問は:

この最初のキャストを正確にする必要は何ですか?:

(T[]) Arrays.copyOf(a, 5, a.getClass());

単純に行う代わりに(型消去の前、コーディング時に):

Arrays.copyOf(a, 5, a.getClass());

このキャストはコンパイラにとって本当に必要ですか?

OK、Arrays.copyOf戻りますObject[]。明示的なダウンキャストなしで、より具体的なタイプから直接参照することはできません。

しかし、この場合、コンパイラはジェネリック型(戻り型!)を処理するため、努力することはできませんか?

確かに、コンパイラーがメソッドの呼び出し元の行に明示的なキャストを適用するだけでは十分ではありませんか?:

(String[])t.toArray(new String[4]);

更新しました - - - - - - - - - - - - - - - - - - - - - - - - - --------------------

@ruakhの回答に感謝します。

ここに、コンパイル時に存在するだけの明示的なキャストが関連していることを証明するサンプルがあります。

public static void main(String[] args) {
   Test t = new Test();
   String[] newArray = t.toArray(new String[4]);
}


public <T> T[] toArray(T[] a) {
   return (T[]) Arrays.copyOf(a, 5, Object[].class);
}

キャスト先T[]は、キャストが関係しない可能性があることをユーザーに通知する唯一の方法です。そして実際、ここではObject[]toのダウンキャストが発生しString[]ClassCastException実行時にになります。

したがって、「コンパイラがメソッドの呼び出し元の行に明示的なキャストを適用するだけでは不十分です」という点まで、答えは次のとおりです。

このキャストはコンパイルステップで自動的に作成されるため、開発者はこのキャストをマスターしません。したがって、このランタイム機能は、コンパイルを開始する前にコードの安全性を深く確認するようにユーザーに警告しません。

一言で言えば、このキャストは存在する価値があります。

4

2 に答える 2

3

あなたの推論の線には2つの問題があります。

1つの問題は、明示的なキャストがコンパイル時の機能(静的型システムの一部)と実行時の機能(動的型システムの一部)の両方であるということです。コンパイル時に、ある静的型の式を別の静的型の式に変換します。実行時に、動的型が実際にはその静的型のサブ型であるという要件を適用することにより、型の安全性を確保します。もちろん、あなたの例では、実行時機能はスキップされます。これは、消去とは、実行時にキャストを強制するのに十分な情報がないことを意味するためです。ただし、コンパイル時の機能は引き続き関連します。

この方法を検討してください。

private void printInt(Number n)
{
    Integer i = (Integer) n;
    System.out.println(i + 10);
}

次のことが有効であると思いますか。

Object o = 47;
printInt(o);            // note: no cast to Number

fooとにかくすぐにその議論を投げかけるという理由でInteger、それで、発呼者がそれを投げかけることを要求する必要はありませんNumberか?

推論のもう1つの問題は、消去とチェックされていないキャストは型の安全性を少し犠牲にしますが、コンパイラは警告を発行することでこの犠牲を補うことです。チェックされていない(またはraw型の)警告を出さないJavaプログラムを作成する場合、ClassCastException暗黙の実行時のみのコンパイラー生成のダウンキャストが原因で、sがスローされないことを確認できます。(もちろん、そのような警告を抑制しない限り。)あなたの例では、ジェネリックであると主張し、そのreturn-typeがそのparameter-typeと同じであると主張するメソッドがあります。に明示的なキャストを提供することによってT[]、あなたはコンパイラに警告を発する機会を与えており、その場所でその主張を強制することはできないとあなたに伝えています。そのようなキャストがなければClassCastException、呼び出しメソッドの結果として生じる可能性について警告する場所はありません。

于 2012-11-30T01:19:14.267 に答える
2

(概念的に)のタイプがであると仮定し"a"ますList<String>[]

の完全なタイプを取得する方法はありませんaa.getClass()どちらに頼るかList[]。コピーもでありList[]、にキャストしたいList<String>[]。コンパイラーは、キャストが安全であると推論することはできません。コピー内のすべての要素がであるため、これを推論できますList<String>。あなたはコンパイラよりもよく知っているので、明示的なキャストが必要であり、「チェックされていない」警告を抑制することは正当化されます。

多くのチェックされていないキャストと同様に、コードは今日のJavaプラットフォームで問題なく動作しますが、理論的には間違っています。コンパイラは警告を出すのに軽薄ではありません。しかし、私たちには選択の余地がありません。

根本的な対立は、消去に対する態度です。

コンパイラは、すべての型が完全な型であるかのように、理想的な世界に住んでいます。コンパイラは消去された型を認識しません。いつの日かJavaが消去を削除することで理想的な型システムを実装するというこのかすかな希望があります。そのため、今日のコンパイラは消去を前提として動作しません。

私たちプログラマーは、消された世界に住んでいます。実際の完全な型にアクセスできないため、消去された型を操作し、完全な型のふりをする必要があります。

私たちのコードは、消去された世界でのみ機能します。Javaがいつか消去を取り除けば、私たちのコードはすべて壊れてしまいます。List[]にキャストList<String>[]?ナンセンス!許可されていません!

しかし、今日は選択の余地がありません。消去に依存するコードはいたるところにあります。Javaが消去を取り除きたい場合、これは大きな問題です。Javaはおそらくそれを決してしません。呪われています。

于 2012-11-30T02:09:34.287 に答える