Subclases y Herencia Los métodos y variables que posee un objeto definen la clase a la cual pertenece. Por ejemplo, todos los objetos de la clase A poseen los métodos Set, Incx y Print y las variables x e y. En cambio los objetos de la clase Eslabon poseen el método Encadenar y las variables next y a. Una variable de tipo Eslabon no puede contener una referencia a un objeto de la clase A.

Eslabon e= new A(); // error de tipos

Puede existir una clase B de objetos que poseen todos los métodos y todas las variables de A, pero además poseen otros métodos y/o variables que no poseen los objetos de A. En ese caso se dice que B es una subclase de A. Los objetos de la clase B también pertenecen a la clase A. El principio es que todo el código que se haya escrito para objetos de la clase A también funcionará con objetos de la clase B. Una subclase se define mediante:

class B extends A {

  // variables que B agrega a A
  int z;
  // Métodos que B agrega a A
  // Observe que B también posee x
  void Incz() { z= z+x; }

}

Se dice que la clase B hereda todas las variables y métodos de A. También se dice que B se deriva de A o que A es la clase base para B. La jerarquía de clases permite apreciar fácilmente qué clases son subclases de otras.

Observe que todos los objetos pertenecen a la clase Object.

Consideraciones importantes al usar subclases:

Una variable de la clase A, también puede contener referencias a objetos de la clase B, porque estos objetos pertenecen a la clase B y a la clase A. Este concepto se denomina proyección. A a; a= new B(); // Proyección

Se dice que A es el tipo estático de la variable a y B es el tipo dinámico de a. El tipo estático siempre se determina en tiempo de compilación mientras que el tipo dinámico en general sólo se puede conocer en tiempo de ejecución y puede cambiar.

Dada una variable a, Java sólo permite invocar los métodos y accesar las variables conocidas para el tipo estático de a. a.Incx(); // Ok a.x; // Ok a.Incz(); // error, Incz no está definido para A

Del mismo modo, Java sólo permite asignar una expresión a una variable de tipo A si el tipo de la expresión es A o una subclase de A: B b= new A(); // error, el objeto no pertence a

              // la clase B

A a= new B(); // Ok … B b= a; // error, la clase estática de a

              // no es una subclase de B.

Un objeto se puede convertir a una referencia de la clase B mediante un cast: A a=new B(); B b=(B)a; b.Incz(); // Ok ( (B)a ).Incz(); // Ok

No todo objeto se puede convertir a la clase B. A a= new A(); … B b=(B)a; // Ok, en compilación, pero

               // error en tiempo de ejecución

Java chequea durante la ejecución todas las conversiones explícitas (casts). Si el objeto no pertence a la clase a la cual se pretende convertir, entonces se produce una excepción.


El operador instanceof Se puede consultar si un objeto pertenece a una clase mediante: Object obj; … if (obj instanceof A)

  // obj pertenece a la clase A
  A a=(A)obj; // Ok, nunca hay error

Los objetos de la clase B también son instancias de la clase A: Object obj= new B(); if (obj instanceof A) // true

  A a= (A)obj;        // Ok

El constructor en una subclase Los constructores no se heredan: class A {

  …
  A(int ix, int iy){ … };

}

class B extends A {

}

B b= new B(1,2); // error, ningún

                 // constructor calza

El constructor de la clase base se puede invocar con super: class B extends A {

  …
  B(int ix, int iy)
  {
    super(ix, iy);
    z= 0;
  }
  B(int ix, int iy, int iz)
  {
    super(ix, iy);
    z= iz;
  }
  B(B b)
  {
    z= b.z; // x=y=?
    super(b.x, b.y); // error, super debe ser
  }                  // la primera instrucción

}

La invocación del constructor de A siempre debe ser la primera instrucción del constructor de B. El principio es que en B las componentes de la clase base (A) deben inicializarse antes que las componentes que se agregan en la clase B.


Redefinición de Métodos Un problema que tiene la clase B que heredó de A es que el método Print sólo imprime los campos x e y: B b= new B(1, 2, 3); b.Print(); // 1 2 >8^(

Al declarar una clase B derivada de A, aparte de agregar campos y métodos, también se pueden redefinir métodos. Por ejemplo, para B se puede redefinir el método Print: class B extends A {

  …
  void Print() // Redefinición
  { System.out.println(x+” “+y+” “+z); }

} B b= new B(1, 2, 3); b.Print(); // 1 2 3 8^)

El número y tipo de los parámetros del método redefinido debe coincidir exactamente con los del método original. Observe que el método Print para la clase A no cambia:

A a= new A(1, 2); a.Print(); // 1 2



Google