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


49

しばらく前に、すべての可変変数に対してゲッターとセッターを持たないようにする方法に関する質問に対するこの回答を書きました。当時、私はこれが悪い考えであると口にするのが難しい直感しか持っていませんでしたが、OPはそれを行う方法を明示的に求めていました。私はここでなぜこれが問題になるのかを検索し、この質問を見つけました。その質問の答えは、絶対に必要でない限りリフレクションを使用するのは悪い習慣であると考えているようです。

それで、特に一般的なゲッターとセッターの場合に、反射を避けるべきだという理由を誰かが言葉で表現できますか?


12
ボイラープレートを減らしたい場合、LombokとGroovyの両方が、静かに安全にゲッターとセッターを生成します。
クリリス

2
javaのような静的言語でこれらのゲッターとセッターをどのように使用しますか?それは私には初心者ではないようです。
エスベンスコフペダーセン

あなたが好きなあまり、それらを消費したい@EsbenSkovPedersen Mapさんgetput、物事のかなり不格好な方法です。
HAEM

2
IIRC、Lombokはコンパイル時にゲッター/セッターを合成します。これは適切な注釈で指示されているとおりです。そのため、リフレクションのパフォーマンスペナルティが発生せず、静的に分析/インライン化でき、リファクタリングできます(IntelliJにはLombokのプラグインがあります)
アレクサンダー

回答:


117

一般的な反射の欠点

反射は、直線のコードよりも理解しにくいです。

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

リフレクションコードは静的分析にアクセスできない

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

ゲッターを使用する場所のようなものである場合でもcallGetter("Foo")callGettergetClass().getMethod("get"+name).invoke(this)、その後、上記の方法は、それを見つけることができません、とコンパイラが文句を言うことはありません。コードが実際に実行されたときのみ、を取得しNoSuchMethodExceptionます。そして、その例外(追跡される)がcallGetter「ハードコーディングされた文字列でのみ使用され、実際には発生しない」ために飲み込まれた場合の痛みを想像してください。(誰もそれをしないだろう、誰かが主張するかもしれない?OPが彼のSO回答でそれ正確に行ったことを除いて。ゲッターのユーザーは、運が良ければ、無視された例外のコンソール出力に気付くかもしれません。

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

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

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

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

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

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

汎用ゲッター/セッターの欠点

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


8
すべての主要なポイントをヒットします。ほぼ完璧な答えです。ゲッターとセッターはパブリックフィールドよりもわずかに優れているが、より大きなポイントは正しいと私はするかもしれません。
ジミージェームズ

2
また、ゲッター/セッターもIDEで簡単に生成できることに言及する価値があります。
stiemannkj1

26
+1生産規模のプロジェクトでリフレクションロジックをデバッグすることは、国連によって拷問として認識され、違法化されるべきです。
誤り言語学者

4
「これらのプログラマーにとって理解しにくい」-これらのプログラマーにとって理解することは非常に困難ですが、熟練していても誰にとっても理解することは困難です。
BartoszKP

4
「静的解析では反射コードにアクセスできません」-これ。リファクタリングを可能にするために非常に重要です。また、静的分析ツールがより強力になると(たとえば、最新のTeam Foundation Serverでのコード検索)、ツールを有効にするコードを作成することがさらに重要になります。
ダンJ

11

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

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

(また、コードのデバッグが難しくなり、誤用する方法が増えるため使いやすさが損なわれます。適切な識別子ではなくマジックストリングを使用しているため、保守性が損なわれます...)


2
質問にはJavaというタグが付けられていることに注意してください。Beanにはパブリックフィールドがありませんが、リフレクションを介して見つけることができるフィールドgetX()およびsetX()メソッドを公開する場合があります。Beanは必ずしも値をカプセル化するものではなく、単にDTOである場合があります。そのため、Javaでは、ゲッターはしばしば苦痛です。C#プロパティは、あらゆる点ではるかに優れています。
アモン

11
ただし、C#プロパティはドレスアップされたゲッター/セッターです。しかし、はい、Beansの概念は災害です。
セバスチャンレッド

6
@amonそう、C#はひどいアイデアを取り入れて、実装が簡単になりました。
ジミージェームズ

5
@JimmyJamesそれは本当にひどい考えではありません。コードを変更してもABIの互換性を維持できます。多くの人が抱えていない問題だと思う。
ケーシー

3
@Caseyこの範囲はコメントを超えていますが、実装の内部詳細からインターフェースを構築するのは逆です。それは手続き型設計につながります。getXメソッドを使用するのが適切な場合もありますが、適切に設計されたコードでは非常にまれです。Javaのゲッターとセッターの問題は、それらを取り巻く定型的なコードではなく、ひどい設計アプローチの一部であるということです。それを簡単にすることは、顔を打ちやすくするようなものです。
ジミージェームズ

8

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

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

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

ユーザーが期待するインターフェイスを提供するには、各プロパティに個別のゲッター/セッターを記述する必要があります。それを行ったら、反射を使用しても意味がありません。


この理由は、他の答えの中で他のすべての理由よりも重要であるように思えます。
エスベンスコフペダーセン

-4

もちろん、悪い考えではありません。通常のJavaの世界では悪い考えです(ゲッターまたはセッターのみが必要な場合)。たとえば、一部のフレームワーク(spring mvc)またはlibraresはすでにそれ(jstl)を使用していますが、一部のjson o xmlパーサーはそれを使用しています。DSLを使用する場合は使用します。次に、ユースケースシナリオによって異なります。

事実として正しいことも間違っていることもありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.