Herencia en Java: Tipos y Ejemplos

POO

La herencia es un pilar importante de OOP (Programación Orientada a Objetos). Es el mecanismo en Java por el cual una clase permite heredar las características (atributos y métodos) de otra clase. Aprenda más a continuación.

En el lenguaje de Java, una clase que se hereda se denomina superclase. La clase que hereda se llama subclase. Por lo tanto, una subclase es una versión especializada de una superclase. Hereda todas las variables y métodos definidos por la superclase y agrega sus propios elementos únicos.

Terminología importante

  • Superclase: la clase cuyas características se heredan se conoce como superclase (o una clase base o una clase principal).
  • Subclase: la clase que hereda la otra clase se conoce como subclase (o una clase derivada, clase extendida o clase hija). La subclase puede agregar sus propios campos y métodos además de los campos y métodos de la superclase.
  • Reutilización: la herencia respalda el concepto de “reutilización”, es decir, cuando queremos crear una clase nueva y ya hay una clase que incluye parte del código que queremos, podemos derivar nuestra nueva clase de la clase existente. Al hacer esto, estamos reutilizando los campos/atributos y métodos de la clase existente.

1. Concepto de Herencia

1.1 ¿Qué es Herencia?

Podemos definir la herencia como la capacidad de crear clases que adquieren de manera automática los miembros (atributos y métodos) de otras clases que ya existen, pudiendo al mismo tiempo añadir atributos y métodos propios.

Java soporta la herencia permitiendo una clase a incorporar otra clase en su declaración. Esto se hace mediante el uso de la palabra clave extends. Por lo tanto, la subclase se añade (se extiende) a la superclase.

Ventajas de la Herencia

Entre las principales ventajas que ofrece la herencia en el desarrollo de aplicaciones, están:

  • Reutilización del código: En aquellos casos donde se necesita crear una clase que, además de otros propios, deba incluir los métodos definidos en otra, la herencia evita tener que reescribir todos esos métodos en la nueva clase.
  • Mantenimiento de aplicaciones existentes: Utilizando la herencia, si tenemos una clase con una determinada funcionalidad y tenemos la necesidad de ampliar dicha funcionalidad, no necesitamos modificar la clase existente (la cual se puede seguir utilizando para el tipo de programa para la que fue diseñada) sino que podemos crear una clase que herede a la primera, adquiriendo toda su funcionalidad y añadiendo la suya propia.

2. Ejemplo de cómo usar la herencia en Java

La palabra clave utilizada para la herencia es extends

  • Ejemplo: Comencemos con un breve ejemplo que ilustra varias de las características clave de la herencia. El siguiente programa crea una superclase llamada DosDimensiones, que almacena el ancho y la altura de un objeto bidimensional, y una subclase llamada Triangulo. Observe cómo se usa la palabra clave extends para crear una subclase.
//Clase para objetos de dos dimensiones
class DosDimensiones{
    double base;
    double altura;

    void mostrarDimension(){
        System.out.println("La base y altura es: "+base+" y "+altura);
    }
}
//Una subclase de DosDimensiones para Triangulo
class Triangulo extends DosDimensiones{
    String estilo;


    double area(){
        return base*altura/2;
    }

    void mostrarEstilo(){
        System.out.println("Triangulo es: "+estilo);
    }
}

class Lados3{
    public static void main(String[] args) {
        Triangulo t1=new Triangulo();
        Triangulo t2=new Triangulo();

        t1.base=4.0;
        t1.altura=4.0;
        t1.estilo="Estilo 1";

        t2.base=8.0;
        t2.altura=12.0;
        t2.estilo="Estilo 2";

        System.out.println("Información para T1: ");
        t1.mostrarEstilo();
        t1.mostrarDimension();
        System.out.println("Su área es: "+t1.area());

        System.out.println();

        System.out.println("Información para T2: ");
        t2.mostrarEstilo();
        t2.mostrarDimension();
        System.out.println("Su área es: "+t2.area());

    }
}

Salida:

Información para T1: 
Triangulo es: Estilo 1
La base y altura es: 4.0 y 4.0
Su área es: 8.0

Información para T2: 
Triangulo es: Estilo 2
La base y altura es: 8.0 y 12.0
Su área es: 48.0
  Nota: En el programa anterior, cuando se crea un objeto de clase Triangulo, una copia de todos los métodos y campos de la superclase adquiere memoria en este objeto. Es por eso que, al usar el objeto de la subclase, también podemos acceder a los miembros de una superclase. 

Tenga en cuenta que durante la herencia solo se crea el objeto de la subclase, no de la superclase.

En la práctica, la herencia y el polimorfismo se usan juntos en Java para lograr un rendimiento rápido y legibilidad del código.

3. Control de Acceso a Miembros en Herencia

A menudo una variable de instancia de una clase se declarará privada (private) para evitar su uso no autorizado o alteración. Heredar una clase no anula la restricción de acceso privado. Por lo tanto, aunque una subclase incluye a todos los miembros de su superclase, no puede acceder a los miembros de la superclase que se han declarado privados.

Por ejemplo, si, como se muestra aquí, la base y la altura se vuelven privados en DosDimensiones, entonces Triangulo no podrá acceder a ellos.

Ejemplo:

//Clase para objetos de dos dimensiones
class DosDimensiones{
    private double base;
    private double altura;

    void mostrarDimension(){
        System.out.println("La base y altura es: "+base+" y "+altura);
    }
}
//Una subclase de DosDimensiones para Triangulo
class Triangulo extends DosDimensiones{
    String estilo;
    double area(){
        return base*altura/2; //Error! no se puede acceder
    }

    void mostrarEstilo(){
        System.out.println("Triangulo es: "+estilo);
    }
}

La clase Triangulo no se compilará porque la referencia a la base y altura dentro del método area() causa una infracción de acceso. Como la base y altura se declaran privados, solo son accesibles para otros miembros de su propia clase. Las subclases no tienen acceso a ellas.

Recuerde que un miembro de la clase que ha sido declarado [java]private[java] seguirá siendo privado para su clase. No es accesible por ningún código fuera de su clase, incluidas las subclases.

Al principio, podría pensar que el hecho de que las subclases no tengan acceso a los miembros privados de las superclases es una restricción seria que impediría el uso de miembros privados en muchas situaciones. Sin embargo, eso no es verdad. Los programadores de Java suelen utilizar métodos de acceso para proporcionar acceso a los miembros privados de una clase.

Lectura Recomendada
Modificadores de Acceso

Aquí hay una reescritura de las clases DosDimensiones y Triangulo que usa métodos para acceder a las variables de instancia privadas base y altura:

//Clase para objetos de dos dimensiones
class DosDimensiones{
    private double base;
    private double altura;

    //Métodos de acceso para base y altura
    double getBase(){return base;}
    double getAltura(){return altura;}
    void setBase(double b){base=b;}
    void setAltura (double h){altura=h;}

    void mostrarDimension(){
        System.out.println("La base y altura es: "+base+" y "+altura);
    }
}
//Una subclase de DosDimensiones para Triangulo
class Triangulo extends DosDimensiones{
    String estilo;


    double area(){
        return getBase()*getAltura()/2;
    }

    void mostrarEstilo(){
        System.out.println("Triangulo es: "+estilo);
    }
}

class Lados3{
    public static void main(String[] args) {
        Triangulo t1=new Triangulo();
        Triangulo t2=new Triangulo();

        t1.setBase(4.0);
        t1.setAltura(4.0);
        t1.estilo="Estilo 1";

        t2.setBase(8.0);
        t2.setAltura(12.0);
        t2.estilo="Estilo 2";

        System.out.println("Información para T1: ");
        t1.mostrarEstilo();
        t1.mostrarDimension();
        System.out.println("Su área es: "+t1.area());

        System.out.println();

        System.out.println("Información para T2: ");
        t2.mostrarEstilo();
        t2.mostrarDimension();
        System.out.println("Su área es: "+t2.area());

    }
}

Salida:

Información para T1: 
Triangulo es: Estilo 1
La base y altura es: 4.0 y 4.0
Su área es: 8.0

Información para T2: 
Triangulo es: Estilo 2
La base y altura es: 8.0 y 12.0
Su área es: 48.0

4. Constructores y herencia

En una jerarquía, es posible que tanto las superclases como las subclases tengan sus propios constructores. Esto plantea una pregunta importante: ¿qué constructor es responsable de construir un objeto de la subclase, el de la superclase, el de la subclase o ambos? La respuesta es esta: el constructor para la superclase construye la porción de la superclase del objeto, y el constructor para la subclase construye la parte de la subclase.

Esto tiene sentido porque la superclase no tiene conocimiento ni acceso a ningún elemento en una subclase. Por lo tanto, su construcción debe estar separada. En la práctica, la mayoría de las clases tendrán constructores explícitos (no predeterminados). Aquí verá cómo manejar esta situación.

Cuando solo la subclase define un constructor, el proceso es sencillo: simplemente construye el objeto de la subclase. La porción de superclase del objeto se construye automáticamente utilizando su constructor predeterminado. Por ejemplo, aquí hay un programa para Triangulo que define un constructor. También hace que el estilo sea privado, ya que ahora lo establece el constructor.

//Clase para objetos de dos dimensiones
//DosDimensiones.java
class DosDimensiones{
    private double base;
    private double altura;

    //Métodos de acceso para base y altura
    double getBase(){return base;}
    double getAltura(){return altura;}
    void setBase(double b){base=b;}
    void setAltura (double h){altura=h;}

    void mostrarDimension(){
        System.out.println("La base y altura es: "+base+" y "+altura);
    }
}
//Una subclase de DosDimensiones para Triangulo
//Triangulo.java
class Triangulo extends DosDimensiones{
    private String estilo;

    //Constructor
    Triangulo(String s, double b, double h){
        setBase(b);
        setAltura(h);
        estilo=s;
    }

    double area(){
        return getBase()*getAltura()/2;
    }

    void mostrarEstilo(){
        System.out.println("Triangulo es: "+estilo);
    }
}

class Lados3{
    public static void main(String[] args) {
        Triangulo t1=new Triangulo("Estilo 1",4.0,4.0);
        Triangulo t2=new Triangulo("Estilo 2",8.0,12.0);

        System.out.println("Información para T1: ");
        t1.mostrarEstilo();
        t1.mostrarDimension();
        System.out.println("Su área es: "+t1.area());

        System.out.println();

        System.out.println("Información para T2: ");
        t2.mostrarEstilo();
        t2.mostrarDimension();
        System.out.println("Su área es: "+t2.area());

    }
}

Salida:

Información para T1: 
Triangulo es: Estilo 1
La base y altura es: 4.0 y 4.0
Su área es: 8.0

Información para T2: 
Triangulo es: Estilo 2
La base y altura es: 8.0 y 12.0
Su área es: 48.0

Aquí, el constructor de Triangulo inicializa los miembros de DosDimensiones que hereda, junto con su propio campo de estilo.

Cuando tanto la superclase como la subclase definen constructores, el proceso es un poco más complicado porque deben ejecutarse tanto la superclase como los constructores de subclase. En este caso, debe usar otra de las palabras clave de Java, super, que tiene dos formas generales.

Lectura Recomendada
Palabra Clave 'super'
  • El primero llama a un constructor de superclase.
  • El segundo se usa para acceder a un miembro de la superclase que ha sido ocultado por un miembro de una subclase.

5. Tipos de herencia en Java

A continuación se muestran los diferentes tipos de herencia compatibles con Java.

  • Herencia única: en la herencia única, las subclases heredan las características de solo una superclase. En la imagen a continuación, la clase A sirve como clase base para la clase derivada B.
Herencia única Java
Herencia única en Java
  • Herencia Multinivel: en la herencia multinivel, una clase derivada heredará una clase base y, además, la clase derivada también actuará como la clase base de otra clase. En la imagen inferior, la clase A sirve como clase base para la clase derivada B, que a su vez sirve como clase base para la clase derivada C. En Java, una clase no puede acceder directamente a los miembros de los “abuelos”.
Herencia Multinivel Java
Herencia Multinivel en Java
  • Herencia Jerárquica: en la herencia jerárquica, una clase sirve como una superclase (clase base) para más de una subclase. En la imagen inferior, la clase A sirve como clase base para la clase derivada B, C y D.
Herencia Jerárquica Java
Herencia Jerárquica Java
  • Herencia Múltiple (a través de interfaces): en Herencia múltiple, una clase puede tener más de una superclase y heredar características de todas las clases principales. Tenga en cuenta que Java no admite herencia múltiple con clases. En Java, podemos lograr herencia múltiple solo a través de Interfaces. En la imagen a continuación, la Clase C se deriva de la interfaz A y B.
Herencia Múltiple Java
Herencia Múltiple en Java
  • Herencia Híbrida (a través de Interfaces): Es una mezcla de dos o más de los tipos de herencia anteriores. Como Java no admite herencia múltiple con clases, la herencia híbrida tampoco es posible con clases. En Java, podemos lograr herencia híbrida solo a través de Interfaces.
Herencia Híbrida Java
Herencia Híbrida en Java

6. Datos importantes acerca de la herencia en Java

  • Superclase predeterminada: excepto la clase Object, que no tiene superclase, cada clase tiene una y solo una superclase directa (herencia única). En ausencia de cualquier otra superclase explícita, cada clase es implícitamente una subclase de la clase Object.
  • La superclase solo puede ser una: una superclase puede tener cualquier cantidad de subclases. Pero una subclase solo puede tener una superclase. Esto se debe a que Java no admite herencia múltiple con clases. Aunque con interfaces, la herencia múltiple es compatible con java.
  • Heredar constructores: una subclase hereda todos los miembros (campos, métodos y clases anidadas) de su superclase. Los constructores no son miembros, por lo que no son heredados por subclases, pero el constructor de la superclase puede invocarse desde la subclase.
  • Herencia de miembros privados: una subclase no hereda los miembros privados de su clase principal. Sin embargo, si la superclase tiene métodos públicos o protegidos (como getters y setters) para acceder a sus campos privados, estos también pueden ser utilizados por la subclase.

7. ¿Qué se puede hacer en una Subclase?

En las subclases podemos heredar los miembros tal como están, reemplazarlos, ocultarlos o complementarlos con nuevos miembros:

  • Los campos heredados se pueden usar directamente, al igual que cualquier otro campo.
  • Podemos declarar nuevos campos en la subclase que no están en la superclase.
  • Los métodos heredados se pueden usar directamente tal como son.
  • Podemos escribir un nuevo método de instancia en la subclase que tenga la misma firma que el de la superclase, anulándolo (como en el ejemplo anterior, el método toString() se reemplaza).
  • Se puede escribir un nuevo método estático en la subclase que tiene la misma firma que el de la superclase, escondiéndolo así.
  • Podemos declarar nuevos métodos en la subclase que no están en la superclase.
  • Podemos escribir un constructor de subclase que invoca el constructor de la superclase, ya sea implícitamente o mediante la palabra clave super.
Herencia en Java
  • Tipos y Ejemplos

Sobre el Autor:

Hey hola! Yo soy Alex Walton y tengo el placer de compartir conocimientos hacía ti sobre el tema de Programación en Java, desde cero, Online y Gratis.

0 comentarios

  1. […] de referencia que se usa para referir objetos de clase padre (puede revisar sobre Herencia, en este enlace). Se usa principalmente en los siguientes […]

Deja una Respuesta

*

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.