virtual, abstract y override
Cómo decide el padre qué pueden cambiar sus hijas: virtual permite, abstract obliga y override es la hija diciendo «esto lo hago a mi manera».
✓ En esta sub-lección
- 01Permitir que una hija cambie un método con virtual y hacerlo con override.
- 02Declarar una clase abstract con métodos que cada hija está obligada a implementar.
- 03Ampliar el comportamiento del padre en lugar de sustituirlo, con super.método().
La nutria: ¿Te acuerdas? · Repaso de la lección 7
¿Qué palabra, en la cabecera del padre, permite que otras clases hereden de él?
En la lección anterior la hija heredaba los métodos del padre tal cual. Pero a menudo cada hija necesita hacer lo mismo de forma distinta: todos los descuentos «se aplican», pero uno resta un porcentaje y otro un importe fijo. Apex te deja decidir, método a método, qué se puede cambiar y qué no.
La nutria: El paralelo de Admin
virtual + override: «puedes cambiarlo»
Igual que las clases, los métodos de Apex son cerrados por defecto: una hija no los puede cambiar. Si el padre marca un método como virtual, ofrece una versión por defecto que las hijas pueden sustituir. La hija lo hace escribiendo el mismo método con override. Sin virtual en el padre o sin override en la hija, no compila.
public virtual class Notification {
public String subject;
public virtual String channel() {
return 'Correo'; // versión por defecto
}
}
public class SmsNotification extends Notification {
public override String channel() {
return 'SMS'; // la hija lo hace a su manera
}
}abstract: «tienes que hacerlo tú»
A veces el padre no tiene ninguna versión sensata que ofrecer: ¿cómo se aplica «un descuento» en abstracto? Entonces la clase se declara abstract y el método también, sin cuerpo, terminado en punto y coma. Una clase abstracta no se puede instanciar (new Discount() no compila) y cada hija concreta está obligada a implementar sus métodos abstractos con override.
public abstract class Discount {
public String label;
public Discount(String label) {
this.label = label;
}
public abstract Decimal apply(Decimal amount); // sin cuerpo: cada hija decide
public virtual String describe() {
return label;
}
}
public class PercentDiscount extends Discount {
private Decimal percent;
public PercentDiscount(String label, Decimal percent) {
super(label);
this.percent = percent;
}
public override Decimal apply(Decimal amount) {
return amount * (1 - percent / 100);
}
public override String describe() {
return super.describe() + ' (' + percent + ' %)'; // amplía, no sustituye
}
}Elige qué descuento fabricar.
| Sin marca | virtual | abstract | |
|---|---|---|---|
| Clase: ¿se puede heredar? | No | Sí | Sí (y hay que hacerlo) |
| Clase: ¿new directamente? | Sí | Sí | No |
| Método: ¿tiene cuerpo? | Sí | Sí, por defecto | No |
| Método: ¿la hija puede o debe cambiarlo? | No puede | Puede (override) | Debe (override) |
¿virtual, abstract o interfaz? Cómo decidir
Las tres herramientas responden a tres preguntas distintas. ¿Hay una forma por defecto que casi todas las hijas usarían? Entonces virtual: el padre la ofrece y quien quiera la cambia. ¿Hay código común que compartir, pero una parte que cada hija DEBE resolver y no tiene versión sensata en abstracto? Entonces clase abstract con un método abstract. ¿No hay nada en común salvo «saber hacer algo»? Entonces una interfaz, que es la lección 10. Elegir bien importa: una clase abstract obliga a heredar de ella, y en Apex solo se hereda de una clase.
La nutria: El layout obligatorio y el valor por defecto que se puede cambiar