子クラスのメソッドの追加
CoddyのLuaジャーニー「Object Oriented Programming」セクションの一部 — レッスン 34/70。
継承によって子クラスは親クラスのメソッドにアクセスできるようになりますが、その真の力は、子クラスだけが持つ新しいメソッドを追加できることにあります。DogはAnimalができるすべてのことに加えて、吠えるといった犬特有の動作を行うことができます。
子クラス固有のメソッドを追加するのは簡単です。子クラスのテーブルでそれを定義するだけです。インスタンスは最初に自身のクラス内でメソッドを検索するため、親クラスに影響を与えることなく新しいメソッドを見つけることができます。
local Animal = {}
Animal.__index = Animal
function Animal:new(name)
local obj = {name = name}
setmetatable(obj, Animal)
return obj
end
function Animal:breathe()
print(self.name .. " is breathing")
end
-- DogはAnimalを継承します
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})
function Dog:new(name)
local obj = Animal.new(Animal, name)
setmetatable(obj, Dog)
return obj
end
-- Dogにのみ存在する新しいメソッド
function Dog:bark()
print(self.name .. " says: Woof!")
endこれで、Dogインスタンスは継承されたメソッドと独自のメソッドの両方を使用できるようになります:
local buddy = Dog:new("Buddy")
buddy:breathe() -- 出力: Buddy is breathing (継承)
buddy:bark() -- 出力: Buddy says: Woof! (子クラスのみ)親クラスは変更されません。通常の Animal に対して :bark() を呼び出すと、そのメソッドはそこには存在しないため失敗します。これが、共通の機能を一箇所に保持しながら、継承によって振る舞いを特化させる仕組みです。
チャレンジ
簡単猫が独自の特別な能力を持つ、ペットの階層構造を構築しましょう!Petという親クラスとCatという子クラスを作成します。CatはPetからすべてを継承しますが、猫だけができるユニークなメソッドも持っています。
コードは3つのファイルに分けて構成します:
Pet.lua: ペットの名前を保存する:new(name)コンストラクタを持つ親クラスを定義します。ペットの名前の後にsays helloと出力する:greet()メソッドを含めてください。標準的なクラスパターンを設定し、クラスを返します。Cat.lua: Petを継承する子クラスを定義します。Catの:new(name)コンストラクタは、親のコンストラクタを呼び出して名前を初期化し、メタテーブルをCatに再リンクする必要があります。猫の名前の後にpurrs contentedlyと出力する、子クラス固有の:purr()メソッドを追加してください。このメソッドはCatにのみ存在し、Petには存在しません!main.lua: Catモジュールを読み込み(require)、入力から名前を読み取り、Catのインスタンスを作成します。そして、継承された:greet()メソッドと、独自の:purr()メソッドの両方が使用できることを示してください。
入力として、猫の名前を1つ受け取ります。
メインファイルで、与えられた名前でCatを作成し、:greet() に続いて :purr() を別々の行で呼び出してください。
例えば、入力が Whiskers の場合、出力は以下のようになります:
Whiskers says hello
Whiskers purrs contentedly最初の行はPetで定義され継承された :greet() メソッドによるもので、2行目はCatにのみ存在する :purr() メソッドによるものです。これは、子クラスが親クラスにできることすべてに加えて、独自の特化した振る舞いができることを示しています!
チートシート
子クラスは、親のすべてのメソッドを継承しながら、自身だけが持つ新しいメソッドを追加できます。インスタンスはまず自身のクラスでメソッドを探し、見つからない場合は親クラスに遡ります。
子クラス固有のメソッドを追加するには、子クラスのテーブルで定義します:
local Animal = {}
Animal.__index = Animal
function Animal:new(name)
local obj = {name = name}
setmetatable(obj, Animal)
return obj
end
function Animal:breathe()
print(self.name .. " is breathing")
end
-- Dog inherits from Animal
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})
function Dog:new(name)
local obj = Animal.new(Animal, name)
setmetatable(obj, Dog)
return obj
end
-- Child-specific method
function Dog:bark()
print(self.name .. " says: Woof!")
end子クラスのインスタンスは、継承されたメソッドと固有のメソッドの両方を使用できます:
local buddy = Dog:new("Buddy")
buddy:breathe() -- Inherited from Animal
buddy:bark() -- Child-only method親クラスは影響を受けません。親クラスは子クラス固有のメソッドにはアクセスできません。
自分で試してみよう
-- main.lua: Catを作成し、継承をデモンストレーションします
local Cat = require('Cat')
-- 入力から猫の名前を読み込む
local name = io.read()
-- TODO: 指定された名前でCatインスタンスを作成する
-- TODO: 継承された :greet() メソッド(Petから)を呼び出す
-- TODO: Cat固有の :purr() メソッドを呼び出す
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。