6

このコードは、javac でコンパイル エラーを引き起こします (ただし、特に Eclipse 4.2.2 ではそうではありません!)。

public interface Foo<T> {
}

class Bar<T> implements Foo<Iterable<T>> {
}

class Test {
    void test(Foo<? extends Iterable<? extends String>> foo) {
        Bar<?> bar = (Bar<?>) foo;
    }
}

javac からのエラーは次のとおりです。

Foo.java:9: error: inconvertible types
        Bar<?> bar = (Bar<?>) foo;
                              ^
  required: Bar<?>
  found:    Foo<CAP#1>
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Iterable<? extends String> from capture of ? extends Iterable<? extends String>

キャストを に変更すると(つまり、生の型を使用して)、 の型を単純に に(Bar) foo変更した場合と同様に、コードをコンパイルできます。fooFoo<? extends Iterable<?>>

編集: 面白いことに、この単純な変更により、Eclipse は拒否されますが、javac は受け入れます:

void test(Foo<Iterable<String>> foo) {
    Bar<?> bar = (Bar<?>) foo;
}

そして、Eclipse と javac の両方がこれを拒否します。

void test(Foo<Iterable<? extends String>> foo) {
    Bar<?> bar = (Bar<?>) foo;
}
4

2 に答える 2

4

コードは合理的であり、コンパイルする必要があります。静的型Fooを に狭めBar、ジェネリック型? extends Iterable<? extends String>を に広げています。?最上位?はワイルドカード キャプチャです。これはチェックされたキャストであり、警告なしでコンパイルする必要があります。

Oracle バグを送信する必要があります。対応するバグ レポートを検索しようとしましたが、この古いクローズ チケットがこの問題に最も近いものでした (IMO の Oracle バグ データベースを検索するのは非常に困難です)。このjavacの問題は非常によく知られているので、機会があればもっと検索します。

于 2013-07-17T22:53:27.610 に答える
2

Xプログラマーが型を typeに明示的にキャストしたい場合、プログラマーがYコンパイラーよりもよく知っていると仮定すると、言語はそれを許可することができます。

しかし、Java の善良な人たちは、明らかに不可能なキャストを防ぎたいと考えてい(Cat)dogます。そのため、彼らは主題に特化したこの輝かしい詳細セクションを持っています - http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.5.1

しかし、これらのルールに問題はありますか? また、コンパイラはこれらの規則に準拠していますか? ... これらの質問は複雑すぎて、あまり面白くありません。

私たちが気にするべきことは、キャストが理にかなっているのかどうかです。それが理にかなっていて、コンパイラがそれを受け入れることを拒否した場合、問題はありません。それを回避するだけです。


あなたの質問では、ワイルドカードを挿入する場所が inFoo< >と in の2 つありますIterable< >。それぞれの場所で、ワイルドカードを使用できます

    0. None
    1. Bounded    "? extends Something"
    2. Unbounded  "?"

それでは、すべての組み合わせを調べてみましょう。これが私の結論です。

     wildcard#1    wildcard#2     should_compile   javac     eclipse

00   -             -              Y                Y         N
01   -             ? extends      N                N         N
02   -             ?              N                N         N
10   ? extends     -              Y                N         Y
11   ? extends     ? extends      Y                N         Y
12   ? extends     ?              Y                Y         Y
20   ?             -              Y                Y         Y

should_compileキャストが意味を成すかどうかを意味します。後で説明します。

の場合10 and 11、コードはコンパイルする必要がありますが、javac はそれを拒否します。ルールに問題があるか、javac にバグがあります。


00たとえば、ケースが理にかなっていてコンパイルする必要がある理由を見てみましょう

void test00(Foo<Iterable<String>> foo) {
    Bar<?> bar = (Bar<?>) foo;
}

the question is, could there be a class/interface `X`, such that
      Bar<X> <: Foo<Iterable<String>> 
=>    Foo<Iterable<X>> <: Foo<Iterable<String>> 
=>    Iterable<X> = Iterable<String>
=>    X = String
so the answer is yes, the cast makes sense.

01ケースがコンパイルされない理由

     Foo<Iterable<X>> <: Foo<Iterable<? extends String>>
=>   Iterable<X> = Iterable<? extends String>
=>   NO SOLUTION
     note that Iterable<String> != Iterable<? extends String>

とケース11

     Foo<Iterable<X>>  <: Foo<? extends Iterable<? extends String>>
=>   Iterable<X> <: Iterable<? extends String>
=>   X <: String

01理にかなっているとはいえ、case がコンパイルされないのは驚くべきことです。根本的な問題は、Iterable共変であり、使用されるほぼすべての場所でワイルドカードを使用する必要があることです。したがって、理想的には宣言する必要があります

class Bar<T> implements Foo<Iterable<? extends T>>

しかし、どこにでもワイルドカードを挿入すると、人生は地獄です。より良い解決策は、宣言サイトの差異です。私たち全員が引退する前にJavaがその機能を追加するかどうかはわかりません.

于 2013-07-18T00:46:57.327 に答える