08 — Traits e Classes Abstratas
Meta do capítulo: expressar contratos e polimorfismo. Referência:
../language-reference/classes.md§4–5. Exemplo:../../examples/abstract_classes.aura.
Trait — um contrato puro
Um trait declara métodos sem estado e sem construtor. Um método sem corpo já é abstrato; não escreva abstract dentro de um trait (trait T { abstract def f() } é erro de parse).
trait Drawable {
public def draw() -> void
public def bounds() -> float
}
class Circle extends Drawable {
public let radius: float = 1.0
public def draw() -> void { print("circle") }
public def bounds() -> float { return 3.14159 * self.radius * self.radius }
}
def main() {
let c = Circle(2.0)
c.draw() // circle
print(c.bounds()) // 12.56636
}
Uma classe concreta deve implementar todo método sem corpo que herda, ou é E309. Traits estendem outros traits com extends.
Use um trait quando a base não precisa de estado.
Classe abstrata — estado compartilhado + métodos adiados
Uma abstract class é uma classe real: campos, métodos concretos e um construtor. Ela não pode ser instanciada (E316) e pode adiar métodos com abstract def.
abstract class Shape {
public let name: str = "shape"
public def describe() -> str {
return "a " + self.name
}
public abstract def area() -> float // no body
}
class Square extends Shape {
public let side: float = 2.0
public def area() -> float { return self.side * self.side }
}
def main() {
let s = Square(3.0)
print(s.area()) // 9.0
print(s.describe()) // a shape
// let bad = Shape() // E316: an abstract class cannot be instantiated
}
Regras:
abstracté um modificador antes declassou antes dedef.- Um
abstract defé sem corpo;abstract def f() { ... }eabstract def f() = exprsão erros de sintaxe. - Um
abstract defpode aparecer apenas em umaabstract class. - Uma classe abstrata pode ela mesma adiar; a obrigação passa para a subclasse concreta.
- Uma classe concreta que não implementa um método abstrato herdado é
E309.
Use uma classe abstrata quando a base precisa de campos ou comportamento concreto compartilhado.
Polimorfismo através da base
Um parâmetro de função anotado com o tipo base aceita qualquer subtipo:
abstract class Shape {
public let name: str = "shape"
public def describe() -> str { return "a " + self.name }
public abstract def area() -> float
}
class Square extends Shape {
public let side: float = 2.0
public def new(side: float) {
self.name = "square"
self.side = side
}
public def area() -> float { return self.side * self.side }
}
class Circle extends Shape {
public let radius: float = 1.0
public def new(radius: float) {
self.name = "circle"
self.radius = radius
}
public def area() -> float { return 3.14159 * self.radius * self.radius }
}
def print_area(shape: Shape) {
print(f"{shape.describe()}: {shape.area()}")
}
def main() {
print_area(Square(3.0))
print_area(Circle(2.0))
}
a square: 9.0
a circle: 12.56636
O tipo do parâmetro é a base abstrata; a chamada despacha para o area da subclasse. Este é o despacho de método comum do CPython após o emit.
super
class Base {
public def name() -> str { return "base" }
}
class Derived extends Base {
public def name() -> str { return "derived of " + super.name() }
}
super.method() chama o método do pai; super(args) chama o construtor do pai. Chamar super.m() onde m é um método abstrato sem implementação é E321.
Escolhendo
| Necessidade | Use |
|---|---|
| Um contrato sem estado | trait |
| Campos compartilhados ou métodos concretos, sem instâncias diretas | abstract class |
| Implementação completa, instanciável | class |
O que você aprendeu
traité um contrato sem estado; métodos sem corpo já são abstratos.abstract classtem estado e pode adiar métodos comabstract def.- Nem um trait nem uma classe abstrata podem ser instanciados.
- Polimorfismo através do tipo base;
superpara o pai.