0Pricing
Lua Academy · Lezione

Override e polimorfismo

Esegua l'override dei metodi ereditati e ottenga il dispatch polimorfico a runtime.

Override e polimorfismo è una lezione Lua Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Lua Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Lua Academy include 4 lezioni in totale.

Sovrascrittura dei metodi

Una classe figlia sovrascrive un metodo della classe padre definendo un metodo con lo stesso nome. Quando viene chiamato su un'istanza figlia, viene eseguita la versione della figlia. La versione del padre resta accessibile tramite 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()

Dispatch polimorfico

Il polimorfismo consente a oggetti di tipo diverso di rispondere in modo diverso allo stesso messaggio. Una funzione che accetta qualsiasi "forma" e chiama :area() funziona con ogni tipo di forma: ciascuno risponde con la propria implementazione.

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))

Duck typing

Lua utilizza il duck typing: se un oggetto possiede il metodo, può essere utilizzato. Non è necessaria alcuna dichiarazione formale dell'interfaccia. Una funzione che chiama :speak() funziona con qualsiasi oggetto che disponga di un metodo speak, indipendentemente dalla classe.

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)

Polimorfismo degli operatori

I metametodi come __add abilitano il polimorfismo degli operatori. Tipi diversi possono rispondere a + con una propria semantica, purché definiscano __add.

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 pattern

Il visitor pattern separa le operazioni dagli oggetti. Un'operazione "visita" oggetti di tipo diverso e chiama il metodo appropriato in base al tipo. Lo utilizzi per operazioni che devono variare a seconda del tipo senza modificare i tipi stessi.

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

Tabella dell'interfaccia

Definisca un'interfaccia come una tabella di nomi di metodi. Verifichi che un oggetto implementi tutti i metodi richiesti prima di utilizzarlo in modo polimorfico.

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

Verifica dell'esistenza di un metodo

Prima di chiamare polimorficamente un metodo opzionale, verifichi che l'oggetto lo possieda. In questo modo è possibile offrire capacità opzionali senza richiedere una classe base comune.

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

Tabella di dispatch basata sul tipo

Anziché usare catene di if/elseif, utilizzi una tabella di dispatch: associ i nomi dei tipi alle funzioni gestore. Questo è lo schema del polimorfismo guidato da tabella.

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}))

Valori restituiti covarianti

I metodi delle classi figlie possono restituire tipi più specifici rispetto ai metodi delle classi padre (ritorno covariante). Lua è tipizzato dinamicamente, quindi questo comportamento è naturale: il factory della classe figlia restituisce un'istanza figlia, non soltanto una del padre.

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

Riepilogo del polimorfismo

Il polimorfismo in Lua, nella pratica, consiste nello stesso nome di metodo con un comportamento diverso per ogni tipo. Si ottiene tramite OOP basato su metatabelle, verifiche di duck typing o tabelle di dispatch. Non è necessario un sistema formale di interfacce: bastano convenzioni coerenti per i nomi dei metodi.

-- 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

Domanda sul polimorfismo

Come funziona il polimorfismo in Lua?

Riepilogo: sovrascrittura e polimorfismo

Sovrascriva i metodi della classe padre ridefinendoli nella classe figlia. Lua risolve le chiamate dal basso verso l'alto attraverso la catena __index, ottenendo naturalmente il dispatch polimorfico durante l'esecuzione.

Domande Frequenti

La lezione «Override e polimorfismo» è gratuita?

Sì — il testo completo di «Override e polimorfismo» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Lua Academy, passa a CoddyKit PRO. Il corso Lua Academy include 4 lezioni in totale.

Cosa imparerò in «Override e polimorfismo»?

Esegua l'override dei metodi ereditati e ottenga il dispatch polimorfico a runtime. Eserciti Lua Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Lua Academy?

Non è richiesta alcuna esperienza precedente. Lua Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Override e polimorfismo»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Lua Academy?

Sì. Ogni lezione Lua Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Ereditarietà singola con concatenamento di __index
  2. Chiamata dei metodi del padre con super
  3. Pattern mixin
  4. Override e polimorfismo
← Torna a Lua Academy