- 締切済み
クラス名やモジュール名の競合について
プログラミング言語のRubyでプログラムを作っていて疑問に思った事がありますので どなたかご存知の方が居れば教えてください。 Rubyにおいては同じクラス名やモジュール名であっても モジュール内に対象クラスやモジュールを定義する事で 「モジュール名::クラス名」等と言う参照が可能になっていますよね そこで気になったのですが、同名のクラスAとモジュールAがある場合に (つまりクラスAの名前=モジュールAの名前) class モジュールA::クラスB # コンストラクタ def initialize @prop = クラスA.new end end 等と記述すると、コンストラクタの内部の記述において クラスAの名前とモジュールAの名前が競合するので、クラスBの生成時にエラーが発生します。 この様なケースにおいて、コンストラクタの内部でクラスAのインスタンスを 正しく生成する方法はあるのでしょうか? 勿論前提として、クラスAは他のモジュールに含まれたクラスではありません。 Javaの様にパッケージ概念があって、全てのクラスについて何らかのパッケージに所属している等の 仕組みが無いので、時々この様な命名をしてしまい困る事があるので この様なケースを回避する方法についてご存知の方が居れば、教授ください。 よろしくお願いします。
- みんなの回答 (5)
- 専門家の回答
みんなの回答
- gotchin72
- ベストアンサー率100% (1/1)
Rubyのモジュールやクラスは定数です。同じ名前空間で同じ名前の定数を定義することはできません。 ですから、 class A end module A end とすると、A is not a module というエラーになります。 これは、クラスとして定義した A をモジュールとして開き直そうとしています。 但し、この class A と module A が別々のファイルに記述されていて、 class A、または。module A を呼び出した場合エラーにはなりません。 なぜなら、Ruby は定数の探索をして最初に到達したものを採用するからです。 そしてご質問のケースの場合、Ruby は module A を見つけたがクラスではないので、エラーを出しています。 a = A # => エラーにはならない。 a = A.new # => エラーになる。 つまり、同一の名前空間に同じ名前のクラスとモジュールを記述することはコーディングミスです。 ファイルが分かれていれば、呼び出さない限りはエラーにはなりませんが、 これではまともなプログラムが書けません。 同じ名前を使いたければ、名前空間を分けるようにしましょう。 例えば、 # coding: utf-8 module Dog class Voice def play puts "わん" end end end module Cat module Voice def cry puts "にゃあ" end end end tama = "ねこ" tama.extend Cat::Voice johns_voice = Dog::Voice.new tama.cry #=> "にゃあ" johns_voice.play #=> "わん" のようにします。
- ki073
- ベストアンサー率77% (491/634)
No.1,2です。 モジュールにせずにクラスの中にクラスを作るのはいかがでしょうか。 class A def initialize end class B def initialize @prop = A.new end end end p A::B.new No.1にも書きましたが、もしclass A::Aがあれば ::A.newで区別できます。
- tatsu99
- ベストアンサー率52% (391/751)
class クラスA # コンストラクタ def initialize end end module モジュールA class クラスB # コンストラクタ def initialize @prop = クラスA.new end end end 上記でエラーになるということですが、このエラーを解消するために、 module 汎用モジュールA class クラスA # コンストラクタ def initialize end end end としては、いかがでしょうか。 つまり class クラスAを汎用モジュールの名前空間に閉じ込めます。 以下は、こちらで実行したサンプルソースです。 ------------------------------------------ # coding:WINDOWS-31J module Common class NameA def initialize p "Class NameA initialize" end def put p "put A" end end end module NameA class NameB def initialize p "Class NameB initialize" @prop = Common::NameA.new end def put @prop.put end end end a = NameA::NameB.new a.put --------------------------------------- 実行結果 "Class NameB initialize" "Class NameA initialize" "put A" --------------------------------------- 実行環境は windows7 1.9.3p374 (2013-01-15) [i386-mingw32] です。
- ki073
- ベストアンサー率77% (491/634)
実際のプログラムを書いてもらえればわかりやすいのですが、 module A class B def initialize @prop = A.new end end end class A end p A::B.new のような場合でしょうか? この場合はA.newでエラーが出るのではなく、9行目のclass Aでエラーが出ます。 classとmoduleで同じ名前が使えないということです。(この場合はclass Aとmodule A) 名前の付け方に注意する必要があります。
- ki073
- ベストアンサー率77% (491/634)
ちょっと質問の意味が分からなかったのですが module ModuleA class ClassA end class ClassB def initialize @prop = ClassA.new end end end p ModuleA::ClassB.new を実行させると、@propの中にはModuleA::ClassAのインスタンスが代入されます。 自分が属しているモジュールの中にClassAがあるのでModuleA::ClassAとなりますが、もし無ければモジュールの外側にClassAがないか探します。モジュールの外側の場合は::ClassA.newで区別できます。 それとも module A class A end class B def initialize @prop = A.new end end end p A::B.new の場合でしょうか、これも全く問題はないですが。 こんな場合でしょうか module A end class A::A end class A::B def initialize @prop = A::A.new end end p A::B.new の場合は @prop = A.new とした場合にはエラーになります。 このような書き方はあまりしないように思います。
補足
質問が判り辛くて申し訳有りません。 私が回避したいケースは下記の様なケースです。 クラスAの名前=モジュールAの名前 と言う状況において class クラスA # コンストラクタ def initialize end end module モジュールA class クラスB # コンストラクタ def initialize @prop = クラスA.new end end end と言うコードのクラスBのインスタンスを生成する際に @prop = クラスA.new の処理のタイミングで、クラスAをモジュールAと解釈してしまい エラーが発生するのですが それを回避する方法があれば教えて頂きたいと言う質問でした。 つまりこのクラスAと言うのは、どのモジュールにも所属していないクラスであり、ki073さんの回答していただいた最後のケースに近い状況でのエラーの回避です。