• ベストアンサー

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

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

  • Java
  • 回答数5
  • ありがとう数10

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

  • ベストアンサー
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
質問者

お礼

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

関連するQ&A

  • コンストラクタについて

    これも試験問題らしくて自分でやってみましたのであってるかどうか自信なくて どなたかみてみていただけたらと思います.よろしくお願いします。 下のソースファイルをコンストラクタを用いたものに修正しなさい class Sconst{ int x,y,z; void print(){ System.out.println(x); System.out.println(y); System.out.println(z); } } class ExConstTest{ public static void main (String[] args ){ Sconst sc=new Sconst(); sc.x=10; sc.y=30; sc.z=5; } } ------------------------------------------------------------- class Sconst{ int x,y,z; Sconst(){ x=10; y=30; z=5; } } class ExConstTest{ public static void main (String[] args ){ Sconst a1; Sconst a2; Sconst a3; a1=new Sconst(); a2=new Sconst(); a3=new Sconst(); System.out.println(a1.x); System.out.println(a2.x); System.out.println(a3.x); } }

    • ベストアンサー
    • Java
  • ロックの取得とwaitメソッド

    インスタンスに対してwait()メソッドを実行するには、そのインスタンスのロックを取得していなくてはならないと思っています。 method2()がエラーになるのと、method3が正常に動作するのは、 インスタンスに対するロックの有無で理解できます。 ただ、下記のソースでmethod()がエラーとならない動作が理解でき ませんでした。 このメソッドだと、メソッド単位ではロックがかかっていますが、 synchronizedでないメソッドが存在すれば別スレッドからアクセスでき るので、インスタンスに対するロックを取得しているわけではないと思 います。 私の理解が間違っているのでしょうか。 class MyClass { String str = new String("a"); public synchronized void method(){ try{ wait(); } catch (Exception e) { e.printStackTrace(); } } public void method2(){ synchronized (str) { try{ wait(); }catch(Exception e){ e.printStackTrace(); } } } public void method3(){ synchronized (this) { try{ wait(); }catch(Exception e){ e.printStackTrace(); } } } } class UseMyThread1 { public static void main(String args[]) { MyClass mc1 = new MyClass(); MyClass mc2 = new MyClass(); MyClass mc3 = new MyClass(); //mc1.method();//ここはエラーにならない System.out.println("*********"); //mc2.method2(); //ここはエラーになる System.out.println("*********"); //mc3.method3();//ここはエラーにならない } }

    • ベストアンサー
    • Java
  • Javaのコンストラクタについて教えてください

    Javaを勉強している初心者です。 次のようなプログラムがあります。 このプログラムでclass aおよびclass bのデフォルトコンストラクタ a() {}とb() {}をコーディングしていないとコンパイルエラーになります。 b() {}についてはclass bのパラメータのあるコンストラクタb(String s)がサブクラスclass cから明示的に呼ばれていないのでデフォルトコンストラクタb() {}をコーディングしないとエラーになる…と考えればよいのでしょうか。 それでは、a() {}はなぜ必要なのでしょうか。 どなたか教えてください。 class a { a() {} a(String s) { System.out.println("In a's constructor..."); System.out.println(s); } } class b extends a { b() {} b(String s) { super(s); System.out.println("In b's constructor..."); System.out.println(s); } } class c extends b { c(String s) { System.out.println("In c's constructor..."); System.out.println(s); } public void some() { System.out.println("something..."); } } public class appJ01 { public static void main(String args[]) { c obj = new c("Hello from Java!"); } } 

  • コンストラクタの動作について

    下記のソースについて質問があります。 public class Test{ private String msg; public Test(){ this("Good morning"); } public Test(String msg){ msg = msg; } public String toString(){ /*(2)*/ return ("msg:" + msg); } public static void main(String args[]){ System.out.println(new Test()); /*(1)*/ } } このコードをコンパイルした出力結果は、 msg:null となるのですが、ここの仕組みがわかりません。 (1)でTestクラスのコンストラクタを呼び出し、msgに"Good morning"を設定し、 処理が終わると思うのですが、(2)の処理も行われてしまいます。 (1)では、Testのコンストラクタを設定しているだけに見えてしまうのですが、 (2)まで処理が行われるのは、何故かのかをご教授の程お願い致します。

    • ベストアンサー
    • Java
  • JAVAコンストラクタについて

    JAVA のコンストラクタ定義の際に、下記のようにコンストラクタにvoidを付けた時と付けない時の処理結果が変わるのはなぜでしょうか。 (1) class O { protected int d; O(){ System.out.println("O = " + d); } } class Study_6_3main_void { public static void main(String args[]) { int i = 1; O OO = new O(); System.out.println("O = "); } } (2) class O { protected int d; void O(){ System.out.println("O = " + d); } } class Study_6_3main_void { public static void main(String args[]) { int i = 1; O OO = new O(); System.out.println("O = "); } } 実行結果 (1) O = 0 O = (2) O = 以上です。回答の程よろしくおねがいします。

    • ベストアンサー
    • Java
  • djUnit コンストラクタがprivateでテストが行えない

    djUnit コンストラクタがprivateでテストが行えない djUnitを使っております。 テスト対象のメソッドにアクセスしてテストを行いたいのですが、 どうもそれができません。 理由としては、コンストラクタがprivateになっているから?です。 (「コンストラクター TestManager() は不可視です」というエラーが出ます。) コンストラクタのprivateを消せば、テスト対象のメソッドにアクセスできるのですが、 実際には消してテストをしてはだめということになっています。 コンストラクタがprivateでもdjUnitでテストが行える方法がございましたら、 書き方をご教授いただけないでしょうか? どうかよろしくお願いいたします。 一応下記が詳細です。 (クラス名、コンストラクタ名、メソッド名) ------------------------ [クラス] public final class TestManager [コンストラクタ] private TestManager() { } [テスト対象のメソッド] public Structure testCreate(byte[], byte[], byte[]) ----------------------- 色々試したのですが、途中で終了してしまって 目的のテスト対象のメソッドを実行するところまで辿りつけません・・・。 [お試しパターン1] Class c = TestManager.class; Constructor[] con = c.getDeclaredConstructors(); TestManager conIns = (TestManager)con[0].newInstance(); //ここで処理が終了してしまう [お試しパターン2] Class testClass = Class.forName("TestManager"); //ここで処理が終了してしまう Constructor[] TestManager = testClass.getDeclaredConstructors(); TestManager[0].setAccessible(true); Object conIns = TestManager[0].newInstance((Object[])null); TestManager test = (TestManager)conIns;

    • ベストアンサー
    • Java
  • Javaにおけるメソッド名・ライン数取得に関して

    Javaの実行コンソールにて、メソッド名・ソースのライン数を出力させたいと思ってます。 C言語でいう、__func__や、__LINE__のように、 下記のように、System.out.println()内に埋め込み、それを出力させたいのです。(下記では、不明箇所をxxxとさせて頂いております。) <ソース-TestClass.java-> public void main(){ Test(); return; } private static void Test(){ System.out.println("メソッド名["+xxx+"] ライン["+ xxx +"]"); return; } <実行ターム> > java TestClass > メソッド名[Test] ライン[14] Javaではそういったメソッド名やライン数などの取得は困難なのでしょうか?または、そういったAPIは用意されておりますでしょうか? ご存じの方いらっしゃいましたら、ご教授お願い致します。

  • コンストラクタを使用したJavaのコンパイルがうまくいかない

    質問内容は、タイトル通りなのですが、 まず、元になるコンストラクタを使用したクラスのソースを以下に書きます。 class test1{ test1(){ System.out.println("コンストラクタ完了"); } } これをtest1.javaという形でコンパイルも終了し、 次に以下のようなソースを書いたstt1.javaを作りました。 class stt1{ public static void main(String[] args){ test1 t0; t0 = new test1(); } } これをコンパイルしようとしたのですが、 シンボルが見つけられないというエラーが出てしまいました。 シンボル:クラス test1 場所:stt1のクラス と出ていました。 現状として、解決方法をネットなどで調べたのですが問題がどこにあるのか分からない状態です。 どなたか詳しい方がいましたら教えていただけると幸いです。

  • Javaのメソッド呼び出しについて

    void usage() { System.out.println("zzz"); System.out.println("xxx"); System.out.println("ccc"); } void finish() { System.out.println("aaa"); } 上記の2つのメソッドを、mainメソッドから呼び出したいのですが どうやるのでしょうか? かなり足りない部分があるかと思うんですが、 おしえていただけないですか?

    • ベストアンサー
    • Java
  • コンストラクタ,interface,abstractの呼び出し順について

    現在、java初心者入門などの本で勉強しております 下記についてご教授お願い致します コンストラクタ、interface、abstractの呼び出し順について質問なのですが、 下記プログラムを実行したところ(同一のパッケージ内に明記) // インターフェイス public interface interFaceClassSS {    public void show(); } public interface interFaceClassS {    public void show(); } // 抽象クラス public abstract class ClassSSSab {    // 共通のメソッドを実装    //個々のメソッド    abstract void show(); } // スーパークラス public class ClassSSS extends ClassSSSab{    public ClassSSS(){       System.out.println("ClassSSS");       show();    }    public void show(){       System.out.println("ClassSSSabの抽象メソッドを実装");    } } public class ClassSS extends ClassSSS implements interFaceClassSS {    public ClassSS() {       System.out.println("ClassSS");       show();    }    public void show() {       System.out.println("interFaceClassSSを実装");    } } // メイン処理 public class ClassS extends ClassSS implements interFaceClassS {    public ClassS() {       System.out.println("ClassS");       show();    }    public static void main(String[] args) {       new ClassSSS();       System.out.println("");       new ClassSS();       System.out.println("");       new ClassS();    }    public void show() {    System.out.println("interFaceClassSを実装");    } } // 結果 ClassSSS ClassSSSabの抽象メソッドを実装 ClassSSS interFaceClassSSを実装 ClassSS interFaceClassSSを実装 ClassSSS interFaceClassSを実装 ClassSS interFaceClassSを実装 ClassS interFaceClassSを実装 の結果となりました。 当方が望む結果は、 ClassSSS ClassSSSabの抽象メソッドを実装 ClassSSS ClassSSSabの抽象メソッドを実装 ClassSS interFaceClassSSを実装 ClassSSS ClassSSSabの抽象メソッドを実装 ClassSS interFaceClassSSを実装 ClassS interFaceClassSを実装 上記となります。 new ClassSSSの処理は当方が望む結果なのですが、 new ClassSSでは、 newClassSSSの結果に ClassSS interFaceClassSS が追加されると理解していたのですが、結果は、 違っておりました。 どこに誤りがあるのが、数日検討したのですが、分からない状態です。 ご教授の程お願い致します。

    • ベストアンサー
    • Java

専門家に質問してみよう