dポイントプレゼントキャンペーン実施中!

プログラミング言語のRubyでプログラムを作っていて疑問に思った事がありますので
どなたかご存知の方が居れば教えてください。

Rubyにおいては同じクラス名やモジュール名であっても
モジュール内に対象クラスやモジュールを定義する事で

「モジュール名::クラス名」等と言う参照が可能になっていますよね

そこで気になったのですが、同名のクラスAとモジュールAがある場合に
(つまりクラスAの名前=モジュールAの名前)

class モジュールA::クラスB
 # コンストラクタ
 def initialize
  @prop = クラスA.new
 end
end

等と記述すると、コンストラクタの内部の記述において
クラスAの名前とモジュールAの名前が競合するので、クラスBの生成時にエラーが発生します。

この様なケースにおいて、コンストラクタの内部でクラスAのインスタンスを
正しく生成する方法はあるのでしょうか?

勿論前提として、クラスAは他のモジュールに含まれたクラスではありません。

Javaの様にパッケージ概念があって、全てのクラスについて何らかのパッケージに所属している等の
仕組みが無いので、時々この様な命名をしてしまい困る事があるので
この様なケースを回避する方法についてご存知の方が居れば、教授ください。

よろしくお願いします。

A 回答 (5件)

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 #=> "わん"



のようにします。
    • good
    • 1

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で区別できます。
    • good
    • 1

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]
です。
    • good
    • 0

実際のプログラムを書いてもらえればわかりやすいのですが、


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)
名前の付け方に注意する必要があります。
    • good
    • 0

ちょっと質問の意味が分からなかったのですが


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さんの回答していただいた最後のケースに近い状況でのエラーの回避です。

補足日時:2013/02/17 21:46
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!