1. Herencia 2. Polimorfismo 3. Interfaces 2 1. Herencia 1.1. Introduccin 1.2. Los constructores en la herencia 1.3. Modificadores de acceso 1.4. La clase Object 1.5 Herencia vs. composicin 3 1.1.- Introduccin La herencia permite definir clases (subclases) a partir de otra clase ms genrica (superclase). La subclase rene todas la propiedades de la superclase, adems de las suyas propias. La herencia potencia la reutilizacin de cdigo, genera cdigo ms fiable y robusto y reduce el coste de mantenimiento. Persona Estudiante Persona nombre apellidos dni mostrarNombre mostrarDNI Estudiante numExpediente mostrarNumExp class Estudiante extends Persona { . . . 4 class Persona(){ String nombre, apellidos, dni; void mostrarNombre(){ System.out.println(Nombre: + apellidos + , + nombre); } void mostrarDNI() { System.out.println(DNI: + dni); } } class Estudiante extends Persona { String numExpediente; void mostrarNumExp(){ System.out.println(Nm. Exp. + numExpediente); } } class Ejemplo { public static void main(String args[]) { Estudiante e = new Estudiante(); e.nombre = Ana; e.apellidos = Garca; e.dni = 1234567; e.numExpediente = 10001; e.mostrarNombre(); e.mostrarNumExp(); } } 5 1.1.- Introduccin En Java no se permite la herencia mltiple. A B C A B C Error Correcto Una subclase hereda todos los mtodos y atributos de la superclase EXCEPTO: Atributos y mtodos privados Constructores (no se heredan pero s se ejecutan) 6 1.2.- Los constructores en la herencia Conceptos previos: toda clase, por defecto, contiene un constructor sin parmetros y vaco. class Esfera(){ double radio; Esfera() {} // Este constructor existe sin necesidad de escribirlo } Esfera e1 = new Esfera(); Incorrecto. No existe constructor sin parmetros Esfera e2 = new Esfera(10); Correcto El constructor por defecto se pierde si escribimos cualquier otro constructor. class Esfera(){ double radio; Esfera(double r) { radio = r; } } Esfera e = new Esfera(); Correcto 7 1.2.- Los constructores en la herencia Los constructores no se heredan. class Esfera(){ double radio; Esfera(double r) { radio = r; } Esfera() { radio = 1; } } class Planeta extends Esfera { int numSatelites; Planeta(double r, int ns) { radio = r; numSatelites = ns; } } Planeta p1 = new Planeta(6378, 1); Correcto / Incorrecto? Planeta p2 = new Planeta(6378); Correcto / Incorrecto? Planeta p3 = new Planeta(); Correcto / Incorrecto? Esfera e = new Esfera(6378); Correcto / Incorrecto? 8 Cuando creamos un objeto de una subclase, el constructor de la clase padre TAMBIN se ejecuta: class A(){ A() { System.out.println(En A); } } class B extends A { B() { System.out.println(En B); } } class Demo { public static void main(String args[]) { B b = new B(); } } Salida por pantalla: En A En B 1.2.- Los constructores en la herencia Primero se ejecuta el constructor de la superclase y luego el de la subclase 9 Qu constructor se ejecuta en la superclase? ! El constructor sin parmetros, a no ser que digamos lo contrario class A(){ int i; A() { i = 0; } A( int i ){ this.i = i; } } class B extends A { int j; B() { j = 0; } B( int j ){ this.j = j; } } class Demo { public static void main(String args[]) { B b1 = new B(); System.out.println(i= + b1.i + j= + b1.j); B b2 = new B(5);System.out.println(i= + b2.i + j= + b2.j); } } Salida por pantalla: i=0 j =0 i=0 j=5 1.2.- Los constructores en la herencia 10 Cmo podemos forzar la ejecucin de un constructor determinado en la clase padre (superclase)? class B extends A { int j; B() { j = 0; } B( int j ){ super(j); // Ejecuta un constructor en la superclase que // contiene un entero como argumento this.j = j; } } Salida por pantalla: i=0 j =0 i=5 j=5 1.2.- Los constructores en la herencia Si utilizamos super, sta debe de ser la primera instruccin del constructor. De este modo se respeta el orden de ejecucin de los constructores. 11 Prdida del constructor por defecto: class Esfera { Esfera ( double r ) { radio = r; } } class Planeta extends Esfera { int numSatelites; Planeta( double r, int ns ) { radio = r; numSatelites = ns; } } 1.2.- Los constructores en la herencia Cuando creo un objeto de tipo Planeta, qu constructor se ejecuta en la clase Esfera? 12 class Esfera { Esfera() { radio = 1; } Esfera ( double r ) { radio = r; } } class Planeta extends Esfera { int numSatelites; Planeta( double r, int ns ) { radio = r; numSatelites = ns; } } 1.2.- Los constructores en la herencia class Esfera { Esfera ( double r ) { radio = r; } } class Planeta extends Esfera { int numSatelites; Planeta( double r, int ns ) { super(r); numSatelites = ns; } } Solucin a Solucin b (preferible) 13 class A { A () { ... } A( int x ) { ... } } class B extends A { B() { ... } } class C extends A { C( int x ) { ... } } class D extends A { D( int x ) { super(x); } } class E extends D { E( int x ) { ... } } 1.2.- Los constructores en la herencia B ob1 = new B(); C ob2 = new C(1); D ob3 = new D(1); E ob4 = new E(1); E ob5 = new E(); Qu constructor se ejecuta en cada caso? 14 1.3.- Modificadores de acceso private Sin modificador (friendly) protected public Misma clase Si Si Si Si Otra clase del mismo paquete No Si Si Si Subclase de diferente paquete No No Si Si No subclase de diferente paquete No No No Si 19 2.2- Sobreescritura de mtodos En ocasiones interesa que la subclase modifique algunos de los mtodos heredados para que tengan un comportamiento distinto: class Nave { int posX, posY, municion; . . . void disparar() { if(municion>0) municion--; } } class NaveConEscudo extends Nave { boolean escudo; . . . void activarEscudo() { escudo = true; } void desactivarEscudo() { escudo = false; } // Sobreescritura del mtodo disparar void disparar() { if( municion>0 && escudo==false ) municion--; } } 20 2.2- Sobreescritura de mtodos class Juego { public static void main( String [] args ) { Nave nave1 = new Nave(); NaveConEscudo nave2 = new NaveConEscudo(); . . . nave1.disparar(); // disparar de Nave nave2.disparar(); // disparar de NaveConEscudo } } 21 2.2- Sobreescritura de mtodos Hay dos formas de sobreescribir un mtodo: Reemplazo: se reescribe el mtodo completamente, ignorando el cdigo de la superclase. Refinamiento: se ampla el mtodo de la superclase con instrucciones extras. class Persona { String nombre, dni; . . . void mostrarDatos() { System.out.println(Nombre: + nombre); System.out.println(DNI: + dni); } } class MiembroUPV extends Persona { String email; . . . void mostrarDatos() { // Refinamiento super.mostrarDatos(); // Mostrar datos de Persona System.out.println(EMAIL: + email); } } 22 2.2- Sobreescritura de mtodos Si se declara un mtodo como final, se impide su sobreescritura. class Persona { String nombre, dni; . . . final void derechosFundamentales() { System.out.print(nombre + tiene derecho a ); System.out.print(una alimentacin adecuada.); } } Si se declara una clase como final, se impide que se extienda. final class Math { . . . 23 2.3- La conversin hacia arriba Sin embargo es posible declarar una referencia de tipo A y emplearla para instanciar un objeto de tipo B, siempre y cuando B sea una subclase (o un subtipo) de A. A ref = new B(); Hasta ahora el tipo de la referencia y el tipo del objeto instanciado han coincidido: A ref = new A(); Se denomina: Tipo esttico: el tipo con el que se declara la referencia. Tipo dinmico: el tipo del objeto instanciado. En la sentencia: A ref = new B(); Tipo esttico de ref: A Tipo dinmico de ref: B 24 2.3- La conversin hacia arriba Hablamos de conversin hacia arriba cuando se instancia un objeto mediante una referencia perteneciente a un tipo o clase que jerrquicamente est arribade la clase del objeto instanciado. Esfera Planeta Esfera e; e = new Planeta(); // Conversin hacia arriba Tipo esttico de e: Esfera Tipo dinmico de e: Planeta Limitaciones: slo se tiene acceso a los miembros definidos en el tipo esttico. En el ejemplo anterior, aunque se ha creado un objeto de tipo Planeta, mediante e slo se tiene acceso a los atributos y mtodos de Esfera. 25 2.3- La conversin hacia arriba class A { public void m1() { . . . } } class B extends A { public void m2() { . . . } } B obj1 = new B(); // Tipo esttico y dinmico de obj1: B A obj2 = new B(); // Tipo esttico A y tipo dinmico B B obj3 = new A(); // Tipo esttico B y tipo dinmico A. ERROR! m2() B A m1() m2() B A m1() A m1() obj1 obj2 obj3 B obj1 = new B() A obj2 = new B() B obj3 = new A() obj1.m1() OK obj1.m2() OK obj2.m1() OK obj2.m2() ERROR ERROR 26 2.3- La conversin hacia arriba Conversin hacia arriba + sobreescritura Para qu sirve todo esto? class A { public void m1() { . . . } } class B extends A { // Sobreescribimos m1 public void m1() { . . . } public void m2() { . . . } } A obj = new B(); obj.m1(); // Qu mtodo m1 se ejecuta? El de A o el de B? El tipo esttico determina QU se puede hacer. El tipo dinmico determina CMO se hace. 27 2.4- Enlace dinmico y polimorfismo class Figura { Color c; double area() { return 0; // No sabemos qu rea tiene una // figura genrica } } class Rectangulo extends Figura { double alto, ancho; . . . double area() { // Sobresscritura del metodo area return alto*ancho; } } class Circulo extends Figura { double radio; . . . double area() { // Sobresscritura del metodo area return Math.PI*radio*radio; } } 28 2.4- Enlace dinmico y polimorfismo public class EnlaceDinamico { public static void main(String[] args) { // Creamos 10 referencias de tipo Figura Figura [] v = new Figura[10]; // En funcin de ciertas acciones tomadas por el // usuario creamos rectngulos o crculos for(int i=0; i<10; i++) { if( el_usuario_realiza_cierta_accion ) v[i] = new Rectangulo(10,10); // Conv. hacia arriba else v[i] = new Circulo(5); // Conv. hacia arriba } // Mostramos las reas de las figuras creadas for(int i=0; i<10; i++) { double a = v[i].area(); // Enlace dinmico System.out.println("Area="+a); } } } 29 2.4- Enlace dinmico y polimorfismo El polimorfismo permite realizar ciertas abstracciones sobre los tipos de datos. No es necesario conocer el tipo exacto de los datos para poder realizar ciertas operaciones. Puedo obtener el rea de una figura, o dibujarla, sin saber exactamente de qu figura se trata! La siguiente clase permite dibujar un conjunto de figuras, sin necesidad de conocer de qu tipo de figuras se trata! class ConjuntoDeFiguras { Figura [] v = new Figura[1000]; int numFiguras = 0; void aadirFigura( Figura f ) { // El objeto pasado como parmetro v[numFiguras++] = f; // puede ser una subclase de Figura } // (conversin hacia arriba) void dibujaTodo() { for(int i=0; i<numFiguras; i++) { v[i].dibuja(); // Desconozco qu tipo de figura } // estar dibujando } } 30 2.4- Enlace dinmico y polimorfismo class Ordena { static void seleccionDirecta( Conjunto c ) { int pos_min, N = c.getNumElementos(); for( int i = 0; i <= N-2; i++ ) { pos_min = i; for( int j = i+1; j < N; j++ ) { if( c.menor(j, pos_min) ) pos_min = j; } c.intercambiar(i, pos_min); } } } La siguiente clase permite ordenar conjuntos de cualquier tipo (nmeros enteros, colores, personas, ) Requerimientos: El objeto que le pasemos como parmetro al mtodo seleccionDirecta debe ser un subtipo (subclase) de Conjunto. La clase Conjunto debe contener los mtodos getNumElementos, menor e intercambiar. El objeto que pasemos como parmetro puede tener sobreescritos los mtodos de la clase Conjunto. 31 2.4- Enlace dinmico y polimorfismo class Figura { . . . double area() { return 0; } boolean mismaArea(Figura otra) { return this.area() == otra.area(); } } class Rectangulo extends Figura { . . . double area() { return alto * ancho; } } class Circulo extends Figura { . . . double area() { return Mat.PI * radio * radio; } } Una situacin algo ms compleja: Qu mtodo ejecuta en la llamada this.area()? 32 2.5- Clases abstractas class Figura { . . . } class Rectangulo extends Figura { . . . double area() { return alto * ancho; } } class Circulo extends Figura { . . . double area() { return Mat.PI * radio * radio; } } Si no vamos a utilizar nunca el mtodo area de la clase Figura, podramos quitarlo Figura [] v = new Figura[10]; . . . // Aado a v Rectangulos y Circulos for(int i=0; i<v.length; i++) System.out.println(Area= + v[i].area()); pero, es correcto el siguiente cdigo? 33 2.5- Clases abstractas Tiene poco sentido implementar un mtodo que nunca voy a utilizar. Adems, si en Figura implementamos el mtodo area, existe la posibilidad de que alguna subclase no implemente su propia versin de area, en cuyo caso heredara la implementacin (errnea) dada en Figura. Lo ideal sera: 1. Incluir el mtodo area pero no implementarlo (sin cdigo) 2. Obligar a las subclases directas que lo implementen 34 2.5- Clases abstractas abstract class Figura { . . . abstract double area(); } class Rectangulo extends Figura { . . . double area() { return alto * ancho; } } class Circulo extends Figura { . . . double area() { return Mat.PI * radio * radio; } } El mtodo area es abstracto. Se incluye la cabecera del mtodo (tipo, nombre y parmetros) pero no la implementacin (el cdigo). Como la clase Figura tiene un mtodo abstracto, tambin debe ser abstracta. Las subclases de Figura debern implementar el mtodo area. 35 2.5- Clases abstractas Cosas que hay que saber: Una clase abstracta no puede ser instanciada. Si una subclase que extiende una clase abstracta no implementa alguno de los mtodos abstractos declarados en la superclase, entonces debe ser declarada tambin como abstracta. Una clase abstracta puede tener mtodos no abstractos. Se pueden declarar variables referencia cuyo tipo sea una clase abstracta. Aunque las clases abstractas no se pueden instanciar, s que pueden tener constructores. 36 2.5- Clases abstractas abstract class Figura { int origenX, origenY; Color color; Figura(int x, int y, Color c) { origenX = x; origenY = y; color = c; } void mover(int despX, int despY) { origenX += despX; origenY += despY; } abstract double area(); } class Circulo extends Figura { private double radio; Circulo(int x, iny y, double r, Color c) { super(x, y, c); radio = r; } double area() { return Mat.PI * radio * radio; } void setRadio(int r) { radio = (r>=0 ? r : 0); } } 37 2.6- La conversin hacia abajo Conversin hacia arriba: se gana generalidad pero se pierde informacin acerca del tipo concreto con el que se trabaja. Figura f; f = new Circulo(...); Qu ocurre si quiero hacer una operacin propia del tipo concreto con el que estoy trabajando? f.setRadio(5); // Error Conversin hacia abajo: cambio del tipo de la referencia a un subtipo (a un tipo que jerrquicamente est por abajo). Figura f = new Circulo(...); // Conversin hacia arriba . . . Circulo c; c = (Circulo)f; // Conversin hacia abajo c.setRadio(5); // Correcto O simplemente: ((Circulo)f).setRadio(5); 38 2.6- La conversin hacia abajo Peligros de la conversin hacia abajo: Debo estar seguro de convertir la referencia al tipo correcto. Cmo puedo conocer el tipo dinmico de f? Solucin: instanceof Figura f; if(cierta_condicion) f = new Circulo(...); else f = new Rectangulo(...); if ( f instanceof Circulo ) // f es un crculo ((Circulo)f).setRadio(5); else if ( f instanceof Rectangulo ) // f es un rectngulo ((Rectangulo)f).setDim(5,5); 39 2.6- La conversin hacia abajo La conversin hacia abajo debemos usarla cuando no haya otra solucin posible. Se pierde la abstraccin y generalidad que habamos ganado con la conversin hacia arriba. Si lo que pretenda era cambiar el tamao de la figura, hubiese sido preferible la siguiente solucin: abstract class Figura { . . . abstrac void zoom( double factorEscala); } class Circulo extends Figura { . . . void zoom( double factorEscala ) { radio *= factorEscala; } } class Rectangulo . . . void zoom( double factorEscala ) { base *= factorEscala; altura *= factorEscala; } } 40 2.7- Sobreescribir mtodos de Object La clase Object tiene mtodos que puede interesar sobreescribir. boolean equals (Object o): compara dos objetos. String toString(): devuelve una cadena de tipo String que contiene una descripcin del objeto. Se invoca automticamente cuando se utiliza el objeto como si fuera una cadena: void finalize(): se ejecuta automticamente al destruirse el objeto. Uso del mtodo toString: Complejo c = new Complejo(2,3); System.out.println(c); Al utilizar c como si fuera un String, se invoca automticamente al mtodo toString. System.out.println(c.toString()); Si no est sobreescrito, se invova toString de Object. No esperemos que Object sepa cmo mostrar un Complejo. 41 2.7- Sobreescribir mtodos de Object Sobreescritura de toString: class Complejo { double real, imag; . . . public String toString() { String s = real + + + imag + i; return s; } } Complejo c = new Complejo(2,3); System.out.println(c); // Se invoca el mtodo toString Salida por pantalla: 2+3i 42 3. Interfaces 3.1. Introduccin 3.2. Declaracin e implementacin de interfaces 3.3. Polimorfismo mediante interfaces 3.4. Definicin de constantes 3.5. Herencia entre interfaces 43 3.1- Introduccin En Java no existe la herencia mltiple. Las interfaces ofrecen algunas de las ventajas de la herencia mltiple sin ninguno de sus inconvenientes. Una interfaz guarda muchas similitudes con una clase abstracta con todos sus mtodos abstractos y atributos constantes y estticos (final static). A B C No genera conflictos si todos los mtodos de A y B son abstractos. 44 3.2.- Declaracin e implementacin de interfaces Declaracin: acceso interface nombre_interfaz { [public static final] tipo var1; [public static final] tipo var2; ... [public] tipo metodo1( ... ) ; [public] tipo metodo2( ... ) ; } interface Coleccion { void aadirElemento( Object o ); int getNumElementos(); void mostrar(); } Ejemplo: 45 3.2.- Declaracin e implementacin de interfaces Una interfaz define qu operaciones se pueden realizar pero no especifica cmo se realizan. Una interfaz puede ser implementada por una o varias clases. Todo lo que tiene que hacer una clase para implementar una interfaz es sobreescribir todos sus mtodos. class Conjunto implements Coleccion { private Object[] v; private int numElementos; public void aadirElemento( Object o ) { . . . } public int getNumElementos() { . . . } public void mostrar() { . . . } } 46 3.2.- Declaracin e implementacin de interfaces Es posible que varias clases sin relacin de herencia implementen una misma interfaz y que una misma clase implemente varias interfaces. I2 E F B C D A I1 47 3.3.- Polimorfismo mediante interfaces Una interfaz es un tipo de dato. Es posible declarar referencias de tipo interfaz (aunque no se puedan instanciar objetos de este tipo). La conversin hacia arriba se puede aplicar tambin a las interfaces. interface Coleccion { void aadir(Elemento e); void borrar(Elemento e); } class Conjunto implements Coleccin { . . . } class ListaEnlazada implements Coleccin { . . . } class Ejemplo { public static void main(String [] args) { Coleccion c; c = new Conjunto(); // Conv. Hacia arriba c.aadir( ... ); . . . 48 3.4.- Definicin de constantes Las interfaces tambin pueden emplearse para definir constantes Aunque no se especifique explcitamente, los atributos de una interfaz siempre son estticos y constantes (static final). interface CteMat{ double pi = 3.14159265; double e = 2.71828182; } class Ejemplo { public static void main(String [] args) { double r = 4; double area = CteMat.pi * r * r; . . . 49 3.5.- Herencia entre interfaces Es posible definir herencia entre interfaces. Se permite la herencia mltiple. interface I1 { void metodo1(); void metodo2(); } interface I2 { void metodo3(); } interface I3 extends I1, I2 { void metodo4(); } class C implements I3 { // Deber implementar metodo1, metodo2, // metodo3 y metodo4 }