クラスの継承・作成・拡張

これまでのレッスンでクラスとは、オブジェクトの種類(型)だと学んできました。そして、あらゆるオブジェクトは何らかのクラスに属していて、このことを〇〇(クラス名)クラスのインスタンス(オブジェクト)という呼び方をすることも学びました。

吉田先生

このクラスとインスタンスの関係性はよく、たい焼き器とたい焼きの関係を例に表されています。


 

これまでのレッスンでクラスとは、オブジェクトの種類(型)だと学んできました。そして、あらゆるオブジェクトは何らかのクラスに属していて、このことを〇〇(クラス名)クラスのインスタンス(オブジェクト)という呼び方をすることも学びました。このクラスとインスタンスの関係性はよく、たい焼き器とたい焼きの関係を例に表されています。 

また、クラスは設計図のようなものとも表現されることがあります。これは、設計図に書いてある決まりに従い作られたオブジェクトというような感じで、クラスとオブジェクトの関係性を分かりやすく表しています。

では、オブジェクトがどのクラスに属しているかを見ていきましょう。

オブジェクト クラス
数値 Numeric
文字列 String
配列 Array
ハッシュ Hash
正規表現 Regexp
ファイル File
シンボル Symbol

〇〇というオブジェクトが〇〇というクラスに属しているか(インスタンスか)ということを調べたい場合は、以下のように調べます。

name = "山田"
p name.instance_of?(String) 
#=> true

このように「instance_of?」というメソッドで確認します。結果は「true(真)」か「false(偽)」で返ってきます。

継承

継承とは、定義済みのクラスを継承した新しいクラスを作成するという事です。既存のクラスを親とし子のクラスを新たに作るといったイメージです。

スーパークラスには共通で使いたい機能を持たせ、サブクラスではそのサブクラスのみで使いたい機能を持たせるという形になります。この他にも、継承を行う事によりスーパークラスで定義した機能をサブクラスで定義し直し違う役割を持たせたり、スーパークラスでの機能に追加機能をサブクラスで持たせるなどすることが出来ます。

文字列オブジェクトの属する「String」クラスや、配列オブジェクトの属する「Array」クラスなど、ruby内の全てのクラスは「BasicObject」というクラスのサブクラスです。これらは、あらかじめ用意されているクラスであり「組み込みクラス」と呼ばれるものです。

では組み込みクラスには、どのようなものが有るのか、また組み込みクラスの関係性どうなっているのかを以下の図を見て学びましょう。

オブジェクトがクラスに属しているか調べるには「is_a?」というメソッドで調べます。

name = "山田"

p name.is_a?(String)
 # => true

name(オブジェクト)は、stringに属しているか? という事ですね。前セクションで「instance_of?」を学んだと思いますが、この「is_a?」との違いについて、以下図を使ってご説明します。

このような関係性にある文字列オブジェクトがあるとします。そして、クラスに属しているか調べるときに以下のようなコードが使えるでしょう。

str = "a"

p str.instance_of?(String)
 #=> true

p str.instance_of?(BasicObject)
 #=> false

p str.is_a?(String)
 #=> true

p str.is_a?(BasicObject)
 #=> true


実行し結果を見てみてください。結果は「true(真)」か「false(偽)」どちらかになりますが、「is_a?」であれば上記画像でいうとオブジェクトが子クラスであるStringに属しているかに加え、子クラスを飛び越え、親クラスであるBasicObjectに属しているかも調べることが出来ます(trueになる範囲が広い)。

逆に「instance_of?」はStringクラスがBasicObjectに属していても、直接属しているStringクラスまでしか調べることが出来ません。

クラスの作成

クラスを定義する際には、様々な規則に従い作成する必要があります。また、構文やメソッドなども出てまいりますので、1つ1つ学んでいきましょう。

class

クラス定義する際には、この「class文」というものを使います。構文は以下のようになります。

class  クラス名
 定義内容
end

クラス名の1文字目は、大文字でなければなりません。

class User
  def welcome
    puts "welcome!"
  end
end 

上記の中のdef welcomeのように、class文の中(class クラス名~end)にメソッドを定義したとき、そのメソッドはインスタンスメソッドとなります。

initialize

前セクションでも登場したdef welcomeとは違い、この「initializeメソッド」はオブジェクトの初期化の処理のメソッドです。「クラス名.new」のコードを実行したとき、initializeメソッドが呼び出されます。では、皆さんも以下例を実行してみましょう!initializeメソッドとクラス名.newが関連づいていることが分かると思います。

class User
  def initialize(user_name = "new_user")
    @name = user_name
  end

  def welcome
    puts "welcome, #{@name}!"
  end
end 

yuki = User.new("yuki")
gest = User.new()
yuki.welcome
#welcome, yuki

インスタンス変数

前セクションで、「@name」という変数が出てきました。これは、インスタンス変数と呼ばれる変数です。インスタンス変数は「@」で始まります。スコープについてですが以下図をご覧ください。

ローカル変数が定義したメソッド外では参照できないのに対し、インスタンス変数は定義したメソッド外で参照することが可能です。つまり、インスタンス変数はクラス内の全メソッドが有効範囲となります。また、インスタンス変数は、オブジェクトごとにそれぞれ異なる値を保有できます。

アクセスメソッド

インスタンス変数はクラス外では、参照したり、値部分に新たな別の値を代入したりすることは出来ません。ですが、参照・変更のためのメソッドを定義すれば可能になるのです。では以下追加部分を記述し実行してみましょう。

ーーーー追加部分ーーーー

def  name_sanshou
  @name
end

def  change_name(new_name)
  @name = new_name
end

puts  yuki.name_sanshou
yuki.change_name("yuki_tanaka")
yuki.welcome

ーーーーーーーー

class User
  def initialize(user_name = "new_user")
    @name = user_name
  end

  def welcome
    puts "welcome, #{@name}!"
  end

  def name_sanshou
    @name
  end

  def  change_name(new_name)
    @name = new_name
  end
end 

yuki = User.new("yuki")
yuki.welcome
#welcome, yuki

puts  yuki.name_sanshou
#yuki

yuki.change_name("yuki_tanaka")
 #=> "yuki_tanaka"

yuki.welcome
#welcome, yuki_tanaka

このようにすればクラス外で参照・変更を行う事が出来るようになるわけですが、参照・変更したいインスタンス変数が複数ある場合は、コードがとても分かりにくくなってしまいます。そういった場合に、使うと良いのが「アクセスメソッド」です。アクセスメソッドには3種類あります。以下表をご覧ください。

呼び方 定義 出来ること
リーダー / ゲッター attr_reader :変数名 参照のみ
ライター / セッター attr_writer :変数名 変更のみ
アクセサー / アクセスメソッド attr_accessor :変数名 参照・変更の両方

このように、1行で書けてしまうので簡単でかつ見やすいですね。では、以下をアクセスメソッドにしてみましょう。

def  name_sanshou
  @name
end

def  change_name(new_name)
  @name = new_name
end

 

name_sanshoは変数nameを参照するためのもので、change_name(new_name)
は変数nameを変更するためのものです。これをアクセスメソッドにすると、

attr_accessor name

となります。これで変数nameを参照・変更するためのアクセスメソッドであるnameというメソッドができました。

self

メソッドのレシーバを参照するには、この「self」という変数を使います。

class User
  attr_accessor :name
  .
  .
  .
  def welcome
    puts "welcome, #{self.name}!"  
  end
end

yuki.welcome

という書き方になります。この場合のレシーバは「yuki」です。

クラスメソッド

こちらはレシーバが「クラス」そのものになります。「クラス」が受け取ることのできる命令という事になります。では、定義の仕方についてみていきましょう。

class << クラス名
 def メソッド名
  定義
 end
end

「class  <<  クラス名」というように特殊なクラス定義をする形になります。また、「class クラス名」のあとに「class  <<   self」と書く方法もあります。この方法が一般的に最も使われています。

class クラス名
 class &lt;&lt; self
  def メソッド名
   定義
  end
 end
end

もしくは、「def クラス名.メソッド名」というように書くこともできます。

def クラス名.メソッド名
 定義
end

また、この書き方の場合にも「self」を使うパターンがあります。

def self.メソッド名
 定義
end

「class << self」を特異クラス定義と呼び、特異クラス定義内で定義したメソッドを特異メソッドと呼びます。

特異クラス

上記の特異クラス定義をすると、特定のオブジェクト専用の特異メソッドというものを定義する事が出来ます。例として、「aisatsu_1」という変数に代入する「こんにちは」という文字列オブジェクトでのみ「welcome」という特異メソッドを使用してみます。

aisatsu_1 = "こんにちは"
aisatsu_2 = "こんにちは"

class &lt;&lt; aisatsu_1
 def welcome
  puts "#{self}、ようこそいらっしゃいました。"
 end
end

p aisatsu_1.welcome
#こんにちは、ようこそいらっしゃいました
p aisatsu_2.welcome
#NoMethodError: undefined method `welcome' for "こんにちは":String
aisatsu_1でのみwelcomeメソッドが呼び出されていればOKです!

クラス変数

インスタンス変数とは、オブジェクトごとで異なる値を持つことのできるものでした。それに対しクラス変数とは、クラス内で値が共有されるので、どのオブジェクトでも値は同じものになります。また、有効範囲(スコープ)は、クラス内(クラス内のメソッド内含む)と、クラスのインスタンスです。書き方は変数名の前に「@@」を付けます。

class Test
 @@aisatsu = "hello!"
 def test_method
   puts @@aisatsu
 end
end

hello = Test.new
hello.test_method
#hello!

制限

rubyではメソッドに以下3種類のアクセス制限の設定が付いています。

public クラス内外
private クラス内のみ
protected クラス内とサブクラス

何も指定しなければ、全てのメソッドはデフォルトで「public」となります。(initalizeメソッドだけは、「private」となります。publicに変更することは出来ません。)アクセス制限を変更する際は以下のように書きます。

アクセス制限種類 :メソッド名

記載する場所は2パターンあります。

class クラス名
 def test(メソッド名)
  定義
 end

private :test

end

この場合は「test」というメソッドを指定し、「private」と設定しています。

class クラス名
  private

 def test(メソッド名)
  定義
 end
 
 def test_2(メソッド名)
  定義
 end
end

この場合ですと、「private」以降のメソッドが全て「private」となります。

拡張

メソッドの追加

既存のクラス(組み込みクラスなど)にメソッドを追加し、クラスを拡張するという事も可能です。では、Stringクラスにメソッドを追加してみましょう。

class String
 def kurikaeshi_kotoba
  puts self + self
 end
end

"もし".kurikaeshi_kotoba
#もしもし

文字列が2回繰り返されていればOKです。

継承を行う

このレッスンの始めの方で継承について学びました。このセクションでは継承の仕方についてみていきましょう。書き方は以下のようになります。

class サブクラス名 &lt; スーパークラス名
 定義
end

では、社員クラス(スーパークラス)、社長クラス(サブクラス1)、従業員クラス(サブクラス2)という例を使い実際にコードを実行してみましょう。

class Personnel
 def shain
  puts "私たちは〇〇社の社員です"
 end
end

class President &lt; Personnel
 def shachou
  puts "私は社長です"
 end
end

class Worker &lt; Personnel
 def shain
  puts "私たちは〇〇社の社員であり従業員です"
 end
end

personnel = Personnel.new                                                                                                                                     
president = President.new
worker = Worker.new

personnel.shain
#私たちは〇〇社の社員です

president.shain
#私たちは〇〇社の社員です

worker.shain
#私たちは〇〇社の社員であり従業員です

このようにスーパークラスで定義したメソッドをサブクラスにて書き換えたり、スーパクラスで定義したメソッドを呼び出したり出来ます。

alias / undef

・alias

既存メソッドに他の名前を付けたい際に、この「alias」を使用します。書き方は以下のようになります。

alias メソッド名() メソッド名()

また、以下のようにシンボルで指定する事も可能です。

alias :メソッド名() :メソッド名()

では使い道について以下例を参考に学びましょう。

def aisatsu
  puts "Hello"
end

alias aisatsu aisatsu_nihongo
def aisatsu_nihongo
  aisatsu
  puts "は、日本語で言うとこんにちは"
end
aisatsu_nihongo

このように、元の名前のメソッドは消えるわけではないので、呼び出すことが可能です。

・undef

定義済みのメソッドを削除したい際などに使用します。書き方は以下のようになります。

undef メソッド名

また、以下のようにシンボルで指定する事も可能です。

undef :メソッド名

では、スーパークラスで定義したメソッドをサブクラスではないものとするという例を以下のように実行してみましょう。

class Test
 def foo
  puts 'hello'
 end
end

class Test_sub &lt; Test
 undef foo
end

test = Test.new
test_sub = Test_sub.new

test.foo
#hello

test_sub.foo
#NoMethodError: undefined method `foo' for #&lt;Test_sub:0x000000009b0d98>

サブクラスでのみメソッドfooが削除されていれば成功です。

まとめ

クラスとは設計図の様なものです。クラスから作成されたオブジェクトはインスタンスと呼ばれ、親クラスには共通の機能、その機能を受け継いで更に独自の機能を持つ子クラスというように継承を行う事が出来ます。

無料ビデオ講座のお知らせ

Skillhub [スキルハブ]では無料の動画講座を多数公開しています。他校だと数万円するような講座が無料で受講できます。

無料講座一覧を見る

×