なぜJavaはその上限クラスに有界型パラメータのインスタンス化のためにキャストを必要としていますか?

マシューS.:

Javaは、たとえば、キャストを持っているために、その上限クラスに有界型パラメータのインスタンス化を必要とします。

<T extends Integer> void passVal (T t) {
    Integer number = 5;
    t = (T) number; // Without cast a compile error is issued
}

Tすでにに制限されているIntegerか、そのサブクラスの一つ(私が知っている、いずれかが存在しない)宣言すると、どの有界型パラメータはだけなので、その上限クラスまたはそのサブクラスの1つにインスタンス化することができるように私には思えますなぜキャストが必要とされていますか?

また、私はそう答えとしてそれを提供していませんが、このメソッドの呼び出し外を通じてパラメータをインスタンス化した場合、これは当てはまらないことを知っています。質問はその宣言クラス/メソッド内でインスタンス化された有界型パラメータに固有のものです。

ggf31416:

Tは整数を意味するものではありません、それは整数またはそれから延びている任意のクラスに対して有効である必要があります。StrangeIntegerは、整数から延びており、StrangeIntegerとTを置き換えることをしましょうと言います:

void passVal (StrangeInteger t) {
    Integer number = 5;
    t = (StrangeInteger) number;
}

数がStrangeIntegerまたは最初の場所での派生クラスでない限り、それはStrangeInteger変数に整数の変数を割り当てようと、あなたはそれを行うことはできません。tは整数でない限り、実際にはあなたのコードは、(概念的には)実行時に例外をスローする必要がありますが、それは実際に起因する型消去(編集2を参照)、この場合にはそれを行うことはありません。

状況は次のようになります。

Object obj = "Hello"
String t = (String)obj; // this will not fail, but requires a cast

Object obj2 = getDBConnection();
String t2 = (String)obj2; // this will fail at runtime

編集:Tは唯一の整数を指定できますので、整数は、確かに最後のですが、それはとても特別なケースが追加することができ、上部の最終であることをバインドするために意味をなさない、すべての後に、上限が最終的なものであれば、コンパイラがチェックされていない可能性があります非常に少ない本当の利益のために複雑。

編集2:TL; DR:あなたは、上限と下限を混乱されますが、型消去と注意点があります。これは、すぐにジェネリックを使用してだけではなく、基本型を使用してやって価値がある何もして壊れます。

私は完全にクリアしなくてもよいので、英語が私の最初の言語ではありません。

私はあなたが上限とジェネリック型を使用して、ちょうどタイプとして上限を使用しての違いで苦労していると思います。(C ++や他の言語からの)ジェネリック医薬品のアイデアは、あなたが上限で定義されていない任意のメソッドを呼び出すことはできませんので、Tは、境界で許可された任意の型でTを交換する場合はコードが有効でなければならないということです。

上側のTに拘束されるものでも、あなたは常にA変数にTオブジェクトを割り当てることができることを意味します。(A == Tがない限り)、あなただけAが下位Tにバインドされた場合は、上限ではないことを行うことができますあなたは安全にT変数にAのオブジェクトを割り当てることはできません。参照してください上の上限と下限を理解しますか?でJavaのジェネリック。

Javaが使用する型消去をジェネリックを実装するために、そこにいくつかの利点があるが、それは必ずしも明白ではないいくつかの制限が発生します。そのため、この場合の型消去のキャスト自体はTが上型消去ステップでバインドに置き換えられて型チェックの後、失敗しない、すなわち(T)の数は、(整数)番号で置き換えられます。あなたが変更されたトンを返し、サブクラスの結果を変数に割り当てた場合、たとえば、サブクラスへのキャストを引き起こし何をすれば、コンパイラは、暗黙的なキャストを追加するため、例外がまだ発生します。

あなたは、たとえば、一般的なパターンであるTのサブクラスに依存メソッドを呼び出す場合にも失敗します。

List<Person> persons = ...
Comparator<Person> nameComparator = (p1,p2) -> p1.getName().compareTo(p2.getName())
java.util.Collections.sort(persons,nameComparator);

次のコードサンプルは、いくつかのケースでの挙動を示します。私は、出力の順序の問題を回避するために、すべてのものの上をSystem.errを使用しました。

import java.util.function.Consumer;
import java.util.function.Function;

class A {
    @Override public String toString(){ return "A";}
    public String foo(){ return "foo";}
}

class B extends A {
    @Override public String toString(){ return "B";}
    public String bar(){ return "bar";}
}

class C extends B { }

public class Main {

    public static void main(String[] args) {
        Function<A,String> funA = a -> a.foo();
        Function<B,String> funB = b -> b.bar();
        Function<C,String> funC = c -> c.bar();
        Consumer<B> ignoreArgument = b -> {
            System.err.println("  Consumer called");
        };

        B b = new B();
        System.err.println("* voidTest *");
        voidTest(b);
        System.err.println("------------");
        System.err.println("* returnTest *"); 
        returnTest(b);
        System.err.println("returnTest without using result did not throw");
        System.err.println("------------");
        try {
            System.err.println("Returned " + returnTest(b).toString());
            System.err.println("returnTest: invoking method on result did not throw");
        }
        catch(Exception ex) {
            System.err.println("returnTest: invoking method on result threw");
            ex.printStackTrace();
        }
        System.err.println("------------");
        B b2 = null;
        try {
            b2 = returnTest(b);
            System.err.println("returnTest: assigning result to a B variable did not throw");
        }
        catch(Exception ex) {
            System.err.println("returnTest: assigning result to a B variable threw");
            ex.printStackTrace();
        }
        System.err.println("------------");
        System.err.println("* functionTest funA *");
        functionTest(b, funA);
        System.err.println("------------");
        System.err.println("* functionTest funB * ");
        functionTest(b, funB);
        System.err.println("------------");
        System.err.println("* consumerTest *");
        consumerTest(b, ignoreArgument);
        // The following won't work because C is not B or a superclass of B
        // Compiler error functionTest(T, Function<? super T,String>) is not applicable for the arguments (B, Function<C,String>)
        // functionTest(b, funC); 
    }

    private static <T extends A> void voidTest(T t){
        System.err.println("  Before: " + t.toString());
        t = (T)new A(); // warning Type safety: Unchecked cast from A to T
        System.err.println("  After: " + t.toString());
    }

    private static <T extends A> T returnTest(T t){
        System.err.println("  Before: " + t.toString());
        t = (T)new A();
        System.err.println("  After: " + t.toString());
        return t;
    }

    private static <T extends A> void functionTest(T t, Function<? super T,String> fun) {
        System.err.println("  fun Before: " + fun.apply(t));
        t = (T)new A();
        try {
            System.err.println("  fun After: " + fun.apply(t));
        }
        catch(Exception ex) {
            System.err.println("  fun After: threw");
            ex.printStackTrace();
        }
    }

    private static <T extends A> void consumerTest(T t, Consumer<? super T> c) {
        System.err.print("  Before: ");
        c.accept(t);
        t = (T)new A();
        try {
            System.err.println("  After: ");
            c.accept(t);
            System.err.println("    c.accept(t) After: worked");
        }
        catch(Exception ex) {
            System.err.println("    c.accept(t) After: threw");
            ex.printStackTrace();
        }
    }
}

OpenJDKの11の下の出力は、次のとおりです。

* voidTest *
  Before: B
  After: A
------------
* returnTest *
  Before: B
  After: A
returnTest without using result did not throw
------------
  Before: B
  After: A
returnTest: invoking method on result threw
java.lang.ClassCastException: class A cannot be cast to class B (A and B are in unnamed module of loader 'app')
    at Main.main(Main.java:35)
------------
  Before: B
  After: A
returnTest: assigning result to a B variable threw
java.lang.ClassCastException: class A cannot be cast to class B (A and B are in unnamed module of loader 'app')
    at Main.main(Main.java:45)
------------
* functionTest funA *
  fun Before: foo
  fun After: foo
------------
* functionTest funB * 
  fun Before: bar
  fun After: threw
java.lang.ClassCastException: class A cannot be cast to class B (A and B are in unnamed module of loader 'app')
    at Main.functionTest(Main.java:83)
    at Main.main(Main.java:57)
------------
* consumerTest *
  Before:   Consumer called
  After: 
    c.accept(t) After: threw
java.lang.ClassCastException: class A cannot be cast to class B (A and B are in unnamed module of loader 'app')
    at Main.consumerTest(Main.java:97)
    at Main.main(Main.java:60)

私は結果が完全に多分キャストが、その場合には、言語によって必要とされていない、無視、またはコンパイラはそれを削除された場合に例外を発生させなかった理由resultTest全くわかりません。上側の結果にバインドさで定義されたメソッドを呼び出すと、まだ例外を発生させました。最後にconsumerTestからの観測では、それはそれだけでB引数を期待する消費者にトンを渡すために必要な、ClassCastExceptionが引き起こすコールバー()する必要はありませんでしたということです。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=311239&siteId=1