コンストラクターとnew()
インスタンスを作成・初期化するコンストラクター関数を記述します。
「コンストラクターとnew()」はCoddyKit上の無料Lua Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLua Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Lua Academyコースには全4レッスンが含まれています。
コンストラクターの役割
コンストラクターはインスタンスを作成して初期化します。初期値を受け取り、値を検証し、未指定のフィールドにデフォルト値を設定して、新しいインスタンスを返します。適切に設計されたコンストラクターにより、すべてのインスタンスが有効な状態で開始されます。
local Player = {}
Player.__index = Player
function Player.new(name, hp, mp)
assert(type(name)=="string" and name~="","need name")
return setmetatable({
name = name,
hp = hp or 100,
mp = mp or 50,
alive = true,
items = {},
}, Player)
end
local p = Player.new("Hero")
print(p.name, p.hp, p.mp) -- Hero 100 50流れるようなコンストラクター
流れるようなコンストラクターは、setterメソッドからselfを返し、構築時のメソッドチェーンを可能にします。これはLuaにおけるビルダーパターンです。
local Config = {}
Config.__index = Config
function Config.new()
return setmetatable({_data={}}, Config)
end
function Config:set(k,v) self._data[k]=v; return self end
function Config:build() return self._data end
local cfg = Config.new()
:set("host","localhost")
:set("port",8080)
:set("debug",true)
:build()
print(cfg.host, cfg.port)コピーコンストラクター
コピーコンストラクターは、既存のインスタンスと同じ値を持つ新しいインスタンスを作成します。重要なのは、ネストされたテーブルを共有するか独立させるかに応じて、シャローコピーまたはディープコピーを使い分けることです。
local function deepCopy(orig)
if type(orig)~="table" then return orig end
local copy = {}
for k,v in pairs(orig) do copy[deepCopy(k)]=deepCopy(v) end
return setmetatable(copy, getmetatable(orig))
end
local Item = {}
Item.__index = Item
function Item.new(name, stats)
return setmetatable({name=name, stats=stats or {}}, Item)
end
function Item:clone()
return deepCopy(self)
end
local sword = Item.new("Sword",{damage=50})
local clone = sword:clone()
clone.stats.damage = 30
print(sword.stats.damage) -- 50 (unchanged)検証付きコンストラクター
コンストラクターの入力値は必ず検証してください。レベル2(呼び出し元の責任)では、入力が無効な場合に説明的なエラーをスローします。これによりバグを早期に検出し、役立つエラーメッセージを表示できます。
local Rectangle = {}
Rectangle.__index = Rectangle
function Rectangle.new(w, h)
assert(type(w)=="number" and w>0,"width must be positive number")
assert(type(h)=="number" and h>0,"height must be positive number")
return setmetatable({width=w, height=h}, Rectangle)
end
function Rectangle:area() return self.width * self.height end
function Rectangle:perimeter() return 2*(self.width+self.height) end
local r = Rectangle.new(5, 3)
print(r:area(), r:perimeter()) -- 15 16デフォルト値パターン
オプションのコンストラクターパラメーターをすっきり扱うには、defaultsテーブルを使用します。これにより、x = x or defaultの長い連鎖を避けられ、デフォルト値が明示的になって変更もしやすくなります。
local Sprite = {}
Sprite.__index = Sprite
local DEFAULTS = {x=0,y=0,rotation=0,scale=1,visible=true,alpha=1}
function Sprite.new(image, opts)
local cfg = opts or {}
local self = {image=image}
for k,v in pairs(DEFAULTS) do
self[k] = cfg[k] ~= nil and cfg[k] or v
end
return setmetatable(self, Sprite)
end
local s = Sprite.new("hero.png",{x=100,y=200})
print(s.x,s.y,s.scale) -- 100 200 1静的ファクトリーメソッド
クラスには、作成のシナリオごとに複数のファクトリーメソッドを用意できます。これは、単一のコンストラクターに多数のオプションパラメーターを持たせるよりもすっきりしています。
local Color = {}
Color.__index = Color
function Color.fromRGB(r,g,b)
return setmetatable({r=r,g=g,b=b}, Color)
end
function Color.fromHex(hex)
hex = hex:gsub("#","")
return setmetatable({
r=tonumber(hex:sub(1,2),16),
g=tonumber(hex:sub(3,4),16),
b=tonumber(hex:sub(5,6),16),
}, Color)
end
function Color:toHex()
return string.format("#%02X%02X%02X",self.r,self.g,self.b)
end
local red = Color.fromHex("#FF0000")
print(red:toHex()) -- #FF0000コンストラクターレジストリ
すべてのインスタンスを追跡するにはレジストリを使用します。これにより、「すべて破棄」や、ある型のすべてのオブジェクトの反復処理といったグローバル操作が可能になります。
local Enemy = {}
Enemy.__index = Enemy
Enemy._all = {}
function Enemy.new(name, hp)
local self = setmetatable({name=name, hp=hp}, Enemy)
Enemy._all[#Enemy._all+1] = self
return self
end
function Enemy.destroyAll()
for _, e in ipairs(Enemy._all) do
print("Destroying:", e.name)
end
Enemy._all = {}
end
Enemy.new("Goblin",30)
Enemy.new("Orc",80)
print("Count:", #Enemy._all) -- 2
Enemy.destroyAll()遅延初期化
計算コストの高いフィールドが存在する場合があります。__indexに関数を指定して、初回アクセス時に計算し、キャッシュしてください。
local BigData = {}
BigData.__index = function(t, k)
if k == "processed" then
print("Computing processed...")
local result = {}
for i,v in ipairs(rawget(t,"raw")) do result[i]=v*2 end
rawset(t,"processed",result)
return result
end
end
function BigData.new(raw)
return setmetatable({raw=raw}, BigData)
end
local d = BigData.new({1,2,3,4,5})
print(d.processed[3]) -- Computing... 6
print(d.processed[3]) -- 6 (cached, no recompute)インスタンスからのプロトタイプ
インスタンスをクローンすると、同じ基本値を持つ別のインスタンスを作成できます。クローンは同じメタテーブル(同じクラス)と、インスタンスデータのコピーを取得します。「テンプレート」オブジェクトに便利です。
local Unit = {}
Unit.__index = Unit
function Unit.new(t)
return setmetatable({name=t.name,hp=t.hp,atk=t.atk}, Unit)
end
function Unit:clone()
return Unit.new({name=self.name,hp=self.hp,atk=self.atk})
end
function Unit:__tostring()
return self.name.."(HP:"..self.hp..",ATK:"..self.atk..")"
end
local template = Unit.new({name="Soldier",hp=100,atk=10})
local u1 = template:clone(); u1.name="Alpha"
local u2 = template:clone(); u2.name="Beta"
print(tostring(u1),tostring(u2))コンストラクターのエラー安全性
構築の途中(リソースの割り当てなど)で失敗した場合は、不完全なオブジェクトが外部に漏れないようにしてください。pcallを使って構築エラーを捕捉し、必要に応じてリソースを解放します。
local FileHandle = {}
FileHandle.__index = FileHandle
function FileHandle.new(path, mode)
local f, err = io.open(path, mode or "r")
if not f then
return nil, "cannot open " .. path .. ": " .. err
end
return setmetatable({_f=f, path=path}, FileHandle)
end
function FileHandle:read(fmt) return self._f:read(fmt) end
function FileHandle:close() self._f:close() end
local fh, err = FileHandle.new("data.txt")
if not fh then print("Error:", err)
else print(fh:read("l")); fh:close()
endクイックチェック
LuaのOOPにおけるコンストラクター(new())の役割は何ですか?
復習:コンストラクター
まとめ:
- コンストラクターは検証と初期化を行い、setmetatableを呼び出す
- オプションパラメーターをすっきり扱うにはdefaultsテーブルを使用する
- 作成シナリオごとに複数のファクトリーメソッドを用意する
- テンプレートオブジェクトにはクローンコンストラクターを使用する
- 計算コストの高いフィールドには__indexによる遅延初期化を使用する
- リソース割り当てなどで失敗した場合、コンストラクターからnil+errを返す
よくある質問
「コンストラクターとnew()」レッスンは無料ですか?
はい。「コンストラクターとnew()」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Lua Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Lua Academyコースには全4レッスンが含まれています。
「コンストラクターとnew()」で何を学びますか?
インスタンスを作成・初期化するコンストラクター関数を記述します。 ブラウザで直接実行するハンズオンコードでLua Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Lua Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのLua Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「コンストラクターとnew()」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このLua Academyレッスンでコードを書いて実行できますか?
はい。すべてのLua Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- メタテーブルによるクラス
- コンストラクターとnew()
- インスタンスメソッドとself
- クロージャによるカプセル化