6

(私がもっと抽象化Xし、それに別のメソッドを追加する必要があることをアドバイスしないでください。)

C ++では、型の変数xがあり、それが型(のサブクラス)X*でもある場合に特定のことを実行したい場合、次のように記述します。Y*YX

if(Y* y = dynamic_cast<Y*>(x)) {
    // now do sth with y
}

同じことはJavaでは不可能のようです(またはそうですか?)。

代わりに、このJavaコードを読みました。

if(x instanceof Y) {
    Y y = (Y) x;
    // ...
}

変数xがなくても、より複雑な式である場合は、この問題のために、Javaでダミー変数が必要になることがあります。

X x = something();
if(x instanceof Y) {
    Y y = (Y) x;
    // ...
}
// x not needed here anymore

(一般的なことはそれsomething()ですiterator.next()。そして、それを2回だけ呼び出すこともできないことがわかります。本当にダミー変数が必要です。)

ここでは実際にはまったく必要ありません。キャストで一度にチェックをx行うことができないため、必要なだけです。instanceofこれを、非常に一般的なC++コードともう一度比較してください。

if(Y* y = dynamic_cast<Y*>( something() )) {
    // ...
}

castOrNullそのため、ダミー変数を回避できる関数を導入しましたx。私は今これを書くことができます:

Y y = castOrNull( something(), Y.class );
if(y != null) {
    // ...
}

の実装castOrNull

public static <T> T castOrNull(Object obj, Class<T> clazz) {
    try {
        return clazz.cast(obj);
    } catch (ClassCastException exc) {
        return null;
    }
}

さて、このcastOrNull関数をそのように使うのは悪いことだと言われました。何故ですか?(または、より一般的な質問になります。同意し、これも悪だと思いますか?はいの場合、なぜそうですか?または、これは有効な(おそらくまれな)ユースケースだと思いますか?)


言ったように、私はそのようなダウンキャストの使用がまったく良い考えであるかどうかについて議論したくありません。しかし、なぜ私が時々それを使用するのかを簡単に明らかにしましょう:

  1. 時々、非常に特定の事柄に対して別の新しいメソッドを追加するか(1つの特定の場合に1つのサブクラスにのみ適用される)、そのようなinstanceofチェックを使用するかを選択する必要がある場合があります。基本的に、関数を追加するか、チェックdoSomethingVeryVerySpecificIfIAmY()を行うかを選択できます。instanceofそしてそのような場合、後者の方がきれいだと思います。

  2. 時々私はいくつかのインターフェース/基本クラスのコレクションを持っていて、タイプのすべてのエントリーについてY、何かをしてからそれらをコレクションから削除したいと思っています。(たとえば、ツリー構造があり、空の葉であるすべての子を削除したい場合がありました。)

4

6 に答える 6

8

さて、このcastOrNull関数をそのように使用することは悪いことだと言われました。何故ですか?

私はいくつかの理由を考えることができます:

  • これは、非常に単純なことを行うためのあいまいでトリッキーな方法です。あいまいでトリッキーなコードは、読みにくく、維持しにくく、エラーの潜在的な原因(誰かがそれを理解していない場合)、したがって悪です。

  • メソッドが機能するあいまいでトリッキーな方法はcastOrNull、JITコンパイラでは最適化できない可能性があります。最終的には、少なくとも3つの追加のメソッド呼び出しに加えて、型チェックを実行して反射的にキャストするための多くの追加のコードが必要になります。反射の不必要な使用は悪です。

(対照的に、(instanceofクラスキャストが続く)単純な方法では、instanceofおよびクラスキャストに特定のバイトコードを使用します。バイトコードシーケンスは、ほぼ確実に最適化され、nullチェックが1つだけで、テストが1つだけになるようになります。ネイティブコードでのオブジェクトのタイプ。これは、JITコンパイラが検出して最適化するのが簡単な一般的なパターンです。)

もちろん、「悪」は、あなたが本当にこれをすべきではないという言い方です。

追加した2つの例のcastOrNullどちらも、必要または望ましい方法を使用します。IMO、「単純な方法」は、読みやすさとパフォーマンスの両方の観点から優れています。

于 2010-10-16T15:06:30.047 に答える
7

instanceofJava 14を開始すると、実行とキャストを同時に実行できるようになります。https://openjdk.java.net/jeps/305を参照してください。

コード例:

if (obj instanceof String s) {
    // can use s here
} else {
    // can't use s here
}

上記の例の変数sは、instanceof評価がtrueの場合に定義されます。変数のスコープはコンテキストによって異なります。その他の例については、上記のリンクを参照してください。

于 2019-12-17T01:19:05.097 に答える
5

最もよく書かれた/設計されたJavaコードでは、instanceofとcastsの使用は決して起こりません。ジェネリックスを追加すると、キャストの多くのケース(したがってinstanceof)は必要ありません。彼らはそうします、時々まだ起こります。

castOrNullメソッドは、Javaコードを「不自然」に見せているという点で悪です。ある言語から別の言語に変更する際の最大の問題は、新しい言語の規則を採用することです。Javaでは一時変数で問題ありません。実際、メソッドが実行しているのは、一時変数を実際に非表示にすることだけです。

たくさんのキャストを書いていることに気付いた場合は、コードを調べてその理由を確認し、それらを削除する方法を探す必要があります。たとえば、「getNumberOfChildren」メソッドを追加すると、ノードが空であるかどうかを確認できるため、キャストせずにノードをプルーニングできます(つまり、この場合は機能しない可能性があります)。

一般的に言えば、キャストは通常​​必要ないため、Javaでは「悪」です。あなたのメソッドは、ほとんどの人がJavaの記述を期待する方法で記述されていないため、より「悪」です。

そうは言っても、あなたがそれをやりたいのなら、それのために行きなさい。これは、Javaでそれを行うための「正しい」方法ではなく、実際には「悪」ではありません。

于 2010-10-16T15:39:28.673 に答える
4

私見あなたcastOrNullは悪ではなく、ただ無意味です。あなたは一時変数と1行のコードを取り除くことに夢中になっているようですが、私にとってより大きな問題は、なぜコードにこれほど多くのダウンキャストが必要なのかということです。OOでは、これはほとんどの場合、最適ではない設計の症状です。そして、症状を治療するのではなく、根本的な原因を解決したいと思います。

于 2010-10-16T14:21:13.743 に答える
2

その人がなぜそれが悪だと言ったのか正確にはわかりません。しかし、彼らの推論の1つの可能性は、キャストする前にチェックするのではなく、後で例外をキャッチしていたという事実でした。これはそれを行う方法です。

public static <T> T castOrNull(Object obj, Class<T> clazz) {
    if ( clazz.isAssignableFrom(obj.getClass()) ) {
        return clazz.cast(obj);
    } else {
        return null;
    }
}
于 2010-10-16T14:15:46.380 に答える
0

Java例外は遅いです。ダブルキャストを避けてパフォーマンスを最適化しようとしている場合は、ロジックの代わりに例外を使用して自分の足を撃ちます。合理的にチェックして修正できるもの(正確に何をしているのか)の例外をキャッチすることに決して依存しないでください。

Javaの例外はどれくらい遅いですか?

于 2015-07-16T14:59:51.387 に答える