• ベストアンサー

コンストラクタからメソッドを呼んではいけない?

会社の先輩から、 「コンストラクタでメソッドを呼ぶな!」をいわれ 理由も聞かずにソースを修正したのですが、 なぜコンストラクタからメソッドを呼んではいけないのでしょうか? 例: public class MyClass{ public MyClass(){ test(); } private void test(){ System.out.println("test"); } } よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
noname#86752
noname#86752
回答No.2

「Javaの鉄則」という本を読み返したら、一つ該当しそうなものが載ってました。(この本に載っている最後の鉄則です) 継承を使った場合のオブジェクトの初期化に起因する副作用のようです。 正直「こんなの見たことねーよ!」と思いましたが(本の中でも「この種のエラーは一般的ではないかもしれない」と書いてありました)、もし起こったら原因が分からず悩むでしょう。 上手く説明できる気がしないので、本を紹介するにとどめます。結構実践的に鉄則を学べるので良い本だと思います。 コンストラクタから呼ぶのはほとんどがアクセサメソッドでしたが、ちょっと複雑なものを呼ぶときは気をつけないといかんなと思いました。

kazuoao
質問者

お礼

「Javaの鉄則」、さっそく買ってきました(笑) とてもいい内容ですね! どうもありがとうございました!

その他の回答 (4)

  • k_tracker
  • ベストアンサー率48% (12/25)
回答No.5

私が先輩でもそういいます。 他の方が言っているように、コンストラクタでメソッドを呼んでも ちゃんと設計してあれば特に問題は起きません。 が、バグの元になるのでやめましょう。 この例ではメソッドの処理が簡単なので問題は起きません。 ただし、メソッドの処理ではクラスのフィールドを使うことも あるでしょう。 ではそのフィールドを初期化するのはどこでしょう? コンストラクタですね。 ということは、コンストラクタでメソッドを呼ぶと、 メソッド内で初期化してないフィールドにアクセスする 可能性が出てきます。 メソッドを作る際に、コンストラクタでのみ呼ぶメソッド! と決めて つくれば間違いは無いでしょう。 ですが、後で修正する場合などに間違いやすくなるのでやめた方が いい、ということです。 goto文を使うな! というのと同じでしょう。 ちゃんと作れば動くけど、バグの元になるから不文律として 使わない、という。

kazuoao
質問者

お礼

今後、クラス設計には十分注意したいと思います。 どうもありがとうございました!

  • kacchann
  • ベストアンサー率58% (347/594)
回答No.4

今度その会社の先輩に 「メソッドを呼びさえしなければ、 コンストラクタ内でいかなる処理を記述しても構わないのか。 なぜにメソッドはいけないのか。 なぜメソッドだけが、やり玉にあがるのか」 と聞いてみましょう! 「メソッド」に焦点があるのだとすれば、 僕が思いつくこととしては Java言語仕様(第2版)の12・5の最後に書かれていること、とか。 (#2さんの言ってることと同じかな、とは思うんだけど、 今、手元に「Javaの鉄則」がないので(どっかに行ってしまった)、 なんとも言えません) --- ちなみに、試しにjava swingコンポーネントのソースコードをいくつか 見てみましたが、 コンストラクタ内でメソッドをガンガン呼んでいます。 メッソド禁止となると、それはそれで不便なので、 (禁止にしたまっとうな理由があるにせよ、) 重要なのは、 会社の先輩から 「禁止にした理由」 を聞きだすことだと思います。 さもなくば、とんでもない勘違いをすることになりかねないと思います。

kazuoao
質問者

お礼

私もjava swingコンポーネントのソースコード見てみましたが、ほんとにガンガン呼んでますね(笑) これからはコンストラクタのコーディングに注意していきたいと思います。 どうもありがとうございました!

  • twk
  • ベストアンサー率29% (18/62)
回答No.3

 今度言われたらちゃんと理由を聞きましょうね。そうしたら先輩とも仲良くなって、より良いプログラマーになれます。  例の場合は単純なメソッドなのでまだよいですが、コンストラクターでメソッドを呼ぶのはバグのもとです。そもそも、コンストラクターの中では複雑な処理をするべきではありません。  コンストラクター内は、まだオブジェクトの状態が完全ではありません (例えばフィールドが初期化されていなかったりとかしますよね)。もし呼び出したメソッドや、そのメソッドが中で呼び出したメソッドが、完全な (コンストラクターが終わった状態の) オブジェクトの状態を要求すると、予期しない動作がおきがちです。  あなたが最初書いた時には、そのような記述はしないとしても、あとで別の人が、そのメソッドの側の記述を変更して、そこで初めて不具合が生じる、と言うのがありがちなパターンです。

kazuoao
質問者

お礼

すみません、なんせ先輩とてもきつい方なので・・・ コミュニケーションのほうもうまくとれるようにがんばりたいと思います。 いろいろ勉強になりました! どうもありがとうございます!

  • liar_adan
  • ベストアンサー率48% (730/1515)
回答No.1

まず、呼んでいけないということはありません。 当然コンパイルできます。動作もします。 なぜいけないか、三つ理由が考えられます。 1)メソッドを呼びだして、エラーが出ると話がややこしくなるため。 この場合コンストラクタが途中で止まってしまって、 正常に動きません。まあだいたいクラッシュしますが…。 2)他のクラスのメソッドを呼んだ場合、循環参照が起こって、 いつまで経っても実行されない可能性があるため。 println()なら、メソッドの属するオブジェクトが無い、という事態は起こりませんが、 一般のオブジェクトに属するメソッドでは 「これを動作するにはこのオブジェクトが必要で、 このオブジェクトにはこのオブジェクトが必要で、 さらにそのオブジェクトは今コンストラクタを起動しているオブジェクト」 ということが起きる可能性があります。 (設計が悪いだけかもしれないけど) 3)コンストラクタの段階では、一般のメソッドが使えるかどうかわからないため。 たとえばアプレットでは、コンストラクタの段階でできることは制限されます。 (これがアプレット制作者の悩みの種です) 一度アプレットを作ってから、init()の中で あれこれやる必要があります。 アプレット以外でも同様な問題のあることがあります。 いずれにせよ、「メソッドを使うな」というのは、 プログラミング作法に属する問題です。 私だったら「使うな」とは言わないけれど、 「できれば使わない方がいいなー」と思います。

kazuoao
質問者

お礼

とても勉強になりました! ありがとうございます!