メソッド呼び出しで 'this'を渡しているJavaの慣習


93

現在のオブジェクトをメソッド呼び出しで渡すことは良い/悪い/許容できる方法ですか?のように:

public class Bar{
    public Bar(){}

    public void foo(Baz baz){
        //  modify some values of baz
    }
}

public class Baz{
    //constructor omitted

    public void method(){
        Bar bar = new Bar();
        bar.foo(this);
    }
}

具体的には、ラインはbar.foo(this)受け入れ可能ですか?


60
なぜそれは受け入れられないのですか?それは一般的です。
DenysSéguret2013

3
だから...それは8はいです:)(そしてはい、私はそれを更新し続けています。)
アレックス・ギッテマイヤー

2
非静的な匿名クラスは、スーパークラスのこの参照を自動的に渡すため、これは許容されます。唯一の注意は、循環参照に注意することです。
Mehul Rathod 2013

5
ただし、注意点が1つあります。これは、オブジェクトを矛盾した状態で公開するため、コンストラクターでこれを渡すべきではありません。人々は通常、匿名の内部クラスとしてコールバック(ActionListenerなど)を作成し、それを別のオブジェクトに渡すときにこれを行います。
Tamas Rev 2013

3
@dystroyは許容できると私は同意しますが、それが一般的であるため許容可能であることを示唆しているのは、本当に悪いロジックです。よくあることで何かを行うと、多くの問題が発生する可能性があります
Carrie Kendall

回答:


155

これを使用しない理由はありません。this現在のインスタンスであり、使用することは完全に正当です。実際、それを省略する明確な方法がないことがよくあります。

だからそれを使う。

例がなければ受け入れられると納得させるのは難しいため(このような質問に対する否定的な答えは常に議論しやすい)、私は最も一般的なjava.langクラスの1つを開きStringました。もちろん、この使用例を見つけました。

1084        // Argument is a String
1085        if (cs.equals(this))
1086            return true;

探し(this大きな「受け入れ」プロジェクトでは、あなたはそれを見つけるために失敗することはありません。


6
完璧な答え。
maxf130 2013

15
-1。双方向のクラスの関係が単方向の関係よりも複雑であるという事実についての言及がないため。ソフトウェアをできるだけ明確にすることが重要です。上記の特定の例では、2つのクラス間の双方向参照を回避し、動作とデータを一緒にするために、メソッド fooをBazクラスに移動する方が賢明です。
JW。

35
質問へのコメントに追加のマイナーな詳細が質問で見つかったため、回答の-1は?本当に ?
DenysSéguret2013

13
@dystroyはい、オープニングフレーズに同意しません。「使用しない理由はありません」。
JW。

4
実際には、(OPの単純化された例とは異なり)実際のコードthisでは、継承やインターフェイスなどのため、通過しても双方向リンクが追加されるわけではありません。
DenysSéguret2013

165

何も問題はありません。まだ完全に初期化されていないオブジェクトへの参照を与えるため、コンストラクター内で同じことを行うのは良い習慣ではありません

似たような投稿がここにあります。Javaがコンストラクターこれをリークし、後者がなぜ悪い習慣であるかを説明しています。


18
+1:コンストラクタで「this」を参照する際の危険性を指摘するのは良いことです。
バトシェバ2013

これは必ずしも悪い習慣ではありません。例えば、Carコンストラクタが作成することができるWheel、インスタンスをCarすることなくWheel、不完全に初期化される一方、Wheel対応せずCar、また不完全に初期化されます。この場合、CarのコンストラクターでthisWheelのコンストラクターに渡すことが許容される場合があります。もう1つの方法は、CarとWheelの両方にプライベートコンストラクターを持たせ、Car、Wheelを構築し、CarにWheelをインストールするファクトリ関数を使用することです。しかし、それは車の静的メソッドまたはホイールの静的メソッドでなければなりませんか?
Lie Ryan

もちろん、CarFactoryWheelInstallerProxyホイールをインストールするを作成する必要があります。
ケビン

6
@LieRyan Wheelはに完全に従属してCarおり、IMOはまったく知りませんCar
イズカタ2013

3
thisコンストラクタ内から使用することの唯一の悪い点は、thisまだ完全に構築されていないオブジェクト参照が信頼できないクライアントまたは不明なクライアント(またはそれが完全に構築されたオブジェクト)。thisコンストラクターから、一般的な初期化を実行するパッケージプライベートメソッドに渡すことは、私の意見では、受け入れられるだけでなく望ましいことです。
scottb 2013

42

はい、しかしあなたは2つのことに注意する必要があります

  1. オブジェクトがまだ構築されていないときに(つまり、コンストラクターで) これを渡す
  2. これを長期間存続するオブジェクトに渡すと、参照が存続し、このオブジェクトがガベージコレクションされるのを防ぎます。

1
Javaのコンストラクターは実際にはコンストラクターではないことに注意してください。Javaのコンストラクターを「初期化子」と呼ぶ方がおそらく適切です。Javaコンストラクター内では、オブジェクトには実際にメモリが割り当てられています。これは、オブジェクトがコンストラクター内にすでに存在/構築されているためです。
Lie Ryan

1
実際には、オブジェクトにはまだ初期化されていないインスタンス変数が含まれている可能性があるため、オブジェクトはまだ完全には機能していません。したがって、コンストラクターでこれを2番目のオブジェクトに渡すと、インスタンス変数をまだ初期化していないオブジェクトに対してメソッドを呼び出すことができます。
Stefanos T.

ただし、2番目のオブジェクトが渡されたオブジェクトが初期化されていないことを認識し、不透明なオブジェクトとして扱うか、そのような状態で安全であると宣言されたメソッドのみを呼び出す限り、問題は渡さthisれません。オブジェクトが割り当てられていない場合、これを行うことは不可能でした。
Lie Ryan


5

これは現在のオブジェクトを表します。あなたがやっていることは文法的に正しいですが、同じクラスのメソッドを呼び出している場合、これは必要ないと思います。


2
サンプルコードでは、Baz.method()は、BazのインスタンスをパラメーターとしてBar.foo()を呼び出すインスタンスメソッドです。したがって、OPは同じクラスのメソッドを呼び出していません。
CVn 2013

@MichaelKjörlingJunedは、foo()メソッドをBazクラスに移動することthisで、2つのクラス間を渡す必要がないと言っていると思います。したがって、余分な複雑さを追加する必要はありません。
JW。

4

同じ振る舞いを実現するためのより簡単な代替手段がある場合、メソッド呼び出しで現在のオブジェクトを渡すことは悪い習慣です。

定義により、双方向の関連付けはthis、あるオブジェクトから別のオブジェクトに渡されるとすぐに作成されます。

Martin Fowlerによるリファクタリングを引用すると:

双方向の関連付けを単方向に変更(200)

双方向の関連付けは便利ですが、コストがかかります。価格は、双方向リンクを維持し、オブジェクトが適切に作成および削除されることを保証するために追加される複雑さです。双方向の関連付けは多くのプログラマにとって自然ではないため、多くの場合、エラーの原因になります。

...

必要な場合は双方向の関連付けを使用する必要がありますが、使用しない場合は使用しないでください。双方向の関連付けが重要度を低下させていないことを確認したら、すぐに不要な端を削除します。

したがって、理論的にはthis、問題を解決するための他の方法を考え、真剣に取り組む必要があるとわかったときに、警報ベルを聞く必要があります。もちろん、最後の手段としてそれを行うことが理にかなっている場合もあります。

また、全体的な改善のためにコードを長期間リファクタリングする際に、設計を一時的に破壊して「悪い習慣」を実行することがしばしば必要になります。(一歩後退、二歩前進)。

実際に私は私のコードが改善されているを発見した大規模な疫病のような双方向リンクを避けることによって。


単純化された例を双方向リンクを確立する必要があると混同します。これをパラメーターとして渡すと、java.langソースコードの多くの例(たとえば、私の回答で見たもの)から明らかなように、双方向の依存関係を追加することを意味しません。この答えは私の意見ではコメントであるはずです。
DenysSéguret2013

@dystroy反対票を投じた理由を説明するコメントを追加していただきありがとうございます。いつも知っておくと良い。私は自分の回答を変更して、定義により、双方向の関連付けthisが通過するとすぐに作成されることを明確にします。
JW。

1
「定義により、これが渡されるとすぐに双方向の関連付けが作成されます。これにより、理解できない箇所が明確になります。私が与える例を見てください。の引数のタイプequalsはObject であるため、双方向リンクはありません。これは非常に一般的です。受信メソッドはその引数をより一般的なクラスまたはインターフェイスとして定義します。このようなパターンがJava 使用される理由の1つは、不要な依存関係を回避することです。先に進む前に、立派なJavaライブラリで引数として渡される多くの状況を確認することをthisおすすめします。
DenysSéguret2013

同意しないことに同意しましょう。this可能であればコードを渡すことを避けたので、私の人生はずっと楽になりました。他の人にもそうすることを勧めます。
JW。

2
@JW:この回答で与えられた推論は無関係です。それは、常にXの任意の値のために、簡単です何か他のものがあるかどうXを行うには悪い考えです
リー・ライアンは、

4

はい。合格するthisことはプログラミングで一般的ですが、それを使用することには賛否両論がありますが、そうすることは危険ではありません。


たくさんの副作用があります。複雑さが増します。
JW。

これらの多くの副作用がある場合、Javaソースコードでそのような単一の証拠を見つけることができませんでした。ソースコードからの@destroysの例を参照してください。
Suresh Atta 2013

2

渡すことthisが正しく、優れたデザインに従うもう1つの例を追加します:訪問者パターン。ビジターデザインパターンでaccept(Visitor v)は、メソッドは通常、を呼び出すだけの方法で実装されますv.visit(this)


1

許容できる

Oracle JAVAドキュメントのスニペット:

インスタンスメソッドまたはコンストラクター内では、これは現在のオブジェクト(メソッドまたはコンストラクターが呼び出されているオブジェクト)への参照です。これを使用して、インスタンスメソッドまたはコンストラクター内から現在のオブジェクトの任意のメンバーを参照できます。

これをフィールドで使用する

thisキーワードを使用する最も一般的な理由は、フィールドがメソッドまたはコンストラクターのパラメーターによって隠されているためです。


2
現在のオブジェクトの任意のメンバーを参照できます」-これは、「パラメーターとして渡すthisことは受け入れられますか?」という質問に答えないようです。
CVn 2013

2
これはthis.some_variable、ローカル変数ではなくクラス変数を参照する方法を示しています。thisパラメータとして渡すこととは関係ありません。
ホセサルバティエラ2013

0

Javaのすべてが値によって渡されます。しかし、オブジェクトがメソッドに渡されることはありません!
Javaがオブジェクトをメソッドに渡すとき、最初にオブジェクト自体のコピーではなく、オブジェクトへの参照のコピーを作成します。したがって、これはJavaで完全に使用されるメソッドです。そして、最も一般的に従う使用法。


9
話題のこのルックス。
DenysSéguret2013

4
「Javaのすべてが値によって渡されます。」-それは非常に誤解を招く最初のコメントです。実際には、すべてのオブジェクトは参照によって渡され、すべてのプリミティブ型は値によって渡されます。あなたはthisプリミティブ型への参照を決して持っていないので、私はあなたの「詳細」が混乱を招いていると思います。
スチュワート

1
@Stewart:彼は、オブジェクト全体がコピーされることを意味しないことをすぐに明らかにします。
LarsH

10
@Stewartいいえ、オブジェクトは参照渡しされオブジェクト参照は値渡しされます。これは重要な違いです-参照渡しは、オブジェクトを参照するローカル変数があり、その変数を別のメソッドに渡す場合、メソッドは変数が参照するオブジェクトを変更できることを意味しますが、これは間違いなく何かですあなたはJavaで行うことができます。このメソッド、独自の参照のコピーを介してオブジェクトの状態を変更できますが、参照のコピーを変更して別のものを指すことはできません。
Ian Roberts

2
@Stewart yoda.arachsys.com/csharp/parameters.htmlは、両方をサポートするC#のコンテキストで、参照渡しと値渡しの違いを説明する優れた記事です。
Ian Roberts
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.