Estendendo o Construtor
Parte da seção Object Oriented Programming do Journey de Lua da Coddy — lição 33 de 70.
Quando uma classe filha herda de uma classe pai, você geralmente deseja que o construtor da filha inicialize tudo o que o pai inicializaria — além de quaisquer campos adicionais específicos da filha. Em vez de duplicar código, você pode chamar o construtor do pai diretamente.
A técnica envolve o uso do nome da classe pai para invocar seu método :new(), passando a classe pai como o primeiro argumento. Você pode fazer isso com a sintaxe de ponto: Vehicle.new(Vehicle, speed), ou de forma equivalente com a sintaxe de dois pontos: Vehicle:new(speed) — ambas são válidas e produzem o mesmo resultado. Isso cria o objeto base com toda a inicialização do pai, que você então customiza para o filho.
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle:new(speed)
local obj = {speed = speed}
setmetatable(obj, Vehicle)
return obj
end
-- Car herda de Vehicle
local Car = {}
Car.__index = Car
setmetatable(Car, {__index = Vehicle})
function Car:new(speed, brand)
local obj = Vehicle.new(Vehicle, speed) -- Chama o construtor pai
obj.brand = brand -- Adiciona campo específico do filho
setmetatable(obj, Car) -- Re-vincula ao Car
return obj
endObserve as etapas principais no construtor filho: primeiro, chamamos Vehicle.new(Vehicle, speed) (ou equivalentemente Vehicle:new(speed)) para obter um objeto com o campo speed já definido. Em seguida, adicionamos o campo brand específico do filho. Finalmente, alteramos a metatabela de Vehicle para Car para que a instância procure os métodos na classe filha primeiro.
local myCar = Car:new(120, "Toyota")
print(myCar.speed) -- Saída: 120
print(myCar.brand) -- Saída: ToyotaEste padrão mantém seu código DRY — a lógica de inicialização compartilhada reside no pai, enquanto cada filho lida apenas com seus atributos únicos.
Desafio
FácilVamos construir uma hierarquia de criaturas onde uma classe filha estende o construtor de sua classe pai! Você criará uma classe pai Animal e uma classe filha Bird, onde o construtor de Bird chama o construtor de Animal para lidar com a inicialização compartilhada antes de adicionar seu próprio atributo exclusivo.
Você organizará seu código em três arquivos:
Animal.lua: Defina sua classe pai com um construtor:new(name)que armazena o nome do animal. Inclua um método:describe()que imprime o nome do animal. Configure o padrão de classe padrão com__indexe retorne a classe.Bird.lua: Defina sua classe filha que herda de Animal. O construtor:new(name, wingspan)de Bird deve chamar o construtor pai usandoAnimal.new(Animal, name)para inicializar o nome, depois adicionar o campowingspanao objeto e, finalmente, vincular novamente a metatabela a Bird. Inclua um método:info()que imprime o nome e a envergadura em um formato específico.main.lua: Importe seu módulo Bird, leia duas entradas (nome e envergadura), crie uma instância de Bird e chame ambos os métodos para demonstrar que a funcionalidade herdada e a específica da classe filha funcionam juntas.
Você receberá duas entradas:
- O nome do pássaro
- A envergadura do pássaro (um número)
No seu arquivo principal, crie um Bird com o nome e a envergadura fornecidos. Em seguida, chame :describe() seguido de :info() em linhas separadas.
O método :describe() (herdado de Animal) deve imprimir:
Animal: {name}O método :info() (definido em Bird) deve imprimir:
{name} has a wingspan of {wingspan}Por exemplo, se as entradas forem Eagle e 200, a saída deve ser:
Animal: Eagle
Eagle has a wingspan of 200A chave aqui é que o construtor de Bird não duplica a inicialização do nome — ele delega esse trabalho ao construtor de Animal e, em seguida, adiciona apenas o que é exclusivo dos pássaros. Isso mantém seu código DRY enquanto constrói uma cadeia de herança adequada!
Folha de consulta
Para chamar o construtor de uma classe pai a partir de uma classe filha, use o nome da classe pai com a sintaxe de ponto, passando a própria classe pai como o primeiro argumento:
local obj = ParentClass.new(ParentClass, parentArgs)Após chamar o construtor pai, adicione campos específicos da classe filha e vincule novamente a metatabela à classe filha:
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle:new(speed)
local obj = {speed = speed}
setmetatable(obj, Vehicle)
return obj
end
local Car = {}
Car.__index = Car
setmetatable(Car, {__index = Vehicle})
function Car:new(speed, brand)
local obj = Vehicle.new(Vehicle, speed) -- Chama o construtor pai
obj.brand = brand -- Adiciona campo específico da filha
setmetatable(obj, Car) -- Vincula novamente ao Car
return obj
end
local myCar = Car:new(120, "Toyota")
print(myCar.speed) -- Saída: 120
print(myCar.brand) -- Saída: ToyotaEste padrão mantém a lógica de inicialização DRY — a configuração compartilhada reside no pai, enquanto os filhos lidam apenas com seus atributos exclusivos.
Experimente você mesmo
-- main.lua: Ponto de entrada
local Bird = require('Bird')
-- Ler entradas
local name = io.read()
local wingspan = tonumber(io.read())
-- TODO: Criar uma instância de Bird com o nome e envergadura fornecidos
-- TODO: Chamar :describe() no pássaro (herdado de Animal)
-- TODO: Chamar :info() no pássaro (definido em Bird)
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Object Oriented Programming
1O Conceito 'Self'
Tabelas com Funções'self' ExplícitoA Sintaxe de Dois-pontosPonto vs Dois-pontosRecapitulação - Movendo o Ponto4Projeto: Banco Digital
Configuração do ProjetoMétodo de Depósito7Polimorfismo e Sobrescrita
Sobrescrita de MétodosChamando Métodos da Classe PaiDuck TypingInterface ComumVerificando o TipoRecapitulação - Papéis de Funcionários10Projeto: Gerenciador de Formas
Configuração do ProjetoClasse Retângulo2Padrão de Protótipo de Classe
O Conceito de ProtótipoVinculando com __indexO Construtor :new()Inicializando AtributosInstâncias IndependentesRecapitulação - Fábrica de Carros5Sobrecarga de Operadores em POO
Somando ObjetosSubtraindo ObjetosConcatenando ObjetosComparando Objetos (<, >)Recapitulação - Matemática de Carteira8Encapsulamento
Convenções de NomenclaturaClosures para PrivacidadeAcesso via ClosuresTabelas Somente LeituraLógica de ValidaçãoRecapitulação - Cofre Seguro11Padrões de Projeto (Lite)
Funções FactoryTabela SingletonPadrão IteratorObserver (Listener)Recapitulação - Logger Factory3Estado e Comportamento do Objeto
Variáveis de InstânciaMétodos GetterMétodos SetterPropriedades CalculadasFormatação de StringsVerificações de IgualdadeRecapitulação - Nota do Aluno6Fundamentos de Herança
Configurando a HerançaHerdando MétodosEstendendo o ConstrutorAdicionando Métodos FilhosCompartilhado vs ÚnicoRecapitulação - Hierarquia de Formas