「オブジェクト」「クラス」を読む1
書いている途中って感じ。これはひどい。
成果物
情報源
ドキュメント
以下の項目において概要説明があった。まだ書いている途中らしく、説明がなく見出しだけの項もあった。
- オブジェクト
- クラス
- モジュール
- メソッド
- 特異クラス
- 特異メソッド
オブジェクト
オブジェクトとはインスタンスのこと。特定のクラスからnew
されたメモリ実体。メンバ変数とメソッドを持つ。
Rubyではすべてがオブジェクトである。Javaは数値や真偽値のリテラルがプリミティブ型でありオブジェクトではない。なので数値はメソッドをもっていない。Rubyは数値リテラルさえもオブジェクトであるため、メソッドをもっている。
3.times {|i| p i}
すべてがオブジェクトである
公式ドキュメントのオブジェクトには以下のように書いてあった。
Ruby で扱える全ての値はオブジェクトです。
たぶん上記が答えだと思う。値というのが何を指しているのかわからないが。リテラル、シンボル、変数、定数のことかな? それ以外のクラス、モジュール、メソッドなどは値なんだろうか。よくわからない。
以下全文。
Ruby で扱える全ての値はオブジェクトです。 Ruby のオブジェクトに対して可能な操作はメソッド呼び出しのみです。あるオブジェクトが反応できるメソッドは、そのオブジェクトが所属するクラスによって一意に決定します。所属するクラスはオブジェクト生成時に決まり、その後は特異クラスの導入以外、所属クラスが変わることはありません。またオブジェクトは、特定のクラスに対比して、その「インスタンス」とも呼ばれます。
オブジェクトの生成は、一般には、別のオブジェクトのメソッドを呼び出すことによって行われます。
以下、非公式のドキュメント。
メソッドやブロックはオブジェクトではありません。
変数・定数も演算子も制御構造もデータではないので,オブジェクトではありません。
プログラムもそれ自体はオブジェクトではありません。
「Ruby はすべてのデータがオブジェクト」と表現すべきでしょう。
じゃあデータって何? 変数に代入できるものをデータと呼んでいるのかな? でもそれだと変数・定数はデータになるし、制御構造だって、すべては式であり値を返すのだから代入できる。まあ式自体が代入されるわけではないけれど。データの定義がよくわからなかった。
たぶん「すべてがオブジェクト」というのは既存のC言語やJavaと比べたときの表現なのだろう。数値型などのリテラルはオブジェクトでなくプリミティブだったので、その対比として「すべて」と言っただけなんだろうな。そもそもドキュメントには「すべての値」と書いてあり「すべて」ではないし。
「Rubyは数値、真偽値、文字列などのリテラル値でさえオブジェクトです」といったほうがいいということだろうか。
ところでdef
で定義されたメソッドって、本当にオブジェクトじゃないの? 以下で調べたらオブジェクトっぽいんだけど。
ri Method
= Method < Object (from ruby core) ------------------------------------------------------------------------ Method objects are created by Object#method, and are associated with a particular object (not just with a class). They may be used to invoke the method within the object, and as a block associated with an iterator. They may also be unbound from one object (creating an UnboundMethod) and bound to another.
おもいっきりMethod objects
って書いてますやん。
ri method
Looks up the named method as a receiver in obj, returning a Method object (or raising NameError). The Method object acts as a closure in obj's object instance, so instance variables and the value of self remain available.
ここでもMethod object
って書いてあるぞ。
メソッドもオブジェクトなんじゃないの? 上記の記事のいうことが嘘くさい。公式ドキュメントじゃないしな。
この分だとブロックもメソッドなんじゃない? たしか公式ドキュメントには「Rubyの演算子はすべて糖衣構文です」みたいなことが書いてあった。もしやRubyのブロックもProc
オブジェクトの糖衣構文だったりしない? 知らんけど。
ri Object
ri Object
= Object < BasicObject ------------------------------------------------------------------------ = Includes: Kernel (from ruby core) (from ruby core) ------------------------------------------------------------------------ https://github.com/seattlerb/minitest/blob/13c48a03d84a2a87855a4de0c959f 96800100357/lib/minitest/mock.rb#L192 Object is the default root of all Ruby objects. Object inherits from BasicObject which allows creating alternate object hierarchies. Methods on Object are available to all classes unless explicitly overridden. Object mixes in the Kernel module, making the built-in kernel functions globally accessible. Although the instance methods of Object are defined by the Kernel module, we have chosen to document them here for clarity. When referencing constants in classes inheriting from Object you do not need to use the full namespace. For example, referencing File inside YourClass will find the top-level File class. In the descriptions of Object's methods, the parameter symbol refers to a symbol, which is either a quoted string or a Symbol (such as :name). ------------------------------------------------------------------------ = Constants: ARGF: ARGF is a stream designed for use in scripts that process files given as command-line arguments or passed in via STDIN. See ARGF (the class) for more details. ARGV: ARGV contains the command line arguments used to run ruby. A library like OptionParser can be used to process command-line arguments. Bignum: An obsolete class, use Integer CROSS_COMPILING: [not documented] DATA: DATA is a File that contains the data section of the executed file. To create a data section use __END__: $ cat t.rb puts DATA.gets __END__ hello world! $ ruby t.rb hello world! ENV: ENV is a Hash-like accessor for environment variables. See ENV (the class) for more details. Fixnum: An obsolete class, use Integer ParseError: [not documented] RUBY_COPYRIGHT: The copyright string for ruby RUBY_DESCRIPTION: The full ruby version string, like ruby -v prints RUBY_ENGINE: The engine or interpreter this ruby uses. RUBY_ENGINE_VERSION: The version of the engine or interpreter this ruby uses. RUBY_PATCHLEVEL: The patchlevel for this ruby. If this is a development build of ruby the patchlevel will be -1 RUBY_PLATFORM: The platform for this ruby RUBY_RELEASE_DATE: The date this ruby was released RUBY_REVISION: The GIT commit hash for this ruby. RUBY_VERSION: The running version of ruby Readline: [not documented] STDERR: Holds the original stderr STDIN: Holds the original stdin STDOUT: Holds the original stdout TOPLEVEL_BINDING: The Binding of the top level scope TimeoutError: Raised by Timeout.timeout when the block times out. = Class methods: yaml_tag = Instance methods: !~, <=>, ===, =~, CSV, DelegateClass, Digest, define_singleton_method, display, dup, enum_for, eql?, extend, freeze, hash, inspect, instance_of?, instance_variable_defined?, instance_variable_get, instance_variable_set, instance_variables, is_a?, itself, kind_of?, method, methods, nil?, object_id, private_methods, protected_methods, public_method, public_methods, public_send, remove_instance_variable, respond_to?, respond_to_missing?, send, singleton_class, singleton_method, singleton_methods, stub, taint, tainted?, timeout, to_enum, to_s, to_yaml, trust, untaint, untrust, untrusted?, xmp
オブジェクトは、すべてのRubyオブジェクトのデフォルトのルートです。 オブジェクトはから継承します 代替オブジェクト階層の作成を可能にするBasicObject。 メソッド on Objectは、明示的にオーバーライドされない限り、すべてのクラスで使用できます。 カーネルモジュールでオブジェクトが混在し、組み込みのカーネル機能が作成されます グローバルにアクセス可能。 Objectのインスタンスメソッドは カーネルモジュールによって定義されているため、ここでそれらを文書化することを選択しました。 明快さ。 Objectから継承するクラスの定数を参照する場合、 完全な名前空間を使用する必要があります。 たとえば、内部のファイルを参照する YourClassは、最上位のFileクラスを検索します。 オブジェクトのメソッドの説明では、パラメータ 記号は、引用符で囲まれた文字列のいずれかである記号を指します またはシンボル(:nameなど)。
Object
クラスの親にBasicObject
というのがあるらしい。
そもそもオブジェクトといっているのはObject
クラスやそのインスタンスのことを指していたのだろうか? それとももっと一般的が概念として、メンバ変数とメソッドを持っているものとしての意味で言ったのだろうか。わからん。
クラス
クラスは自身に所属するオブジェクトが反応できるメソッドを決定します。所属するオブジェクトに対してあるメソッドを呼び出すことができるなら、そのメソッドが「クラスに定義されている」と言います。またメソッドはクラスとメソッド名によって一意に決定します。
クラスは必ずただひとつの「スーパークラス」を持ち、スーパークラスであるメソッドが定義されていれば自クラスでもそのメソッドが同じ内容で定義されていることになります。これを「継承」と呼びます。継承は推移するので、スーパークラスのスーパークラスが持つメソッドもまた自クラスにおいて定義されていることになります。
この説明はたぶんJavaやC#などのちゃんとしたオブジェクト指向言語と似たようなものだと思う。
モジュール
include
ちょっと特殊でクラスではない。しかし機能は同じ。
急に雑になった。
機能はおなじ? 嘘でしょ? new
できるの? 継承できるの?
irb
で確かめたが、継承もnew
もできない。嘘じゃん。これはひどい。そのことをクラスではない
の一言で説明したつもりってこと? いやいや伝わらないから。
module M; end module N < M; end : syntax error, unexpected '<' (SyntaxError)
> M.new : undefined method `new' for M:Module (NoMethodError)
じゃあモジュールってなんなの? 機能は同じっていうけど、すでに違うんですけど? ていうか機能ってなにを指しているの? 同じ部分があるなら違う部分もあるはずだよね? だってすべてが同じだったらクラスだけでいいし。モジュールというクラスとは違う概念をわざわざ用意したんだからクラスとは別物のはず。実際、クラスではない
って言ってるし。
なんのためにモジュールを作ったの? どうやって使うの? 用途は? なんの説明もないのでさっぱりわからん。
ググってみると以下がヒットした。
以下のように使うらしい。
- 名前空間として使う
- クラスへMixinして多重継承する
DRYに書くための存在だと思われる。異なるクラス間をまたいだ共通処理をモジュールに書いて各クラスへ取り込む。そういったことをするのがモジュールの肝なのだろう。
名前空間として使うのも大切だろう。名前重複はコードが肥大化すると避けられないからね。
使い方を網羅するのはちょっと大変そう。また別口で勉強しよう。今回はモジュールの用途だけを把握する感じでいい。
仕組みとしては継承チェーンに含まれることになるようだ。
メソッド
メソッドは実行することができます。その実行を開始することを通常「呼び出す」と言います。また呼び出すときにはオブジェクトを渡すことができ、そのオブジェクトを「引数」と呼びます。いくつの引数を受け取ることができるかはメソッドの定義時に決定し、変更することはできません。
ふつうは最初に「処理のまとまり」とか「関数」という説明をすると思うのだが。1つ以上の式や文をまとめたものとか。引数には言及しているのに戻り値の説明がないし。なんかもう超適当だな。
クラスとオブジェクト
Ruby プログラムからはクラスもまたオブジェクトとして扱うことができ、文による操作と合わせると以下の基本操作が可能です。
- メソッドの存在を問い合わせる
- メソッドを定義する
- メソッドの定義を取り消す
特異メソッド
1つのインスタンスに固有のメソッド。既存のメソッドを上書きする形で定義する。存在しない名前のメソッドを特異メソッドとして作ることはできない。
class C def m; p 'm'; end end c = C.new d = C.new c.m d.m def d.m; p 'M'; end c.m d.m
def d.m; ...
が特異メソッドの定義。これによりインスタンスd
だけはM
を返すようになる。
Rubyは特異メソッドが定義されると、特異クラスを生成して、継承チェーンに組み込む。
継承チェーンは以下が参考になった。
特異クラス
特異クラスはObject#singleton_class
によって確認できる。この特異クラスに定義されたものが特異メソッドである。特異クラスは元のクラスより継承チェーンの前方に来るため優先度が高い。よって特異メソッドが定義されたインスタンスは、通常のインスタンスメソッドよりも特異メソッドが優先されて呼び出される。
class C class << self def m; :M; end end end
class << C def m; :N; end end
上記URLを読むと「クラスメソッドは特異メソッドである」と言っているように見えるのだが。だって特異メソッドとクラスメソッドとextend
の3つの記法は、おなじ「クラスメソッドの定義」という見出しの中で紹介されている。つまり特異メソッドもクラスメソッドもextend
も同じってことでは?
Object#singleton_class
にメソッドをもたせる方法は以下の3つ。それらはクラスメソッドとして機能する。そういう解釈でよろしいか?
- クラスメソッド
- 特異メソッド
extend
Object#singleton_class
は特異クラスであり、すべてのオブジェクトが1つだけ持てる特殊なインスタンスである。まさにsingleton
の名にふさわしい。ところでインスタンスごとに1つではなく、クラスごとに1つである。それはクラスメソッドと同じではないだろうか。
でもちょっと待って。特異クラスが持っているのは特異メソッドでしょ? でも特異メソッドって特定のインスタンスだけがもつメソッドなんだよね?
あ、そうか。Rubyではすべてがオブジェクト。つまりクラスもオブジェクトだし、インスタンスもオブジェクトだ。そして特異クラスとは、すべてのオブジェクトが1つずつ持っているもの。つまりクラスも特異クラスを持っているし、インスタンスも特異クラスを持っている。で、クラスA
がもつ特異クラスに特異メソッドを定義したら、クラスメソッドになる。それに対し、クラスA
のインスタンスa
に対して、a
がもつ特異クラスに特異メソッドを定義したら、インスタンスa
の固有メソッドになる。そういうことか!
まとめる。特異クラスは全オブジェクトが1つだけ持てるObject#singleton_class
である。以下3つの特異クラスははそれぞれ異なるものである。
class A; end a1 = A.new a2 = A.new A.singleton_class a1.singleton_class a2.singleton_class
クラスがもつ特異クラスに特異メソッドを定義する記法は次のとおり。
class A def A.m; :cm; end end class B def self.m; :cm; end end class C; def self.m; :cm; end; end class << C def m; :CM; end end
インスタンスがもつ特異クラスに特異メソッドを定義する記法は次の通り。
class C def m; p 'm'; end end c = C.new d = C.new def d.m; p 'M'; end
なお、クラスがもつ特異クラスに特異メソッドを定義することは、ほかのプログラミング言語におけるクラスメソッドと同じである。なぜならそのクラスにひとつだけしか存在しないから。
インスタンスがもつ特異クラスに特異メソッドを定義することは、ほかのプログラミング言語における継承と似ている。ただし継承とは別物だ。Rubyは継承リストによって呼び出すメソッドを決定している。その仕組みを理解することが正しい。Module#ancestors
参照。
所感
特異クラス、特異メソッド、extend
あたりが怪しい。とくにextend
について。
- どんな記述方法があるのか
- その効果に違いはあるのか
まさかと思うが、include
やprepend
についてもメソッドがあって、class内に書いたときと動作が違ったりするんじゃないの?
もう疲れたよパトラッシュ。たのむから断片的な情報でなくまとめてくれや。ググッて断片情報みつけて試してこれまでのルールぶち壊されての繰り返しとか、勘弁して。ちゃんと勉強させてよ公式ドキュメント。