4

以下のプログラムを確認してください。

コンパイラーがコンパイラーレベルでキャスト例外を発行する時期と、それがいつになるのruntimeか 疑問があります。

以下のプログラムのように、式

(Redwood) new Tree()Tree は Redwood ではないため、コンパイル時に失敗するはずだったと思います。しかしcompile time、予想どおりruntime!!!で失敗したわけではありません。

public class Redwood extends Tree {
     public static void main(String[] args) {
         new Redwood().go();
     }
     void go() {
         go2(new Tree(), new Redwood());
         go2((Redwood) new Tree(), new Redwood());
     }
     void go2(Tree t1, Redwood r1) {
         Redwood r2 = (Redwood)t1;
         Tree t2 = (Tree)r1;
     }
 }
 class Tree { }
4

6 に答える 6

6

Tree は必ずしも Redwood であるとは限りませんが、そうである可能性があるため、コンパイル時のエラーが発生しません。

この場合、そうではないことは明らかですが、コンパイラは、おそらく間違っている、または間違っている可能性があるというよりも、明らかに間違っていることについて文句を言う傾向があります。この場合、コード自体ではなくロジックが正しくなく、ロジックはコンパイラの問題ではなく、ユーザーの問題です。

Tree t = new Redwood();
Redwood r = (Redwood) t;

コンパイル時と実行時の両方で完全に有効です。

于 2013-01-06T09:33:50.703 に答える
5

説明に のサブクラスをもう 1 つ追加しました。

       Tree
      /    \
     /      \
    /        \ 
Redwood       Blackwood  

この階層では:

アップキャスト: クラス階層に沿ってある方向に参照をキャストするときfrom the sub classes towards the rootこの場合、キャスト演算子を使用する必要はありません

大文字の例:

ARedwoodまたはBlackwood両方tree: そう

Tree t1;
Tree t2;
Redwood r = new Redwood() ;
Blackwood  b = new Blackwood() ; 

t1 = r;    // Valid 
t2 = b;    // Valid   

ダウンキャスト: クラス階層に沿ってある方向に参照をキャストする場合from the root class towards the children or subclasses明示的に型キャストする必要があります。

ダウンキャストの例:

Redwood r = new Tree();  //compiler error, because Tree is not a Redwood 
Blackwood r = new Tree();  //compiler error, because Tree is not a Blackwood  

どちらかまたはオブジェクトTree object を実際に指している場合は、 キャスト a を明示的に入力する必要があります。そうでない場合は、実行時にエラーになります。例えばRedwoodBlackwook

この場合:

Tree t1;
Tree t2;
Redwood r = new Redwood() ;
Blackwood  b = new Blackwood() ; 

t1 = r;    // Valid 
t2 = r;    // Valid     

Redwood r2 = (Redwood)t1; 
Blackwood  b2 = (Blackwood)t2  

[答え]

Redwood r = (Redwood) new Tree(); なぜコンパイラエラーがないのですか?

Downcastの例:

ソースのRedwood r = (Redwood) new Tree();最初に Tree オブジェクトを作成し、 に型キャストしRedwoodます。

これは次のように考えることができます。

Tree t = new Tree();`
Redwood r = (Redwood)t;  

したがって、コンパイル時には問題ありません。

[答え]

なぜランタイムエラー?

しかし、実際にはサブクラスはスーパークラスのオブジェクトRedwoodを指すことはできません。Treeそのため、コードは実行時に失敗します。

ツリー t = 新しいツリー();

t対象Tree()Redwood()です。それが実行時エラーの理由です。

コンパイラは現在、値とは何かt構文的に記述していません。しかし、実行時にが故障した場合のオブジェクトはRedwood r = (Redwood)t;whereです。 tTree class

[提案:]

instanceof演算子 を使用することをお勧めします。

キャスト/強制操作は型間の変換に使用され、instanceof演算子は実行時に型情報をチェックするために使用されます。

説明:

instanceof演算子を使用すると、オブジェクトの型を判別できます。演算子の左側にオブジェクト、演算子の右側に型を取り、オブジェクトがその型に属しているかどうかを示すブール値を返します。これは、次の例で最も明確に示されています。

if (t instanceof Redwood)
{
    Redwood r = (Redwood)t;
    // rest of your code
}

ただし、基本型から派生型へのキャストは悪いことです。 参考instanceof演算子に注意

于 2013-01-06T09:58:40.967 に答える
5

コンパイラは、式のコンパイル時の型を調べるだけです。式の実行時の型に関する仮定は行いません。new Tree()はコンパイル時の型Treeを持っているので、(Redwood)new Tree()と違いはありません(Redwood)myTreeVariable

于 2013-01-06T10:02:13.283 に答える
2

コンパイラーがコンパイラーレベルでキャスト例外を発行する時期と、実行時にいつになるか疑問がありますか?

厳密に言えば、コンパイラは「キャスト例外を発行」しません。次のいずれかを取得します。

  • コンパイルエラー、または
  • 実行時例外。

(Redwood) new Tree()コンパイル エラーではなく実行時例外が発生する理由は、それがJLS (セクション 5.5.1)で発生するはずだということです。

具体的には、JLS は次のように述べています。

「コンパイル時の参照型 S (ソース) とコンパイル時の参照型 T (ターゲット) が与えられた場合、次の規則によりコンパイル時のエラーが発生しない場合、S から T へのキャスト変換が存在します。」

"S がクラス型の場合" AND "T がクラス型の場合、|S| <: |T| または |T| <: |S| のいずれか。それ以外の場合、コンパイル時エラーが発生します。"

ここで、「|S| <: |T|」型 S が型 T または T のサブタイプであることを意味します。

この場合、 STreeと T はRedwoodどちらもクラスでありRedwood、... のサブタイプであるTreeため、コンパイル エラーは発生しません。

これが「間違っている」ことが明らかであるという事実は関係ありません。JLS は、それが正当な Java であるため、コンパイル エラーが発生しないと述べています。(スマートコンパイラ、式が常に例外をスローするという効果のためにコンパイル警告を発行する場合があります...しかし、それは別の問題です。)


JLS のルールの背後にある理由は、仕様では詳しく説明されていませんが、次のようになると思います。

次の 3 つのフラグメントを比較します。

Redwood r = (Redwood) new Tree();

Tree t = new Tree();
Redwood r = (Redwood) t;

Tree t1 = new Tree();  Tree t2 = new Redwood();
Redwood r = (Redwood) (someCondition ? t1 : t2);

Tree t = gardenStore.purchaseTree();
Redwood r = (Redwood) t;

最初のフラグメントをコンパイル エラーとして定義したとします。

  • 2つ目はどうですか?それも簡単に証明できます。

  • 3つ目はどうですか?それは簡単かもしれません...あるいは非常に難しいかもしれません。

  • 4つ目はどうですか?フラグメントの合法性は、ソースコードさえないメソッドのセマンティクスに依存します!

要点は、式の動的な値に関することを証明するようにコンパイラーに要求し始めると、停止問題につながる滑りやすい坂道を進んでいるということです。また、コンパイル エラーをオプションにすると、あるコンパイラはプログラムが有効であると言い、別のコンパイラはプログラムにエラーがあると言うという恐ろしい状況が発生します。

于 2013-01-06T10:55:29.277 に答える
0

参照されるツリーは事前にRedwoodである可能性があるため、コードは適切にコンパイルされます。

     Tree t1 =  new Redwood(); 
     Redwood r1 = (Redwood)t1;
     Tree t2 = (Tree)r1;

Tree()がすでにRedwoodである可能性があることがわかります。

于 2013-01-06T10:09:33.680 に答える
0

ダウンキャストする必要がないことを追加する必要があります。レッドウッドもツリーであるため、いつでもツリー変数に割り当てることができます。

Redwood r;
Tree t = r; // no casting needed

Java 型システムでは、いつでもSubstitution Principleを探すことができます。ギガトンの材料があると確信しています。

于 2013-01-06T09:42:50.873 に答える