web-dev-qa-db-ja.com

リフレクションを備えた汎用のセッターとゲッターを作成するのはなぜ悪い考えなのですか?

しばらく前に私は this answer を、すべての可変変数のゲッターとセッターを持たないようにする方法についての質問に書きました。当時、私はこれが悪いアイデアだと言葉で説明するのは難しいと感じていましたが、OPはその方法を明確に尋ねていました。なぜこれが問題になるのかをここで検索したところ、 この質問 であることがわかりました。

それで、特にジェネリックなゲッターとセッターの場合に、リフレクションを避けなければならないことがなぜ与えられているのか、誰かが言葉で説明できますか?

50
HAEM

一般的な反射の欠点

リフレクションは、直線のコードよりも理解が困難です。

私の経験では、リフレクションはJavaの「エキスパートレベル」の機能です。私は、ほとんどのプログラマーがリフレクションを積極的に使用することは決してないと主張します(つまり、リフレクションを使用するライブラリを使用することは考慮されません)。そのため、これらのプログラマーはコードを理解しにくくなります。

リフレクションコードは静的解析にアクセスできません

クラスにゲッターgetFooがあり、名前をgetBarに変更したいとします。リフレクションを使用しない場合は、コードベースでgetFooを検索するだけで、getterを使用するすべての場所を見つけることができるので、リフレクターを更新できます。

しかし、getterを使用する場所がcallGetter("Foo")のようなものであり、callGettergetClass().getMethod("get"+name).invoke(this)を実行する場合、上記のメソッドはそれを検出せず、コンパイラーは検出しません。文句を言う。コードが実際に実行された場合のみ、NoSuchMethodExceptionが取得されます。そして、その例外(追跡されている)がcallGetterによって飲み込まれた場合の苦痛を想像してください。「ハードコードされた文字列でのみ使用されるため、実際には発生しません」。 (だれもそれをしないだろう、誰かが主張するかもしれません。OPが正確にそれを除いて、SOの回答です。フィールドの名前が変更された場合、ジェネリックセッターのユーザーはサイレントで何もしないセッターの非常にあいまいなバグを除いて、気づかないでください。ゲッターのユーザーは、運が良ければ、無視された例外のコンソール出力に気づくかもしれません。)

リフレクションコードはコンパイラーによって型チェックされません

これは基本的に上記の大きなサブポイントです。リフレクションコードはObjectに関するものです。タイプは実行時にチェックされます。エラーは単体テストで発見されますが、カバレッジがある場合のみです。 (「これは単なるゲッターです。テストする必要はありません。」)基本的に、Java over Pythonを使用すると、最初の場所。

リフレクションコードは最適化に使用できません

理論上ではないかもしれませんが、実際には、Method.invokeのインラインキャッシュまたはインラインキャッシュを作成するJVMは見つかりません。このような最適化には、通常のメソッド呼び出しを使用できます。それは彼らをはるかに速くします。

一般的にリフレクションコードが遅い

リフレクションコードに必要な動的メソッド検索と型チェックは、通常のメソッド呼び出しよりも時間がかかります。その安価な1行のゲッターを反射獣に変えると、(私はこれを測定していませんが)数桁の速度低下を見ている可能性があります。

一般的なゲッター/セッターの欠点

クラスにカプセル化がなくなったため、これは単に悪い考えです。 Everyフィールドにアクセスできます。それらをすべて公開することもできます。

118
Sebastian Redl

パススルーゲッター/セッターは価値のない嫌悪者だからです。ユーザーはプロパティを介して実際の値を取得できるため、カプセル化は提供されません。フィールドストレージの実装を変更することはめったにないので、これらはYAGNIです。

Andこれはパフォーマンスがはるかに低いためです。少なくともC#では、getter/setterは単一のCIL命令に直接インライン化します。あなたの答えでは、それを3つの関数呼び出しに置き換えています。これは、反映するためにメタデータを読み込む必要があります。私は通常、反射コストの経験則として10xを使用しましたが、データを検索すると、最初の回答の1つに this answer が含まれており、1000xに近い値になりました。

(そしてそれはあなたのコードをデバッグするのを難しくし、それを誤用するより多くの方法があるのでそれは使いやすさを損ないます、そしてあなたは今適切な識別子ではなく魔法の文字列を使用しているので保守性を損ないます...)

11
Telastyn

すでに提示された議論に加えて。

期待されるインターフェースを提供していません。

ユーザーは、foo.get( "bar")およびfoo.set( "bar"、value)ではなく、foo.getBar()およびfoo.setBar(value)を呼び出すことを期待します。

ユーザーが期待するインターフェイスを提供するには、プロパティごとに別々のゲッター/セッターを作成する必要があります。それが終わったら、リフレクションを使用しても意味がありません。

8
Peter Green