Lua Academy · Oppitunti

Ylikirjoittaminen ja polymorfismi

Ylikirjoittakaa emoluokan metodeja ja toteuttakaa ajonaikainen polymorfinen metodikutsu.

Oppitunti 4/412 vaihetta

Ylikirjoittaminen ja polymorfismi on ilmainen Lua Academy-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Lua Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Lua Academy-kurssilla on yhteensä 4 oppituntia.

Metodin ylikirjoittaminen

Lapsiluokka ylikirjoittaa isäntäluokan metodin määrittelemällä samannimisen metodin. Kun metodia kutsutaan lapsiolion kautta, lapsiluokan versio suoritetaan. Isäntäluokan versio on edelleen käytettävissä muodossa Parent.method(self).

local Shape={} Shape.__index=Shape
function Shape:area() return 0 end
function Shape:describe() print(type(self).." area="..self:area()) end

local Circle={} Circle.__index=Circle
setmetatable(Circle,{__index=Shape})
function Circle.new(r) return setmetatable({r=r},Circle) end
function Circle:area() return math.pi*self.r^2 end

local Square={} Square.__index=Square
setmetatable(Square,{__index=Shape})
function Square.new(s) return setmetatable({s=s},Square) end
function Square:area() return self.s^2 end

Circle.new(5):describe()
Square.new(4):describe()

Polymorfinen hajautus

Polymorfismissa erityyppiset oliot vastaavat samaan viestiin eri tavoin. Funktio, joka hyväksyy minkä tahansa "shape"-olion ja kutsuu metodia :area(), toimii kaikilla muototyypeillä — kukin käyttää omaa toteutustaan.

local shapes={
  Circle.new(3),
  Square.new(4),
  Circle.new(1),
}

local total=0
for _,shape in ipairs(shapes) do
  total=total+shape:area()
end
print(string.format("Total area: %.2f",total))

Ankkatyypitys

Lua käyttää ankkatyypitystä: jos oliolla on metodi, se toimii. Muodollista rajapintamäärittelyä ei tarvita. Funktio, joka kutsuu metodia :speak(), toimii minkä tahansa speak-metodin sisältävän olion kanssa luokasta riippumatta.

local function makeNoise(obj)
  if type(obj.speak)=="function" then
    obj:speak()
  else
    print("(silent)")
  end
end

local Duck={speak=function(self) print("Quack!") end}
local Robot={speak=function(self) print("Beep boop!") end}
local Rock={}

makeNoise(Duck)   -- Quack!
makeNoise(Robot)  -- Beep boop!
makeNoise(Rock)   -- (silent)

Operaattorien polymorfismi

Metametodit, kuten __add, mahdollistavat operaattorien polymorfismin. Eri tyypit voivat toteuttaa operaattorille + oman semantiikkansa, kunhan ne määrittelevät __add-metodin.

local Vec2={} Vec2.__index=Vec2
Vec2.__add=function(a,b) return Vec2.new(a.x+b.x,a.y+b.y) end
Vec2.__tostring=function(v) return "("..v.x..","..v.y..")" end
function Vec2.new(x,y) return setmetatable({x=x,y=y},Vec2) end

local Color={} Color.__index=Color
Color.__add=function(a,b)
  return Color.new(math.min(255,a.r+b.r),math.min(255,a.g+b.g),math.min(255,a.b+b.b))
end
function Color.new(r,g,b) return setmetatable({r=r,g=g,b=b},Color) end

print(tostring(Vec2.new(1,2)+Vec2.new(3,4)))  -- (4,6)

Visitor-suunnittelumalli

Visitor-suunnittelumalli erottaa operaatiot olioista. Operaatio "vierailee" eri tyyppisissä olioissa ja kutsuu tyypin mukaista metodia. Käytä sitä operaatioihin, joiden on vaihdeltava tyypin mukaan ilman tyyppien muokkaamista.

local function printInfo(obj)
  local mt = getmetatable(obj)
  if mt == Circle then
    print(string.format("Circle r=%.1f area=%.2f",obj.r,obj:area()))
  elseif mt == Square then
    print(string.format("Square s=%.1f area=%.2f",obj.s,obj:area()))
  end
end
for _,s in ipairs({Circle.new(3),Square.new(4)}) do
  printInfo(s)
end

Rajapintataulukko

Määritä rajapinta metodinimien taulukkona. Tarkista ennen polymorfista käyttöä, että olio toteuttaa kaikki vaaditut metodit.

local function implements(obj, interface)
  for _, method in ipairs(interface) do
    if type(obj[method]) ~= "function" then
      return false, "missing: "..method
    end
  end
  return true
end

local Drawable = {"draw","resize","move"}

local Widget = {
  draw=function(self) print("draw "..self.type) end,
  resize=function(self,w,h) self.w,self.h=w,h end,
  move=function(self,x,y) self.x,self.y=x,y end,
  type="button"
}

local ok,err = implements(Widget,Drawable)
print(ok,err)  -- true nil

Metodin olemassaolon tarkistus

Tarkista ennen valinnaisen metodin polymorfista kutsumista, että oliolla on kyseinen metodi. Näin voidaan tarjota valinnaisia ominaisuuksia ilman yhteistä kantaluokkaa.

local function maybeUpdate(obj, dt)
  if type(obj.update)=="function" then
    obj:update(dt)
  end
end

local objects={
  {x=0,y=0,update=function(self,dt) self.x=self.x+10*dt end},
  {x=0,y=0},  -- no update
  {x=0,y=0,update=function(self,dt) self.y=self.y+5*dt end},
}

for _,o in ipairs(objects) do maybeUpdate(o,1) end
for _,o in ipairs(objects) do print(o.x,o.y) end

Tyyppiin perustuva hajautustaulukko

Käytä if/elseif-ketjujen sijaan hajautustaulukkoa: yhdistä tyyppinimet käsittelijäfunktioihin. Tämä on taulukko-ohjatun polymorfismin malli.

local handlers={
  number  = function(v) return "num:"..v end,
  string  = function(v) return "str:\"".."..v.."\"" end,
  boolean = function(v) return "bool:"..(v and "T" or "F") end,
  table   = function(v) return "tbl:#"..tostring(#v) end,
}

local function describe(v)
  local h=handlers[type(v)]
  return h and h(v) or "unknown"
end

print(describe(42))
print(describe("hello"))
print(describe({1,2,3}))

Kovariantit palautusarvot

Lapsiluokan metodit voivat palauttaa isäntäluokan metodeja tarkempia tyyppejä (kovariantti palautusarvo). Lua on dynaamisesti tyypitetty, joten tämä on luontevaa — lapsiluokan factory palauttaa lapsiolion, ei vain isäntäoliota.

local Base={} Base.__index=Base
function Base.new() return setmetatable({type="base"},Base) end
function Base:copy() return Base.new() end

local Child={} Child.__index=Child
setmetatable(Child,{__index=Base})
function Child.new(extra)
  local self=setmetatable({type="child"},Child)
  self.extra=extra; return self
end
function Child:copy() return Child.new(self.extra) end  -- covariant

local c=Child.new("hello")
local c2=c:copy()
print(c2.extra,getmetatable(c2)==Child)  -- hello true

Polymorfismin yhteenveto

Lua-polymorfismi käytännössä: sama metodinimi, erilainen toiminta tyypin mukaan. Se voidaan toteuttaa metatauluihin perustuvalla olio-ohjelmoinnilla, ankkatyypityksen tarkistuksilla tai hajautustaulukoilla. Muodollista rajapintajärjestelmää ei tarvita — johdonmukaiset metodien nimeämiskäytännöt riittävät.

-- Three ways to achieve polymorphism:
-- 1. Metatable inheritance + method override
-- 2. Duck typing: just call it if it exists
-- 3. Dispatch table: map type -> handler

local function area(shape)
  -- Duck typing approach:
  assert(type(shape.area)=="function","needs area()")
  return shape:area()
end

print(area({area=function() return 16 end}))  -- 16

Polymorfismi-kysymys

Miten polymorfismi toimii Luassa?

Kertaus: ylikirjoittaminen ja polymorfismi

Ylikirjoita isäntäluokan metodit määrittelemällä ne uudelleen lapsiluokassa. Lua ratkaisee kutsut alhaalta ylöspäin __index-ketjun kautta, mikä mahdollistaa luonnollisen ajonaikaisen polymorfisen hajautuksen.

Aloita maksutta

Opi Lua tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
40
Oppitunnit
159

Usein kysytyt kysymykset

Onko oppitunti ”Ylikirjoittaminen ja polymorfismi” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Lua Academy-oppimispolun 3 oppituntia, myös oppitunnin “Ylikirjoittaminen ja polymorfismi”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Lua Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Ylikirjoittaminen ja polymorfismi”?

Ylikirjoittakaa emoluokan metodeja ja toteuttakaa ajonaikainen polymorfinen metodikutsu. Harjoittelet Lua Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Lua Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Lua Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.

Kuinka kauan ”Ylikirjoittaminen ja polymorfismi”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Lua Academy-oppitunnilla?

Kyllä. Jokainen Lua Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Yksiperintä __index-ketjutuksella
  2. Emoluokan metodien kutsuminen superilla
  3. Mixin-rakenteet
  4. Ylikirjoittaminen ja polymorfismi
← Takaisin: Lua Academy