Herencia

Se pueden distinguir las superclases de sus subclases por tama˜no. En general una subclase es más grande que una supercl
209KB Größe 10 Downloads 161 Ansichten
Herencia H. Tejeda Abril 2016

´Indice 1. Concepto de herencia

1

2. Extensi´ on de clases

5

3. Anulaci´ on de m´ etodos superclase

6

4. Funcionamiento de constructores subclase

8

5. Uso de m´ etodos de la superclase

10

6. Ocultamiento de informaci´ on

12

7. M´ etodos no anulables

14

1.

Concepto de herencia

En los lenguajes orientados al objeto, la herencia es un mecanismo que permite a una clase adquirir el comportamiento y los atributos de otra clase para expandirse en estas caracter´ısticas. Herencia es el principio que permite aplicar el conocimiento de una categor´ıa general a objetos m´as espec´ıficos. Con la herencia se puede crear una nueva clase indicando la forma en la cual difiere de una clase que ha sido desarrollada y probada.

1

1.1.

UML

Los programadores y analistas en ocasiones usan un lenguaje gr´afico para describir las clases y procesos orientados al objeto; este Lenguaje Unificado de Modelado o´ UML (Unified Modeling Language) consiste de varios tipos de diagramas. Los diagramas UML pueden servir para ilustrar la herencia. La clase Empleado, c´odigo 1, contiene dos campos de datos, numEmp y salEmp, y cuatro m´etodos: un m´etodo accesor y mutador por cada campo. El cuadro 1 muestra un diagrama de clase UML para la clase Empleado. Un diagrama de clase es una herramienta visual que da un panorama de una clase. Esta consiste de un rect´angulo dividido en tres secciones, la secci´on superior contiene el nombre de la clase, la secci´on central contiene los nombres y los tipos de datos de los atributos, y la secci´on inferior contiene los m´etodos. S´olo el tipo que regresa el m´etodo, el nombre, y los argumentos son puestos en el diagrama, las instrucciones del cuerpo del m´etodo no se ponen. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public c l a s s Empleado { private int numEmp ; private double salEmp ; public int getNumEmp ( ) { return numEmp ; } public double getSalEmp ( ) { return salEmp ; } public void setNumEmp ( int num) { numEmp = num ; } public void setSalEmp ( double s a l ) { salEmp = s a l ; } }

C´odigo 1: La clase Empleado. Empleado -numEmp : int -salEmp : double +getNumEmp : int +getSalEmp : double +setNumEmp(int num) : void +setSalEmp(double sal) : void Cuadro 1: Diagrama de la clase Empleado Nota. Un diagrama de clase contiene el tipo de dato siguiendo a cada atributo o m´etodo. Un signo (-) es puesto antes de cada campo privado o m´etodo, y un signo m´as (+) es puesto antes de cada campo o m´etodo.

2

Despu´es de crear la clase Empleado, se pueden crear objetos espec´ıficos Empleado, tales como las siguientes: Empleado recepcionista = new Empleado(); Empleado mensajero = new Empleado(); Suponiendo que se tiene un nuevo tipo de empleado, el cual requiere tener un campo adicional que indique un territorio donde este trabaja, adem´as de los campos para el n´ umero y el salario. Se puede crear una clase con un nombre tal como EmpleadoConTerritorio y con tres campos (numEmp, salEmp y territorioEmp) y seis m´etodos (un m´etodo accesor y mutador por cada uno de los tres campos). Sin embargo, cuando se hace esto, se est´a duplicando el trabajo que ya se ha hecho para la clase Empleado. La alternativa m´as eficiente es crear la clase EmpledoConTerritorio para que herede todos los atributos y los m´etodos de Empleado y agregar un campo y dos m´etodos nuevos dentro de los objetos EmpleadoConTerritorio. El cuadro 2 muestra un diagrama de clase de esta relaci´on. En un diagrama UML, una relacion de herencia es indicada con una flecha que apunta desde la clase descendiente a la clase original. Empleado -numEmp : int -salEmp : double +getNumEmp : int +getSalEmp : int +setNumEmp(int num) : void +setSalEmp(double sal) : void ~ w w w w

EmpleadoConTerritorio -territorioEmp : int +getTerritorioEmp : int +setTerritorioEmp(int terr) : void Cuadro 2: Diagrama de EmpleadoConTerritorio.

la

clase

mostrando

la

relaci´on

entre

Empleado

y

Cuando se usa herencia para crear la clase EmpleadoConTerritorio, se logra: Ahorrar tiempo porque los campos y m´etodos Empleado ya existen. Reducir los errores porque los m´etodos Empleado ya han sido usados y probados. Reducir la cantidad de nuevo aprendizaje requerido para usar la nueva clase, porque ya se han usado los m´etodos Empleado en objetos m´as simples y ya se entiende como ellos trabajan. 3

La habilidad para usar herencia en Java hace los programas m´as f´aciles de escribir, menos susceptibles a errores, y m´as f´acil de entender. Adem´as de crear EmpleadoConTerritorio, se pueden tambi´en crear otras clases Empleado espec´ıficas, por ejemplo para incluir una comisi´on o una raz´on para un empleado despedido. Mediante el uso de herencia, se puede desarrollar cada nueva clase de forma correcta y r´apida. El concepto de herencia es u ´til porque hace un c´odigo de clase m´as f´acilmente reusable. Cada campo de dato y m´etodo definido ya escrito y probado en la clase original se convierte en parte de la nueva clase que la hereda.

1.2.

Terminolog´ıa de herencia

La clase base es la que es usada como base para la herencia, como la clase Empleado. Cuando se crea un clase que hereda desde una clase base, tal como EmpleadoConTerritorio, esta es una clase derivada. Se puede decir cual es la clase base y cual es la clase derivada usando las dos clases en una sentencia con la frase “es un(a)”. Una clase derivada siempre “es un” caso, o ejemplo, de una clase base m´as general. Por ejemplo, una clase Arbol puede ser una clase base para una clase Pino. Un Pino “es un” Arbol, as´ı Arbol es la clase base; sin embargo, esto no es cierto para todos los a´rboles que “un Arbol es un Pino”. De igual forma, un EmpleadoConTerritorio “es un” Empleado, pero no a la inversa, as´ı Empleado es la clase base. Nota. Como un objeto de una clase derivada tambi´en “es una” instancia de la clase base, se pueden asignar referencias de objetos de clases derivadas a referencias de la base clase. De igual forma, si un m´etodo acepta una referencia objeto de una clase base, este tambi´en aceptar´a referencias de sus clases derivadas.

No se debe confundir situaciones “es una” con situaciones “tiene una”. Considerar una clase Negocio que contiene un arreglo de objetos Departamento, y a su vez un objeto Departamento podr´ıa contener un arreglo de objetos Empleado. Observar que no se puede decir “un departamento es un negocio” pero s´ı “un negocio tiene departamentos”. Por lo tanto, esta relaci´on no es herencia, es una composici´ on, la relacion en la cual una clase contiene uno o m´as miembros de otra clase, cuando estos miembros podr´ıan no continuar existiendo sin el objeto que los contiene. Si un Negocio cierra, sus Departamentos tambi´en. De igual forma no se dice “un empleado es un departamento” pero si “un departamento tiene empleados”. Esta relaci´on no es herencia; es un tipo espec´ıfico de composici´on conocido como agregaci´ on, la relaci´on en la cual una clase contiene uno o m´as miembros de otra clase, cuando aquellos miembros continuar´ıan existiendo sin el objeto que los contiene. Si un departamento o negocio cierra, los empleados podr´ıan continuar existiendo. Se pueden usar los t´erminos superclase y subclase como sin´onimos para la clase base y la clase derivada, respectivamente. As´ı, Pino puede ser llamada una subclase de la superclase Arbol. Se puede tambi´en usar los t´erminos clase padre y clase hija. Un EmpleadoConTerritorio es una hija del padre Empleado. 4

Se pueden distinguir las superclases de sus subclases por tama˜ no. En general una subclase es m´as grande que una superclase porque esta usualmente tiene campos y m´etodos adicionales. Una descripci´on de subclase podr´ıa verse peque˜ na, pero cualquier subclase contiene todos los campos y m´etodos de su superclase, al igual que los campos y m´etodos nuevos m´as espec´ıficos que se agregan a la subclase.

2.

Extensi´ on de clases

Se usa la palabra reservada extends para lograr herencia en Java. La siguiente cabecera de clase crea una relacion superclase-subclase entre Empleado y EmpleadoConTerritorio: public class EmpleadoConTerritorio extends Empleado EmpleadoConTerritorio recibe los campos de datos y m´etodos de la superclase EmpleadoNumSal; luego se agregan campos y m´etodos nuevos a la subclase creada. El c´odigo 2 muestra la clase EmpleadoConTerritorio. 1 2 3 4 5 6 7 8 9

public c l a s s Em pl ea do Co nT er ri to ri o extends Empleado { private int t e r r i t o r i o E m p ; public int g e t T e r r i t o r i o E m p ( ) { return t e r r i t o r i o E m p ; } public void s e t T e r r i t o r i o E m p ( int t e r r ) { territorioEmp = terr ; } }

C´odigo 2: La clase EmpleadoConTerritorio. Se pueden escribir una sentencia que instancie un objeto de la clase derivada, tal como la siguiente: EmpleadoConTerritorio agenteDelSur = new EmpleadoConTerritorio(); Luego se puede cualquiera de las sentencias para obtener los valores de los campos del objeto agenteDelSur: agenteDelSur.getNumEmp(); agenteDelSur.getSalEmp(); agenteDelSur.getTerritorioEmp();

5

El objeto agenteDelSur tiene acceso a los tres m´etodos de acceso, los dos m´etodos que son heredados de Empleado y el m´etodo que pertenece a la clase EmpleadoConTerritorio. La herencia es una proposici´on en un s´olo sentido; un hijo hereda de un padre, y no en sentido contrario. Cuando se instancia un objeto Empleado, este no tiene acceso a los m´etodos EmpleadoConTerritorio. Lo anterior tiene sentido, ya que cuando se crea la clase padre, no se sabe cuantas subclases futuras podr´ıa tener o como ser´ıan sus datos o m´etodos. Las subclases son m´as espec´ıficas que las superclases que extienden. Una clase Ortodoncista y una Periodoncista son hijas de la clase padre Odont´ ologo. No se espera que todos los miembros de la clase padre Odont´ ologo tengan el m´etodo aplicarBrackets() de Ortodoncista o el m´etodo implanteDental() de Periodoncista. Sin embargo, objetos Ortodoncista y Periodoncista tienen acceso a los m´etodos generales de Odont´ ologo como hacerHistoriaCl´ ınica() y facturarPacientes(). Se puede emplear el operador instanceof para determinar si un objeto es un miembro o un descendiente de una clase. Si agenteDelSur es un objeto EmpleadoConTerritorio, entonces el valor de cada una de las siguientes expresiones es true: agenteDelSur instanceof EmpleadoConTerritorio agenteDelSur instanceof Empleado Si unOficinista es un objeto Empleado, entonces lo siguiente da true: unOficinista instanceof Empleado pero la siguiente da false: unOficinista instanceof EmpleadoConTerritorio Los programadores dicen que instanceof da true si el operando izquierdo puede ser convertido hacia arriba del operando derecho.

3.

Anulaci´ on de m´ etodos superclase

Cuando se crea una subclase extendiendo una clase existente, la nueva subclase contiene campos de datos y m´etodos que fueron definidos en la superclase original. Es decir, cualquier objeto clase hija tiene todos los atributos de sus padres. Sin embargo en ocasiones los campos de datos y m´etodos no son adecuados para los objetos de la subclase; en este caso, se quiere anular, o sobreescribir, los miembros de la clase padre.

6

En el lenguaje coloquial frecuentemente se usa el mismo nombre de un m´etodo para indicar diversos significados. Si se piensa en InstrumentoMusical como una clase, se puede pensar sonar() como un m´etodo en esta clase. Si se piensan varias subclases tales como Guitarra y Flauta, entonces el m´etodo sonar() se hace de forma diferente para cada subclase. Usar el mismo nombre de m´etodo para indicar diferentes implementaciones es denominado polimorfismo, un t´ermino que significa “varias formas”. Varias formas de la misma palabra existen, dependiendo del objeto asociado con la palabra. Suponer que se crea una superclase Empleado conteniendo campos de datos tales como nombres, apellidos, numeroSeguridadSocial, fechaContratacion, salario, etc., y sus m´etodos accesores y mutadores. Si el periodo de pago para cada objeto Empleado es semanal, el m´etodo mostrarSalario() podr´ıa incluir una sentencia como: System.out.println("Salario es $" + salario + " por semana."); Suponer que una empresa tiene pocos Empleados a los que no se les paga semanalmente. Algunos reciben su pago por hora, y otros son contratados por obra. Como cada tipo Empleado requiere diferentes procedimientos de c´alculo para el pago, se podr´ıan crear subclases de Empleado, como EmpleadoPorHora y EmpleadoPorContrato. Cuando se llama al m´etodo mostrarSalario() para un objeto EmpleadoPorHora se quiere mostrar algo como “Salario es $100.oo por hora.” y cuando se hace para un EmpleadoPorContrato se muestra “Salario es $20000.oo por contrato.”. La superclase Empleado y las dos subclases requieren su propio m´etodo mostrarSalario(). Si se crean m´etodos mostrarSalario() separados para cada clase, los objetos de cada clase usan el m´etodo apropiado para esa clase. Cuando se crea un m´etodo en la clase hija que tiene el mismo nombre y lista de par´ametros como en su clase padre, se anula el m´ etodo en la clase padre. Nota. Un objeto clase hija puede usar un m´etodo padre anulado usando la palabra reservada super.

Si el m´etodo de la clase padre tiene el mismo nombre pero una lista de par´ametros diferente, el m´etodo subclase no anula la versi´on clase padre; lo que se logra es que el m´etodo subclase sobrecargue el m´etodo de la clase padre, y cualquier objeto subclase tiene acceso a ambas versiones Si no se pueden anular m´etodos superclase, siempre se podr´ıa crear un nombre u ´nico para cada m´etodo subclase, pero esto hace m´as engorroso la escritura y la comprensi´on de la clase, en vez de usar un s´olo m´etodo para hacer esencialmente la misma cosa. Los programadores orientados al objeto, usan el t´ermino polimorfismo cuando se emplea alguna operaci´on que tiene varios significados. El signo mas (+) es polim´orfico porque opera de diferente forma dependiendo de los operandos. Se puede usar el signo m´as para sumar enteros o de punto flotante, concatenar cadenas, o indicar un valor positivo. Tambi´en los m´etodos 7

con el mismo nombre pero listas de par´ametros diferentes son polim´orficos porque la llamada del m´etodo opera diferente dependiendo de los argumentos. Cuando los desarrolladores de Java se refieren al polimorfismo, frecuentemente se refieren a polimorfismo de subtipo, la habilidad del nombre de un m´etodo para trabajar apropiadamente para objetos subclase diferentes de la misma clase padre.

4.

Funcionamiento de constructores subclase

Cuando se crea cualquier objeto, como en la siguiente sentencia, se est´a llamando a un m´etodo clase constructor que tiene el mismo nombre que la clase: AlgunaClase unObjeto = new AlgunaClase(); Cuando se instancia un objeto que es un miembro de una subclase, se est´an llamando al menos dos constructores: el constructor de la clase base y el constructor para la clase derivada. Cuando se crea cualquier objeto subclase, el constructor superclase se ejecuta primero, y luego el constructor subclase. Nota. Cada objeto Java autom´aticamente es un hijo de la clase llamada Object cuando no se indica expl´ıcitamente que clase extiende la subclase. Entonces, cuando se instancia cualquier objeto, se llama su constructor y el constructor de Object.

Cuando una superclase contiene un constructor por defecto y se instancia un objeto subclase, la ejecuci´on del constructor superclase es transparente, es decir, no hay nada que haga notar el hecho de que el constructor de la superclase se ejecuta. El c´odigo 3 muestra tres clases. La clase UnaSuperClase tiene un constructor que muestra un mensaje. La clase UnaSubClase desciende de UnaSuperClase, y su constructor muestra un mensaje diferente. La clase DemoConstructores tiene una sola sentencia que instancia un objeto del tipo UnaSubClase.

8

1 2 3 4 5

public c l a s s UnaSuperClase { public UnaSuperClase ( ) { System . out . p r i n t l n ( ”En e l c o n s t r u c t o r de l a s u p e r c l a s e ” ) ; } }

6 7 8 9 10 11

public c l a s s UnaSubClase extends UnaSuperClase { public UnaSubClase ( ) { System . out . p r i n t l n ( ”En e l c o n s t r u c t o r de l a s u b c l a s e ” ) ; } }

12 13 14 15 16 17

public c l a s s DemoConstructores { public s t a t i c void main ( S t r i n g [ ] a r g s ) { UnaSubClase h i j a = new UnaSubClase ( ) ; } }

C´odigo 3: Tres clases que muestran el llamado a constructores cuando un objeto subclase es instanciado. Al ejecutar la aplicaci´on DemoConstructores se puede ver que al instanciar el objeto UnaSubClase, primero se ejecuta el constructor de la clase padre, UnaSuperClase, mostrando un mensaje, y luego se ejecuta el constructor de la clase hija. A pesar de que s´olo un objeto es creado, dos constructores son ejecutados. Los constructores realizan otras tareas m´as u ´tiles que mostrar un mensaje. Cuando los constructores inicializan variables, se quiere que el constructor superclase lo haga con los campos de datos espec´ıficos de la superclase, y a su vez, el constructor subclase inicialice los campos de datos espec´ıficos a la subclase. Cuando una superclase tiene un constructor por defecto, se puede crear una subclase con o sin su propio constructor. Sin embargo, cuando una superclase contiene solo constructores que requieren argumentos, se debe incluir al menos un constructor para cada subclase creada. Los constructores de la subclase pueden contener cualquier cantidad de sentencias, pero si todos los constructores superclase requieren argumentos, entonces la primera sentencia dentro de cada constructor subclase debe llamar a uno de los constructores superclase. Si una superclase tiene constructores m´ ultiples pero uno es constructor por defecto, no se tiene que crear un constructor subclase al menos que se quiera. Si la subclase no contiene constructor, todos los objetos subclase usan el constructor superclase por defecto cuando son instanciados. El formato de la sentencia que llama al constructor superclase desde el constructor de la subclase es: super (lista de argumentos); 9

La palabra reservada super se refiere a la superclase de la clase en la cual se usa este. Si una superclase contiene solo constructores que requieren argumentos, se debe crear un constructor subclase, pero el constructor subclase no necesariamente debe tener par´ametros propios. Suponer que se ha creado una clase Empleado con un constructor que requiere tres argumentos, un char, un double y un int, y se ha creado la clase EmpleadoPorHora que es una subclase de Empleado. El siguiente c´odigo muestra un constructor v´alido para EmpleadoPorHora: public EmpleadoPorHora() { super (’D’, 120.00, 40); // otras sentencias pueden ir aqu´ ı } El constructor por defecto no requiere argumentos, pero pasa tres argumentos constantes a su constructor superclase. Una versi´on del constructor EmpleadoPorHora diferente puede ocupar argumentos. Se podr´ıan entonces pasar los argumentos apropiados al constructor de la subclase. Por ejemplo: public EmpleadoPorHora(char dept, double tarifa, int horas) { super (dept, tarifa, horas); // otras sentencias pueden ir aqu´ ı } La sentencia super() deber´a ser la primera en cualquier constructor subclase que la use. Ni las definiciones de campos de datos la pueden preceder.

5.

Uso de m´ etodos de la superclase

Una subclase puede contener un m´etodo con el mismo nombre y argumentos, la misma firma, como un m´etodo en su clase padre. Cuando esto pasa, el m´etodo subclase anula el m´etodo superclase. En alguna situaci´on se podr´ıa querer usar el m´etodo superclase dentro de la subclase. Para lograrlo se usa la palabra reservada super para acceder el m´etodo de la clase padre. La superclase Cliente, c´odigo 4, tiene los campos numId y saldoAdeudado. La subclase ClientePreferido, c´odigo 5, agrega el campo descuento. En el m´etodo mostrar() ClientePreferido, se quiere mostrar los tres campos, los dos de la superclase y el de la subclase. Como en la clase Cliente se tiene el m´etodo mostrar() para los dos campos, en el m´etodo mostrar() de ClientePreferido se usa el m´etodo mostrar() del padre antes de mostrar el descuento. 10

1 2 3 4 5 6 7 8 9 10 11 12

public c l a s s C l i e n t e { private int numId ; private double saldoAdeudado ; public C l i e n t e ( int num , double s a l ) { numId = num ; saldoAdeudado = s a l ; } public void m o s t r a r ( ) { System . out . p r i n t l n ( ” C l i e n t e #” + numId + ” S a l d o $ ” + saldoAdeudado ) ; } }

C´odigo 4: Clase Cliente. 1 2 3 4 5 6 7 8 9 10 11

public c l a s s C l i e n t e P r e f e r i d o extends C l i e n t e { private double d e s c u e n t o ; public C l i e n t e P r e f e r i d o ( int num , double s a l , double d e s c ) { super (num , s a l ) ; descuento = desc ; } public void m o s t r a r ( ) { super . m o s t r a r ( ) ; System . out . p r i n t l n ( ” El d e s c u e n t o e s de ” + d e s c u e n t o ) ; } }

C´odigo 5: Clase ClientePreferido. La aplicaci´on ProbarClientes, c´odigo 6, crea un objeto Cliente usando su constructor de dos argumentos, enseguida se crea otro objeto ClientePreferido con su constructor de tres argumentos. Luego cada uno de los objetos es mostrado. Ejecutar esta aplicaci´on para observar como el m´etodo mostrar() ClientePreferido llama al m´etodo mostrar() de la superclase. 1 2 3 4 5 6 7 8

public c l a s s P r o b a r C l i e n t e s { public s t a t i c void main ( S t r i n g [ ] a r g s ) { C l i e n t e u n C l i e n t e = new C l i e n t e ( 1 2 3 , 1 2 3 . 4 5 ) ; C l i e n t e P r e f e r i d o unCPref = new C l i e n t e P r e f e r i d o ( 2 3 4 , 3 4 5 6 . 7 8 , 0 . 1 5 ) ; unCliente . mostrar ( ) ; unCPref . m o s t r a r ( ) ; } }

C´odigo 6: Aplicaci´on ProbarCliente. Cuando se llama constructor superclase desde un constructor subclase, la llamada deber´a ser la primer sentencia en el constructor. Sin embargo, cuando se llama un m´etodo superclase 11

ordinario dentro de un m´etodo subclase, no se requiere que la llamada sea la primera sentencia en el m´etodo, como se muestra en el m´etodo mostrar() de la subclase.

5.1.

this y super

En una subclase, las palabras reservadas this y super en ocasiones se refieren al mismo m´etodo, pero en ocasiones no. Por ejemplo, si una subclase ha anulado un m´etodo superclase llamado algunMetodo(), entonces dentro de la subclase, super.algunMetodo() se refiere a la versi´on superclase del m´etodo, y ambas algunMetodo() y this.algunMetodo() se refieren a la version subclase. Por otra parte, si una subclase no ha anulado un m´etodo superclase llamado algunMetodo(), la clase hija puede usar el nombre del m´etodo con super, porque el m´etodo es un miembro de la superclase, con this, porque el m´etodo es un miembro de la subclase por virtud de la herencia, o sola, porque el m´etodo es un miembro de la subclase.

6.

Ocultamiento de informaci´ on

La clase Estudiante, c´odigo 7, es un ejemplo de una clase Java t´ıpica. Dentro de la clase Estudiante la palabra reservada private precede cada campo de datos, y la palabra reservada public precede cada m´etodo. Los m´etodos accesores y mutadores son public dentro de la clase Estudiante porque los campos de datos private. Si los m´etodos no son public, entonces no hay forma de acceder los campos de datos private. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public c l a s s E s t u d i a n t e { private int numEst ; private double promedio ; public int getNumEst ( ) { return numEst ; } public double getPromedio ( ) { return promedio ; } public void setNumEst ( int num) { numEst = num ; } public void setProm edio ( double prom ) { promedio = prom ; } }

C´odigo 7: La clase Estudiante.

12

Cuando una aplicaci´on es un cliente de la clase Estudiante, es decir, esta instancia un objeto Estudiante, el cliente no puede directamente alterar el dato de cualquier campo private. Por ejemplo, suponer que se escribe un m´etodo main() que crea un Estudiante como: Estudiante algunEstudiante = new Estudiante(); Entonces no se puede cambiar el numEst de Estudiante con una sentencia como la siguiente: algunEstudiante.numEst = 812; numEst del objeto algunEstudiante no es accesible en el m´etodo main() que usa el objeto Estudiante porque numEst es privado. S´olo los m´etodos que son parte de la clase Estudiante pueden modificar los datos private Estudiante. Para modificar el numEst de Estudiante se debe usar un m´etodo public, como el siguiente: algunEstudiante.setNumEst(812); El concepto de mantener los datos privados se conoce como ocultamiento de informaci´ on. De esta forma los datos pueden ser alterados solo por los m´etodos que se escojan y solo en formas que se puedan controlar. Se podr´ıa querer que el m´etodo setNumEst() para revisar que el par´ametro est´a dentro de un rango espec´ıfico de valores para ser asignado al campo numEst. Si una clase diferente a la clase Estudiante pudiera modificar numEst, numEst podr´ıa asignar un valor que la clase Estudiante no podr´ıa controlar. Cuando una clase sirve como una superclase a otras clases que se creen, las subclases heredan todos los datos y m´etodos de la superclase. Los m´etodos en una subclase pueden usar todos los campos de datos y m´etodos que pertenezcan a su padre, con una excepci´on: miembros private de la clase padre no son accesibles dentro los m´etodos de la clase hija. Si una nueva clase pudiera simplemente extender la clase Estudiante y acceder a sus campos de datos sin ir a trav´es de los canales apropiados, el ocultamiento de informaci´on no operar´ıa. Nota. Si los miembros de una clase base no tienen un especificador de acceso expl´ıcito, su acceso es package por defecto. Tales miembros clase base no pueden ser accedidos dentro de una clase hija al menos que las dos clases est´en en el mismo paquete.

En ocasiones se quiere acceso a los datos de la clase padre dentro de una subclase. Si se quiere que los m´etodos subclase puedan directamente acceder a los campos de datos, entonces estos campos no pueden ser private y si por otra parte no se quiere acceso desde otras clases que no sean hijas, entonces tampoco pueden ser public. Con la palabra reservada protected se tiene un nivel de seguridad entre el acceso private y public. Usando protected con un campo de datos o un m´etodo, se permite su uso dentro de su propia clase o en cualquier 13

clase extendida desde esa clase, pero no podr´a ser usado por clases “externas”. Miembros protected son aquellos que pueden ser usados por su clase y sus descendientes. Si se quiere que las clases hijas puedan acceder directamente datos de la clase padre, se necesita hacer que los campos clase padre sean protected. Un acceso indirecto a los campos clase padre private es a trav´es de los m´etodos accesores y mutadores definidos en la clase padre. El acceso directo se podr´ıa necesitar para cuando no se quiere que una clase padre tenga un m´etodo accesor para un campo, pero se quiere que la clase hija pueda acceder al campo. Otro caso, para usar protected, ser´ıa cuando un m´etodo clase padre mutador forza l´ımites en los valores del campo, pero un objeto de la clase hija podr´ıa no tener tales l´ımites. El especificador de acceso protected para un campo puede ser conveniente, ya que mejora el rendimiento del programa porque la clase hija puede usar un campo heredado directamente en vez de “ir a trav´es” de m´etodos para acceder los datos. Los miembros dato protected deber´an usarse lo menos posible. Siempre que sea posible, el principio de ocultamiento deber´a observarse, as´ı clases hijas podr´ıan tener que ir a trav´es de m´etodo public para “obtener” los datos privados del padre. Cuando a las clases hijas se les permite acceder directamente a los campos padre, la probabilidad de errores futuros se incrementa. Las clases que directamente usan campos de las clases padre se dice que son fr´ agiles porque son propensas a errores, ya que son f´aciles de “romper”.

7.

M´ etodos no anulables

En ocasiones cuando se crea un clase, se podr´ıa querer no permitir a las subclases anular o sobrescribir algunos de los m´etodos superclase. Las razones por las cuales se quiere esta restricci´on es porque no se quiere que cualquier clase derivada puede dar su propia versi´on de alg´ un m´etodo, o quiz´as porque la clase tiene un m´etodo que muestra restricciones legales para usar la clase, y no se quiere que una clase derivada pueda mostrar una versi´on diferente. Los tres tipos de m´etodos que no se pueden anular en una subclase son: M´etodos static. M´etodos final. M´etodos dentro de clases final.

7.1.

M´ etodos superclase static

Una subclase no puede anular m´etodos que son declarados static en la superclase. Un m´etodo static se usa sin instanciar un objeto. Una subclase puede ocultar un m´etodo padre static declarando un m´etodo static en la subclase con la misma firma que el m´etodo static en la superclase; entonces se puede llamar el nuevo m´etodo static desde adentro 14

de la subclase o desde otra clase usando un objeto subclase. El m´etodo static que oculta el m´etodo static superclase no puede acceder al m´etodo padre usando el objeto super. El c´odigo 8 muestra la clase JugadorBeisbol que contiene un s´olo m´etodo static llamado mostrarOrigenes(). El c´odigo 9 muestra la clase JugadorProfesionalBeisbol que extiende la clase JugadorBeisbol para poner un salario. En la clase JugadorProfesionalBeisbol se intenta crear un m´etodo no est´atico que anule el m´etodo static mostrarOrigenes(). Pero el compilador devuelve un mensaje de error indicando que no se puede anular un m´etodo static con un m´etodo no static. Compilar estas clases para ver el mensaje de error de indicado. 1 2 3 4 5 6 7 8

public c l a s s J u g a d o r B e i s b o l { private int numeroPlayera ; private double promedioBateo ; public s t a t i c void m o s t r a r O r i g e n e s ( ) { System . out . p r i n t l n ( ” Abner Doubleday r e c i b e f r e c u e n t e m e n t e ” + ” e l c r´e d i t o de haber i n v e n t a d o e l b e i s b o l ” ) ; } }

C´odigo 8: Clase JugadorBeisbol. 1 2 3 4 5 6 7 8

public c l a s s J u g a d o r P r o f e s i o n a l B e i s b o l extends J u g a d o r B e i s b o l { double s a l a r i o ; public void m o s t r a r O r i g e n e s ( ) { super . m o s t r a r O r i g e n e s ( ) ; System . out . p r i n t l n ( ” El p r i m e r j u e g o p r o f e s i o n a l ” + ” de l a s l i g a s mayores de b´e i s b o l f u e jugado en 1871 ” ) ; } }

C´odigo 9: Clase JugadorProfesionalBeisbol que intenta anular el m´etodo static del padre En el c´odigo 10 se muestra la clase JugadorProfesionalBeisbol. En esta clase el m´etodo mostrarOrigenes() ha sido cambiado a static en un intento por arreglar el error de la clase JugadorProfesionalBeisbol. Compilar esta clase para ver el mensaje de error que aparece. Lo anterior obedece a que el m´etodo es static y por lo tanto no usa un objeto, as´ı que no recibe una referencia this. La palabra reservada super puede ser usada en la clase hija en m´etodos de instancia y constructores, pero no en m´etodos static.

15

1 2 3 4 5 6 7 8

public c l a s s J u g a d o r P r o f e s i o n a l B e i s b o l extends J u g a d o r B e i s b o l { double s a l a r i o ; public s t a t i c void m o s t r a r O r i g e n e s ( ) { super . m o s t r a r O r i g e n e s ( ) ; System . out . p r i n t l n ( ” El p r i m e r j u e g o p r o f e s i o n a l ” + ” de l a l i g a s mayores de b´e i s b o l f u e jugado en 1871 ” ) ; } }

C´odigo 10: Clase JugadorProfesionalBeisbol El c´odigo 11 muestra la clase JugadorProfesionalBeisbol la cual compila sin error. La clase extiende a JugadorBeisbol, y su m´etodo mostrarOrigenes() es static. Como el m´etodo tiene la misma firma que el de la clase padre, cuando se usa con un objeto de la clase hija, este m´etodo oculta el original. Como el m´etodo es static no se puede usar super para emplear el m´etodo padre, se puede hacer lo siguiente: Se puede poner una sentencia dentro del m´etodo clase hija que repita el mensaje que aparece en la clase padre usando println(). Usar el nombre de la clase padre, un punto, y el nombre del m´etodo, ya que los m´etodos static del padre no los hereda la clase hija, sin embargo, se pueden acceder estos de la misma forma que cualquier otra clase. 1 2 3 4 5 6 7 8

public c l a s s J u g a d o r P r o f e s i o n a l B e i s b o l extends J u g a d o r B e i s b o l { double s a l a r i o ; public s t a t i c void m o s t r a r O r i g e n e s ( ) { JugadorBeisbol . mostrarOrigenes ( ) ; System . out . p r i n t l n ( ” El p r i m e r j u e g o p r o f e s i o n a l ” + ” de l a s l i g a s mayores de b´e i s b o l f u e jugado en 1871 ” ) ; } }

C´odigo 11: La clase JugadorProfesionalBeisbol. El c´odigo 12 tiene la aplicaci´on ProbarJugadorPro que crea un objeto de la clase JugadorProfesionalBeisbol3 y que llama con este objeto al m´etodo mostrarOrigenes(). Compilar y ejecutar esta aplicaci´on para observar que el compilador ya no genera error, y que se muestran tanto el mensaje del m´etodo de la clase hija, as´ı como el de la clase padre.

16

1 2 3 4 5 6 7

public c l a s s ProbarJugadorPro { public s t a t i c void main ( S t r i n g [ ] a r g s ) { J u g a d o r P r o f e s i o n a l B e i s b o l unNaranjero = new J u g a d o r P r o f e s i o n a l B e i s b o l ( ) ; unNaranjero . m o s t r a r O r i g e n e s ( ) ; } }

C´odigo 12: Aplicaci´on ProbarJugadorPro.

7.2.

M´ etodos final de la superclase

Una subclase no puede anular m´etodos que est´an declarados final en la superclase. Considerar las clases JugadorBasquet y JugadorProfesionalBasquet, c´odigos 13 y 14 respectivamente. Cuando se intenta compilar la clase JugadorProfesionalBasquet, se recibe un mensaje de error, ya que la clase no puede anular el m´etodo mostrarMensaje() final de la clase padre. 1 2 3 4 5 6 7 8

public c l a s s JugadorBasquet { private int numeroPlayera ; private double p o r c e n t a j e T i r o s L i b r e s ; public f i n a l void mostrarMensaje ( ) { System . out . p r i n t l n ( ” Michael Jordan e s e l ” + ” j u g a d o r m´ a s i m p o r t a n t e en e l b a s q u e t b o l −− y e s o e s f i n a l ” ) ; } }

C´odigo 13: Clase JugadorBasquet. 1 2 3 4 5 6

public c l a s s J u g a d o r P r o f e s i o n a l B a s q u e t extends JugadorBasquet { double s a l a r i o ; public void mostrarMensaje ( ) { System . out . p r i n t l n ( ”Yo no t e n g o nada que d e c i r ” ) ; } }

C´odigo 14: Clase JugadorProfesionalBasquet. La palabra reservada final se ha usado para crear constantes con nombre, como en final double IVA = 0.16;. El modificador final tambi´en se puede usar con m´etodos que no quieran ser anulados, es decir, cuando se quiere que cualquier clase hija use la versi´on original de la clase padre de un m´etodo. En Java, todas las llamadas a m´etodos de instancia son llamadas a m´ etodos virtuales por defecto, es decir, el m´etodo usado es determinado cuando el programa se ejecuta porque el tipo del objeto usado podr´ıa no ser conocido hasta la ejecuci´on del m´etodo. Para el siguiente

17

m´etodo se puede pasar un objeto JugadorBasquet, o cualquier otro objeto que sea hijo de JugadorBasquet, asi el tipo “actual” del argumento jugadorB, y cual versi´on usar de mostrarMensaje(), no es conocido hasta la ejecuci´on del m´etodo. public void mostrar(JugadorBasquet jugadorB) { jugadorB.mostrarMensaje(); } La versi´on del m´etodo usado no es determinado cuando el programa es compilado; es determinado cuando la llamada al m´etodo es hecha. Determinar el m´etodo correcto toma una peque˜ na fraccion de tiempo. El tener un m´etodo final es una ventaja para el compilador porque sabe que s´olo hay una versi´on del m´etodo, la versi´on de la clase padre, y es la u ´nica versi´on que se puede usar, de esta forma se logra que el programa sea m´as eficiente. El compilador optimiza el rendimiento de un programa cuando hay definiciones de m´etodos final, ya que las llamadas a los m´etodos final son quitadas y se reemplazan con el c´odigo expandido de sus definiciones en cada lugar de la llamada al m´etodo. Este proceso es llamado expansi´ on en l´ınea o´ inlining del c´odigo. Cuando el programa se ejecuta, el proceso de expansi´on en l´ınea no se hace, el compilador escoge usar este procedimiento para ahorrar la sobrecarga de llamada al m´etodo, y este se ejecuta m´as r´apido. El compilador hace la expansi´on en l´ınea de un m´etodo final solo si este es un m´etodo peque˜ no que contenga una o dos l´ıneas de c´odigo.

7.3.

M´ etodos en una superclase final

Se puede declarar una clase para que sea final. Cuando se hace, todos los m´etodos son final, no importando cual modificador de acceso preceda al nombre del m´etodo. Una clase final no puede ser padre. En la clase JugadorEsconderseBuscar, c´odigo 15, se tiene una clase final porque la palabra resevada final est´a en la cabecera de la clase, y la clase JugadorProfesionalEsconderseBuscar, codigo 16, intenta extender la clase final, agregando un campo salario; compilar esta clase para mostrar el mensaje de error. 1 2 3 4 5 6 7

public f i n a l c l a s s J u g a d o r E s c o n d e r s e B u s c a r { private int c u e n t a ; public void m o s t r a r R e g l a s ( ) { System . out . p r i n t l n ( ” Se t i e n e que c o n t a r h a s t a ” + c u e n t a + ” a n t e s de empezar a b u s c a r a l o s e s c o n d i d o s ” ) ; } }

C´odigo 15: Clase JugadorEsconderseBuscar final.

18

1 2 3 4

public c l a s s J u g a d o r P r o f e s i o n a l E s c o n d e r s e B u s c a r extends J u g a d o r E s c o n d e r s e B u s c a r { double s a l a r i o ; }

C´odigo 16: Clase JugadorProfesionalEsconderseBuscar.

19