はじめに
この記事は書籍「プロを目指す人のためのRuby入門」に掲載できなかったトピックを著者自らが紹介するアドベントカレンダーの19日目です。
本文に出てくる章番号や項番号は書籍の中で使われている番号です。
今回はStructクラスで単純なクラスを定義する方法を説明します。
必要な前提知識
「プロを目指す人のためのRuby入門」の第7章まで読み終わっていること。
Structクラスで単純なクラスを手軽に定義する
RubyにはStructという構造体(のようなクラス)を作るクラスが用意されています。このクラスは次のように使います。
# Structを使ってUserクラス(Userの構造体)を作成する
User = Struct.new(:first_name, :last_name)
# Structクラスから作成されたUserクラスを使う
user = User.new('Alice', 'Ruby')
# 属性を参照する
user.first_name
#=> "Alice"
user.last_name
#=> "Ruby"
# 属性を変更する
user.last_name = 'Python'
user.last_name
#=> "Python"
少し不思議なコードですが、Struct.new
を呼ぶとClassオブジェクトが返されます。
Struct.new(:first_name, :last_name).class #=> Class
これを定数(上のコードでいうとUser
)に代入すると、新しいクラスをその場で定義したのと同じことになるのです。Struct.new
の引数に渡したシンボルは、作成したクラスの属性になり、値を読み書きするメソッドが自動的に作成されます。また、作成したクラスのnew
メソッドに渡した引数は、それぞれStruct.new
で渡したシンボルに対応して作成したクラスの属性にセットされます。
また、Struct.new
で指定しなかった属性は読み書きできません。
User = Struct.new(:first_name, :last_name)
user = User.new('Alice', 'Ruby')
# ageという属性は存在しないので読み書きできない
user.age = 20
#=> NoMethodError: undefined method `age=' for #<struct User first_name="Alice", last_name="Ruby">
user.age
#=> NoMethodError: undefined method `age' for #<struct User first_name="Alice", last_name="Ruby">
さらに、Struct.new
にブロックを渡すと作成したクラスにインスタンスメソッドを定義することもできます。
User = Struct.new(:first_name, :last_name) do
# インスタンスメソッドの定義
def full_name
"#{first_name} #{last_name}"
end
end
user = User.new('Alice', 'Ruby')
# インスタンスメソッドの呼び出し
user.full_name
#=> "Alice Ruby"
上のコードは次のようにclass構文を使って書いてもほとんど同じ結果になります。
# Struct.newで返却されたクラスを継承してUserクラスを定義する
class User < Struct.new(:first_name, :last_name)
def full_name
"#{first_name} #{last_name}"
end
end
user = User.new('Alice', 'Ruby')
user.full_name
#=> "Alice Ruby"
このようにStructを使うと、データを保持するための単純なクラスを手軽に作ることができます。とはいえ、普通のクラスとStructで作ったクラスをどのように使い分けるのかは判断が難しいところです。一般的には「わざわざ明示的にクラスを定義するほどでもないケースに向いている」などと言われたりしますが、「じゃあ、それってどんなケース?」となってしまいます。筆者の意見としては初心者のうちはStructを無理に使おうとせず、普通のクラス定義を使う方が変な癖が付かなくて良いと思います。
次回予告
次回は特異クラスについて説明します。