メソッドのオーバーライド
CoddyのLuaジャーニー「Object Oriented Programming」セクションの一部 — レッスン 37/70。
これまで、子クラスは親クラスのメソッドを継承し、新しいメソッドを追加してきました。しかし、もし子クラスが同じメソッド名で異なる振る舞いを必要とする場合はどうすればよいでしょうか?これはメソッドのオーバーライド(method overriding)と呼ばれます。
子クラスで親クラスと同じ名前のメソッドを定義すると、子クラスのバージョンが優先されます。Luaのルックアップチェーンは最初に子クラスをチェックします。そこでメソッドが見つかれば、探索を停止します。
local Animal = {}
Animal.__index = Animal
function Animal:new(name)
local obj = {name = name}
setmetatable(obj, Animal)
return obj
end
function Animal:speak()
print(self.name .. " makes a sound")
end
-- Dog は speak() をオーバーライドします
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
function Dog:speak()
print(self.name .. " barks: Woof!")
endこれで、各クラスが同じメソッド呼び出しに対して異なる応答をするようになります:
local generic = Animal:new("Creature")
local buddy = Dog:new("Buddy")
generic:speak() -- 出力: Creature makes a sound
buddy:speak() -- 出力: Buddy barks: Woof!Dogの:speak()は、Dogインスタンスにおいて親のバージョンを完全に置き換えます。これにより、一貫したインターフェースを維持しながら、特化した動作を作成できます。すべての動物が:speak()を実行できますが、種ごとにその方法は異なります。
チャレンジ
簡単さまざまな生き物たちが独自の鳴き声で話す、小さなアニマルキングダムを作ってみましょう!Animalという親クラスと、2つの子クラス(CatとCow)を作成します。それぞれが親の:speak()メソッドを独自の動作でオーバーライドします。
コードは4つのファイルに分けて構成します:
Animal.lua: 動物の名前を保存する:new(name)コンストラクタを持つベースクラスです。動物の名前の後にmakes a soundと出力する:speak()メソッドを含めてください。これが子クラスでオーバーライドされるデフォルトの動作になります。Cat.lua: Animalを継承する子クラスです。その:new(name)コンストラクタは親のコンストラクタを呼び出し、メタテーブルをCatに再リンクする必要があります。:speak()メソッドをオーバーライドして、猫が異なる鳴き声(猫の名前の後にmeows: Meow!)を出力するようにします。Cow.lua: Animalを継承するもう一つの子クラスです。Catと同じ方法でコンストラクタを設定してください。:speak()をオーバーライドして、牛が名前の後にmoos: Moo!と出力するようにします。main.lua: すべてをまとめます!入力から2つの名前を読み取り、最初の名前でCatを、2番目の名前でCowを作成します。その後、それぞれの:speak()を呼び出し、同じメソッド名でもクラスによって異なる出力が生成されることを確認してください。
2つの入力を受け取ります:
- 猫の名前
- 牛の名前
メインファイルで両方の動物を作成し、最初に猫の:speak()を呼び出し、次に牛の:speak()を呼び出してください。
例えば、入力がWhiskersとBessieの場合、出力は以下のようになります:
Whiskers meows: Meow!
Bessie moos: Moo!両方の動物が同じ:speak()メソッドの呼び出しに反応していますが、それぞれの型に固有の出力を生成していることに注目してください。Catのバージョンは親の汎用的な "makes a sound" という動作を完全に置き換えており、Cowも同様です。これがメソッドのオーバーライドの実践です。同じインターフェースで、異なる実装を実現しています!
チートシート
メソッドのオーバーライドにより、子クラスは親メソッドと同じ名前のメソッドを定義し、親の動作を特化した機能で置き換えることができます。
Luaがメソッドを検索するとき、まず子クラスを確認します。そこでメソッドが見つかれば、そのバージョンを使用し、親クラスの検索を停止します。
メソッドオーバーライドの基本的な例:
local Animal = {}
Animal.__index = Animal
function Animal:new(name)
local obj = {name = name}
setmetatable(obj, Animal)
return obj
end
function Animal:speak()
print(self.name .. " makes a sound")
end
-- Dogがspeak()をオーバーライド
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
function Dog:speak()
print(self.name .. " barks: Woof!")
endオーバーライドされたメソッドの使用:
local generic = Animal:new("Creature")
local buddy = Dog:new("Buddy")
generic:speak() -- 出力: Creature makes a sound
buddy:speak() -- 出力: Buddy barks: Woof!子クラスのメソッドは、その子クラスのインスタンスに対して親のバージョンを完全に置き換え、すべてのクラスで一貫したインターフェースを維持しながら、特化した動作を可能にします。
自分で試してみよう
-- main.lua: すべてをまとめる
local Cat = require('Cat')
local Cow = require('Cow')
-- 入力を読み込む
local catName = io.read()
local cowName = io.read()
-- TODO: 最初の名前でCatを作成する
-- TODO: 2番目の名前でCowを作成する
-- TODO: 最初にcatで:speak()を呼び出し、次にcowで呼び出す
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。