Buscar..


Introducción

La herencia es una característica básica orientada a objetos en la que una clase adquiere y se extiende sobre las propiedades de otra clase, utilizando la palabra clave extends . Para Interfaces y los implements palabras clave, vea interfaces .

Sintaxis

  • clase ClassB extiende ClassA {...}
  • clase ClassB implementa InterfaceA {...}
  • interface InterfaceB extiende InterfaceA {...}
  • clase ClassB extiende ClassA implementa InterfaceC, InterfaceD {...}
  • clase abstracta AbstractClassB extiende ClassA {...}
  • clase abstracta AbstractClassB extiende AbstractClassA {...}
  • clase abstracta AbstractClassB extiende ClassA implementa InterfaceC, InterfaceD {...}

Observaciones

La herencia a menudo se combina con los genéricos de modo que la clase base tenga uno o más parámetros de tipo. Consulte Creación de una clase genérica .

Clases abstractas

Una clase abstracta es una clase marcada con la palabra clave abstract . Contrariamente a la clase no abstracta, puede contener métodos abstractos sin implementación. Sin embargo, es válido crear una clase abstracta sin métodos abstractos.

Una clase abstracta no puede ser instanciada. Puede ser subclasificado (extendido) siempre que la subclase sea abstracta, o implemente todos los métodos marcados como abstractos por súper clases.

Un ejemplo de una clase abstracta:

public abstract class Component {
    private int x, y;
    
    public setPosition(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public abstract void render();
}

La clase debe estar marcada como abstracta, cuando tiene al menos un método abstracto. Un método abstracto es un método que no tiene implementación. Se pueden declarar otros métodos dentro de una clase abstracta que tiene implementación para proporcionar código común para cualquier subclase.

Intentar crear una instancia de esta clase proporcionará un error de compilación:

//error: Component is abstract; cannot be instantiated   
Component myComponent = new Component();

Sin embargo, una clase que amplía el Component y proporciona una implementación para todos sus métodos abstractos y se puede instanciar.

public class Button extends Component {

    @Override
    public void render() {
        //render a button
    }
}

public class TextBox extends Component {

    @Override
    public void render() {
        //render a textbox
    }
}

Las instancias de clases heredadas también se pueden convertir como la clase principal (herencia normal) y proporcionan un efecto polimórfico cuando se llama al método abstracto.

Component myButton = new Button();
Component myTextBox = new TextBox();

myButton.render(); //renders a button
myTextBox.render(); //renders a text box

Clases abstractas vs interfaces

Tanto las clases abstractas como las interfaces proporcionan una forma de definir firmas de métodos al tiempo que requieren que la clase extendida / implementada proporcione la implementación.

Hay dos diferencias clave entre las clases abstractas y las interfaces:

  • Una clase solo puede extender una sola clase, pero puede implementar muchas interfaces.
  • Una clase abstracta puede contener campos de instancia (no static ), pero las interfaces solo pueden contener campos static .
Java SE 8

Los métodos declarados en las interfaces no pueden contener implementaciones, por lo que se usaron clases abstractas cuando fue útil proporcionar métodos adicionales que las implementaciones llamaron métodos abstractos.

Java SE 8

Java 8 permite que las interfaces contengan métodos predeterminados , generalmente implementados usando los otros métodos de la interfaz , haciendo que las interfaces y las clases abstractas sean igualmente poderosas en este sentido.

Subclases anónimas de clases abstractas

Como conveniencia, java permite crear instancias anónimas de subclases de clases abstractas, que proporcionan implementaciones para los métodos abstractos al crear el nuevo objeto. Usando el ejemplo anterior, esto podría verse así:

Component myAnonymousComponent = new Component() {
    @Override
    public void render() {
        // render a quick 1-time use component
    }
}

Herencia estática

El método estático se puede heredar de forma similar a los métodos normales, sin embargo, a diferencia de los métodos normales, es imposible crear métodos " abstractos " para forzar la anulación del método estático. Escribir un método con la misma firma que un método estático en una súper clase parece ser una forma de anulación, pero en realidad esto simplemente crea una nueva función que oculta a la otra.

public class BaseClass {
    
    public static int num = 5;

    public static void sayHello() {
        System.out.println("Hello");
    }

    public static void main(String[] args) {
        BaseClass.sayHello();
        System.out.println("BaseClass's num: " + BaseClass.num);
            
        SubClass.sayHello();
        //This will be different than the above statement's output, since it runs
        //A different method
        SubClass.sayHello(true);
        
        StaticOverride.sayHello();
        System.out.println("StaticOverride's num: " + StaticOverride.num);
    }
}

public  class SubClass extends BaseClass {
    
    //Inherits the sayHello function, but does not override it   
    public static void sayHello(boolean test) {
        System.out.println("Hey");
    }
}

public static class StaticOverride extends BaseClass {

    //Hides the num field from BaseClass
    //You can even change the type, since this doesn't affect the signature
    public static String num = "test";
        
    //Cannot use @Override annotation, since this is static
    //This overrides the sayHello method from BaseClass
    public static void sayHello() {
        System.out.println("Static says Hi");
    }

}

La ejecución de cualquiera de estas clases produce el resultado:

Hello
BaseClass's num: 5
Hello
Hey
Static says Hi
StaticOverride's num: test

Tenga en cuenta que, a diferencia de la herencia normal, en los métodos de herencia estática no están ocultos. Siempre puede llamar al método base sayHello usando BaseClass.sayHello() . Pero las clases heredan métodos estáticos si no se encuentran métodos con la misma firma en la subclase. Si las firmas de dos métodos varían, ambos métodos pueden ejecutarse desde la subclase, incluso si el nombre es el mismo.

Los campos estáticos se ocultan entre sí de forma similar.

Usando 'final' para restringir la herencia y anular

Clases finales

Cuando se usa en una declaración de class , el modificador final evita que se declaren otras clases que extend la clase. Una clase final es una clase "hoja" en la jerarquía de clases de herencia.

// This declares a final class
final class MyFinalClass {
    /* some code */
}

// Compilation error: cannot inherit from final MyFinalClass
class MySubClass extends MyFinalClass {
    /* more code */
}

Casos de uso para las clases finales.

Las clases finales se pueden combinar con un constructor private para controlar o evitar la creación de instancias de una clase. Esto se puede usar para crear una llamada "clase de utilidad" que solo define miembros estáticos; Es decir, constantes y métodos estáticos.

public final class UtilityClass {

    // Private constructor to replace the default visible constructor
    private UtilityClass() {}

    // Static members can still be used as usual
    public static int doSomethingCool() {
        return 123;
    }

}

Las clases inmutables también deben ser declaradas como final . (Una clase inmutable es aquella cuyas instancias no se pueden cambiar después de haber sido creadas; consulte el tema Imutables de objetos ). Al hacer esto, hace imposible crear una subclase mutable de una clase inmutable. Eso violaría el principio de sustitución de Liskov, que exige que un subtipo obedezca el "contrato de comportamiento" de sus supertipos.

Desde una perspectiva práctica, declarar que una clase inmutable es final hace que sea más fácil razonar sobre el comportamiento del programa. También aborda los problemas de seguridad en el escenario donde el código no confiable se ejecuta en un entorno limitado de seguridad. (Por ejemplo, como String se declara como final , una clase confiable no tiene que preocuparse de que pueda ser engañada para que acepte una subclase mutable, que la persona que llama no de confianza podría cambiar de manera subrepticia).

Una desventaja de las clases final es que no funcionan con algunos marcos burlones como Mockito. Actualización: la versión 2 de Mockito ahora admite la burla de las clases finales.

Métodos finales

El modificador final también se puede aplicar a los métodos para evitar que se sobrescriban en las subclases:

public class MyClassWithFinalMethod {

    public final void someMethod() {
    }
}

public class MySubClass extends MyClassWithFinalMethod {

    @Override
    public void someMethod() { // Compiler error (overridden method is final)
    }
}

Los métodos finales se usan normalmente cuando se quiere restringir lo que una subclase puede cambiar en una clase sin prohibir por completo las subclases.


El modificador final también se puede aplicar a las variables, pero el significado de final para las variables no está relacionado con la herencia.

El principio de sustitución de Liskov

Posibilidad de sustitución es un principio en la programación orientada a objetos introducidos por Barbara Liskov en un discurso de apertura 1987 declarando que, si la clase B es una subclase de la clase A , a continuación, siempre que sea A se espera, B se puede utilizar en su lugar:

class A {...}
class B extends A {...}

public void method(A obj) {...}

A a = new B(); // Assignment OK
method(new B()); // Passing as parameter OK

Esto también se aplica cuando el tipo es una interfaz, donde no se necesita ninguna relación jerárquica entre los objetos:

interface Foo {
    void bar();
}

class A implements Foo {
    void bar() {...}
}

class B implements Foo {
    void bar() {...}
}

List<Foo> foos = new ArrayList<>();
foos.add(new A()); // OK
foos.add(new B()); // OK

Ahora la lista contiene objetos que no son de la misma jerarquía de clases.

Herencia

Con el uso de la extends palabra clave entre las clases, todas las propiedades de la superclase (también conocido como la clase padre o Clase Base) están presentes en la subclase (también conocida como la clase hija o clase derivada)

public class BaseClass {

    public void baseMethod(){
        System.out.println("Doing base class stuff");
    }
}

public class SubClass extends BaseClass {

}

Las instancias de SubClass han heredado el método baseMethod() :

SubClass s = new SubClass();
s.baseMethod();  //Valid, prints "Doing base class stuff"

Se puede agregar contenido adicional a una subclase. Si lo hace, permite la funcionalidad adicional en la subclase sin ningún cambio en la clase base o cualquier otra subclase de la misma clase base:

public class Subclass2 extends BaseClass {

    public void anotherMethod() {
        System.out.println("Doing subclass2 stuff");
    }
}

Subclass2 s2 = new Subclass2();
s2.baseMethod(); //Still valid , prints "Doing base class stuff"
s2.anotherMethod(); //Also valid, prints "Doing subclass2 stuff" 

Los campos también se heredan:

public class BaseClassWithField {

    public int x;

}

public class SubClassWithField extends BaseClassWithField {

    public SubClassWithField(int x) {
        this.x = x; //Can access fields
    }
}

private campos y métodos private aún existen dentro de la subclase, pero no son accesibles:

public class BaseClassWithPrivateField {

    private int x = 5;

    public int getX() {
        return x;
    }
}

public class SubClassInheritsPrivateField extends BaseClassWithPrivateField {

    public void printX() {
        System.out.println(x); //Illegal, can't access private field x
        System.out.println(getX()); //Legal, prints 5
    }
}

SubClassInheritsPrivateField s = new SubClassInheritsPrivateField();
int x = s.getX(); //x will have a value of 5.

En Java, cada clase puede extenderse a lo sumo a otra clase.

public class A{}
public class B{}
public class ExtendsTwoClasses extends A, B {} //Illegal

Esto se conoce como herencia múltiple, y si bien es legal en algunos idiomas, Java no lo permite con clases.

Como resultado de esto, cada clase tiene una cadena de clases ancestrales no ramificadas que conducen a Object , de la que descienden todas las clases.

Herencia y métodos estáticos

En Java, las clases padre e hijo pueden tener métodos estáticos con el mismo nombre. Pero en tales casos, la implementación del método estático en hijo oculta la implementación de la clase padre, no se trata de una anulación del método. Por ejemplo:

class StaticMethodTest {

  // static method and inheritance
  public static void main(String[] args) {
    Parent p = new Child();
    p.staticMethod(); // prints Inside Parent
    ((Child) p).staticMethod(); // prints Inside Child
  }

  static class Parent {
    public static void staticMethod() {
      System.out.println("Inside Parent");
    }
  }

  static class Child extends Parent {
    public static void staticMethod() {
      System.out.println("Inside Child");
    }
  }
}

Los métodos estáticos se unen a una clase y no a una instancia y este enlace de método ocurre en tiempo de compilación. Dado que en la primera llamada a staticMethod() , matriz de referencia de clase p se utilizó, Parent versión 's de staticMethod() se invoca. En el segundo caso, hicimos p en Child clase Child , se ejecutó staticMethod() Child .

Sombreado variable

Las variables están SOMBRADAS y los métodos son ANULADOS. La variable que se utilizará depende de la clase de la que se declara la variable. El método que se utilizará depende de la clase real del objeto al que hace referencia la variable.

class Car {
    public int gearRatio = 8;

    public String accelerate() {
        return "Accelerate : Car";
    }
}

class SportsCar extends Car {
    public int gearRatio = 9;

    public String accelerate() {
        return "Accelerate : SportsCar";
    }

    public void test() {

    }


    public static void main(String[] args) {

        Car car = new SportsCar();
        System.out.println(car.gearRatio + "  " + car.accelerate());
        // will print out 8  Accelerate : SportsCar
    }
}

Reducción y ampliación de referencias de objetos.

Convertir una instancia de una clase base en una subclase como en: b = (B) a; se denomina estrechamiento (cuando intenta restringir el objeto de clase base a un objeto de clase más específico) y necesita una conversión de tipos explícita.

Convertir una instancia de una subclase en una clase base como en: A a = b; Se llama ampliación y no necesita una conversión de tipo.

Para ilustrar, considere las siguientes declaraciones de clase y el código de prueba:

class Vehicle {
}

class Car extends Vehicle {
}

class Truck extends Vehicle {
}

class MotorCycle extends Vehicle {
}

class Test {

    public static void main(String[] args) {
    
        Vehicle vehicle = new Car();
        Car car = new Car();        
    
        vehicle = car; // is valid, no cast needed

        Car c = vehicle // not valid
        Car c = (Car) vehicle; //valid
    }
}

La declaración Vehicle vehicle = new Car(); Es una declaración válida de Java. Cada instancia de Car es también un Vehicle . Por lo tanto, la asignación es legal sin la necesidad de una conversión de tipos explícita.

Por otro lado, Car c = vehicle; no es válido. El tipo estático de la variable de vehicle es Vehicle que significa que podría referirse a una instancia de Car , Camión , Motocicleta , or any other current or future subclass of Vehículo . (Or indeed, an instance of Vehículo en itself, since we did not declare it as an class.) The assignment cannot be allowed, since that might lead to abstracta class.) The assignment cannot be allowed, since that might lead to automóvil se referring to a instancia de referring to a Camión.

Para evitar esta situación, necesitamos agregar una conversión de tipos explícita:

Car c = (Car) vehicle;

La conversión de tipos le dice al compilador que esperamos que el valor del vehicle sea ​​un Car o una subclase de Car . Si es necesario, el compilador insertará el código para realizar una verificación de tipo en tiempo de ejecución. Si la comprobación falla, se emitirá una ClassCastException cuando se ejecute el código.

Tenga en cuenta que no todos los tipos de conversión son válidos. Por ejemplo:

String s = (String) vehicle;  // not valid

El compilador de Java sabe que una instancia que es compatible con Type con Vehicle no puede ser compatible con String . La conversión de tipos nunca podría tener éxito, y JLS exige que esto dé un error de compilación.

Programación a una interfaz

La idea detrás de la programación de una interfaz es basar el código principalmente en interfaces y solo usar clases concretas en el momento de la creación de instancias. En este contexto, el buen código que se ocupa, por ejemplo, de las colecciones de Java se verá así:

public <T> Set<T> toSet(Collection<T> collection) {
  return Sets.newHashSet(collection);
}

mientras que el código malo podría tener este aspecto:

public <T> HashSet<T> toSet(ArrayList<T> collection) {
  return Sets.newHashSet(collection);
}

No solo los primeros se pueden aplicar a una variedad más amplia de argumentos, sino que sus resultados serán más compatibles con el código proporcionado por otros desarrolladores que generalmente se adhieren al concepto de programación para una interfaz. Sin embargo, las razones más importantes para usar el primero son:

  • la mayoría de las veces, el contexto en el que se usa el resultado no necesita ni debería necesitar tantos detalles como lo proporciona la implementación concreta;
  • adherirse a una interfaz obliga a un código más limpio y menos hacks, como otro método público que se agrega a una clase que atiende a un escenario específico;
  • el código es más comprobable ya que las interfaces son fácilmente simulables;
  • finalmente, el concepto ayuda incluso si solo se espera una implementación (al menos para la capacidad de prueba).

Entonces, ¿cómo se puede aplicar fácilmente el concepto de programación a una interfaz al escribir un nuevo código teniendo en cuenta una implementación en particular? Una opción que usamos comúnmente es una combinación de los siguientes patrones:

  • programación a una interfaz
  • fábrica
  • constructor

El siguiente ejemplo basado en estos principios es una versión simplificada y truncada de una implementación RPC escrita para varios protocolos diferentes:

public interface RemoteInvoker {
  <RQ, RS> CompletableFuture<RS> invoke(RQ request, Class<RS> responseClass);
}

Se supone que la interfaz anterior no debe crearse una instancia directamente a través de una fábrica, en su lugar derivamos interfaces más concretas, una para la invocación HTTP y otra para AMQP, cada una con una fábrica y un constructor para construir instancias, que a su vez también son instancias de la interfaz anterior:

public interface AmqpInvoker extends RemoteInvoker {
  static AmqpInvokerBuilder with(String instanceId, ConnectionFactory factory) {
    return new AmqpInvokerBuilder(instanceId, factory);
  }
}

Las instancias de RemoteInvoker para el uso con AMQP ahora pueden construirse tan fácilmente (o más involucradas según el constructor):

RemoteInvoker invoker = AmqpInvoker.with(instanceId, factory)
  .requestRouter(router)
  .build();

Y una invocación de una solicitud es tan fácil como:

Response res = invoker.invoke(new Request(data), Response.class).get();

Debido a que Java 8 permite la colocación de métodos estáticos directamente en interfaces, la fábrica intermedia se ha convertido en implícita en el código anterior reemplazado con AmqpInvoker.with() . En Java anterior a la versión 8, se puede lograr el mismo efecto con una clase de Factory interna:

public interface AmqpInvoker extends RemoteInvoker {
  class Factory {
    public static AmqpInvokerBuilder with(String instanceId, ConnectionFactory factory) {
      return new AmqpInvokerBuilder(instanceId, factory);
    }
  }
}

La instanciación correspondiente se convertiría entonces en:

RemoteInvoker invoker = AmqpInvoker.Factory.with(instanceId, factory)
  .requestRouter(router)
  .build();

El generador utilizado anteriormente podría tener este aspecto (aunque esto es una simplificación, ya que el actual permite definir hasta 15 parámetros que se desvían de los valores predeterminados). Tenga en cuenta que la construcción no es pública, por lo que solo se puede utilizar específicamente desde la interfaz AmqpInvoker anterior:

public class AmqpInvokerBuilder {
  ...
  AmqpInvokerBuilder(String instanceId, ConnectionFactory factory) {
    this.instanceId = instanceId;
    this.factory = factory;
  }

  public AmqpInvokerBuilder requestRouter(RequestRouter requestRouter) {
    this.requestRouter = requestRouter;
    return this;
  }

  public AmqpInvoker build() throws TimeoutException, IOException {
    return new AmqpInvokerImpl(instanceId, factory, requestRouter);
  }
}

En general, un generador también se puede generar utilizando una herramienta como FreeBuilder.

Finalmente, la implementación estándar (y la única esperada) de esta interfaz se define como una clase de paquete local para imponer el uso de la interfaz, la fábrica y el constructor:

class AmqpInvokerImpl implements AmqpInvoker {
  AmqpInvokerImpl(String instanceId, ConnectionFactory factory, RequestRouter requestRouter) {
    ...
  }

  @Override
  public <RQ, RS> CompletableFuture<RS> invoke(final RQ request, final Class<RS> respClass) {
    ...
  }
}

Mientras tanto, este patrón demostró ser muy eficiente en el desarrollo de todo nuestro nuevo código, sin importar cuán simple o compleja sea la funcionalidad.

Clase abstracta y uso de la interfaz: "Is-a" relationship vs "Has-a" capacity

Cuándo usar clases abstractas: implementar el mismo comportamiento o diferente entre múltiples objetos relacionados

Cuándo usar interfaces: para implementar un contrato por múltiples objetos no relacionados

Las clases abstractas crean que "es una" relación mientras que las interfaces proporcionan "tiene una" capacidad.

Esto se puede ver en el siguiente código:

public class InterfaceAndAbstractClassDemo{
    public static void main(String args[]){
        
        Dog dog = new Dog("Jack",16);
        Cat cat = new Cat("Joe",20);
            
        System.out.println("Dog:"+dog);
        System.out.println("Cat:"+cat);
        
        dog.remember();
        dog.protectOwner();
        Learn dl = dog;
        dl.learn();
                
        cat.remember();
        cat.protectOwner();
        
        Climb c = cat;
        c.climb();
        
        Man man = new Man("Ravindra",40);
        System.out.println(man);
        
        Climb cm = man;
        cm.climb();
        Think t = man;
        t.think();
        Learn l = man;
        l.learn();
        Apply a = man;
        a.apply();
    }
}

abstract class Animal{
    String name;
    int lifeExpentency;
    public Animal(String name,int lifeExpentency ){
        this.name = name;
        this.lifeExpentency=lifeExpentency;
    }
    public abstract void remember();
    public abstract void protectOwner();
    
    public String toString(){
        return this.getClass().getSimpleName()+":"+name+":"+lifeExpentency;
    }
}
class Dog extends Animal implements Learn{
    
    public Dog(String name,int age){
        super(name,age);
    }
    public void remember(){
        System.out.println(this.getClass().getSimpleName()+" can remember for 5 minutes");
    }
    public void protectOwner(){
        System.out.println(this.getClass().getSimpleName()+ " will protect owner");
    }
    public void learn(){
        System.out.println(this.getClass().getSimpleName()+ " can learn:");
    }
}
class Cat extends Animal implements Climb {
    public Cat(String name,int age){
        super(name,age);
    }
    public void remember(){
        System.out.println(this.getClass().getSimpleName() + " can remember for 16 hours");
    }
    public void protectOwner(){
        System.out.println(this.getClass().getSimpleName()+ " won't protect owner");
    }
    public void climb(){
        System.out.println(this.getClass().getSimpleName()+ " can climb");
    }
}
interface Climb{
    void climb();
}
interface Think {
    void think();
}

interface Learn {
    void learn();
}
interface Apply{
    void apply();
}

class Man implements Think,Learn,Apply,Climb{
    String name;
    int age;

    public Man(String name,int age){
        this.name = name;
        this.age = age;
    }
    public void think(){
        System.out.println("I can think:"+this.getClass().getSimpleName());
    }
    public void learn(){
        System.out.println("I can learn:"+this.getClass().getSimpleName());
    }
    public void apply(){
        System.out.println("I can apply:"+this.getClass().getSimpleName());
    }
    public void climb(){
        System.out.println("I can climb:"+this.getClass().getSimpleName());
    }
    public String toString(){
        return "Man :"+name+":Age:"+age;
    }
}

salida:

Dog:Dog:Jack:16
Cat:Cat:Joe:20
Dog can remember for 5 minutes
Dog will protect owner
Dog can learn:
Cat can remember for 16 hours
Cat won't protect owner
Cat can climb
Man :Ravindra:Age:40
I can climb:Man
I can think:Man
I can learn:Man
I can apply:Man

Notas clave:

  1. Animal es una clase abstracta con atributos compartidos: name y lifeExpectancy y métodos abstractos: remember() y protectOwner() . Dog y Cat son Animals que han implementado los métodos remember() y protectOwner() .

  2. Cat puede climb() pero el Dog no puede. Dog puede think() pero el Cat no puede. Estas capacidades específicas se agregan a Cat y Dog por implementación.

  3. Man no es un Animal pero puede Think , Learn , Apply y Climb .

  4. Cat no es un Man pero puede Climb .

  5. Dog no es un Man pero puede Learn

  6. Man no es ni un Cat ni un Dog pero puede tener algunas de las capacidades de los dos últimos sin extender Animal , Cat o Dog . Esto se hace con interfaces.

  7. Aunque Animal es una clase abstracta, tiene un constructor, a diferencia de una interfaz.

TL; DR:

Las clases no relacionadas pueden tener capacidades a través de interfaces, pero las clases relacionadas cambian el comportamiento a través de la extensión de las clases base.

Consulte la página de documentación de Java para comprender cuál usar en un caso de uso específico.

Considera usar clases abstractas si ...

  1. Desea compartir código entre varias clases estrechamente relacionadas.
  2. Usted espera que las clases que extiendan su clase abstracta tengan muchos métodos o campos comunes, o que requieran modificadores de acceso que no sean públicos (como protegidos y privados).
  3. Desea declarar campos no estáticos o no finales.

Considera usar interfaces si ...

  1. Usted espera que las clases no relacionadas implementen su interfaz. Por ejemplo, muchos objetos no relacionados pueden implementar la interfaz Serializable .
  2. Desea especificar el comportamiento de un tipo de datos en particular, pero no le preocupa quién implementa su comportamiento.
  3. Desea aprovechar la herencia de tipo múltiple.

Anulando en herencia

La anulación de herencia se usa cuando se usa un método ya definido de una superclase en una subclase, pero de una manera diferente a como se diseñó originalmente el método en la superclase. La anulación permite al usuario reutilizar el código utilizando el material existente y modificándolo para que se adapte mejor a las necesidades del usuario.


El siguiente ejemplo muestra cómo ClassB anula la funcionalidad de ClassA al cambiar lo que se envía a través del método de impresión:

Ejemplo:

public static void main(String[] args) {
    ClassA a = new ClassA();
    ClassA b = new ClassB();
    a.printing();
    b.printing();
}

class ClassA {
    public void printing() {        
        System.out.println("A");
    }
}

class ClassB extends ClassA {
    public void printing() {
         System.out.println("B");
    }
}

Salida:

UNA

segundo



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow