You are on page 1of 130

1

Sun Certified Java Programmer 6, CX-310-065 - Parte 1: Declaraciones y Controles de Acceso Este es el primer post dedicado a la certificacin SCJP 6 (CX-310065). Definiremos algunos conceptos bsicos del lenguaje Java as como sus reglas ms bsicas de funcionamiento. Por qu es importante esto? Bueno esto es de vital importancia ya que en todo lenguaje de programacin, y tecnologa en general, es importante para entender sus funciones avanzadas, comenzar entendiendo las partes bsicas, as como definir los trminos que se usan, para que todos entendamos de lo que estamos hablando. Pues comencemos con el primer tema con un repaso de los conceptos bsicos que debemos tener presentes, a modo de recordatorio. DECLARACIONES Y CONTROLES DE ACCESO Conceptos bsicos: Recordemos que Java es un lenguaje orientado a objetos, as que comencemos con algunas definiciones de este paradigma: Clase: Una clase es una especie de plantilla que describe el estado y comportamiento que los objetos de su tipo soportan. Las clases son una forma de encapsular datos y las operaciones relacionadas con esos datos. Son la estructura fundamental del paradigma orientado a objetos. Objeto: Son las instancias de una clase. Tiene su propio estado, y el acceso a todos los comportamientos u operaciones definidas por su clase. Estado: Los valores asignados a las "variables de instancia" de un objeto constituyen el estado del objeto. Comportamiento (Mtodos): Es donde la lgica de la clase es almacenada, donde los algoritmos son ejecutados y los datos son manipulados.

Cuando comenzamos a trabajar con el paradigma orientado a objetos algunas veces es difcil entender la diferencia entre clase y objeto. Usando una analoga, podramos decir que, en vez de ser programadores somos cocineros y estamos haciendo galletas, la clase sera el molde para las galletas y los objetos seran las galletas que se hagan con ese molde. Identificadores y Palabras Reservadas: Los nombres de las clases, variables y mtodos (en general los nombres de todo lo que podemos declarar) son llamados identificadores. Los programadores de Java (y Sun) han creado convenciones para nombrar los mtodos, variables y clases. Estos mtodos sern mencionados ms adelante en este mismo post. Como todos los lenguajes de programacin Java cuenta con palabras reservadas, tenemos que recordar que estas palabras reservadas no deben ser usadas como identificadores. Herencia: El centro de Java y de la programacin orientada a objetos. Con la herencia podemos definir que una clase sea reusada en otras clases, podemos definir una clase general (sper-clase) y sub-clases que heredan de esta. La sper-clase no sabe nada de la existencia de las clases que heredan de ella pero todas las sub-clases que la heredan deben declarar explcitamente la relacin de herencia que existe. Una sub-clase obtiene automticamente acceso a las variables y mtodos definidos en la sper-clase y tambin es libre de sobre escribir los mtodos (hablaremos sobre sobre-escritura de mtodos en un post posterior). Interfaces: El compaero perfecto de la herencia es el uso de las interfaces. Las interfaces son clases 100% abstractas que definen los mtodos que una subclase debe implementar, pero no como debe hacerlo. Esto nos permite definir el comportamiento que esperamos que tenga una clase, sin decir exactamente cmo debe hacerlo. Gracias a las interfaces podemos lograr uno de los objetivos ms buscados dentro

del diseo orientado a objetos: bajo hablaremos en los siguientes posts). Identificadores y JavaBeans

acoplamiento (del

cual

En java existen reglas y acuerdos para denominar los diferentes tipos de datos y objetos existentes y creados por nosotros mismos a continuacin explicamos brevemente cada uno de estos conceptos: Identificador legal: Como dijimos: un identificador es el nombre que se le da a un elemento en Java (ya sea una clase, interface, variable, enumeracin, etc). Para que un identificador sea legal se utilizan una serie de reglas explicadas posteriormente. Si un identificador no sigue las reglas necesarias para sea correcto o legal este arrojara un error. Para que el compilador detecte si un identificador es legal o no, utiliza las reglas establecidas para su correcto funcionamiento. Hablaremos de estas reglas en un momento. Convenciones de cdigo Java de Sun: Son recomendaciones de Sun para nombrar a las clases, variables y mtodos. Estndar de Nombres JavaBeans: Los requerimientos de nombres de la especificacin JavaBeans.

Ahora pasaremos a hablar detalladamente de cada uno de estos: Identificador legal: Tcnicamente un identificador legal debe estar compuesto por solo caracteres Unicode, nmeros, smbolos de dlar ( $) y caracteres de conexin (como el guion bajo "_"). Los identificadores legales deben cumplir con los siguientes requisitos: Un identificador debe empezar con una letra, smbolo de dlar ($) o caracteres de conexin (_), nunca debe empezar con un nmero. Despus del primer carcter l identificador puede contener cualquier combinacin de letras, smbolo de dlar ( $), caracteres de conexin (_) o nmeros. No hay un lmite de caracteres que un identificador pueda tener.

No se pueden usar una palabra reservada como identificador. Los identificadores son "Case Sensitive", es decir, una clase "Carro" no es igual que una clase "cARRO".

*Nota: Usaremos el IDE Netbeans para ayudarnos a aclarar algunos ejemplos. Este IDE est actualmente en su versin 6.9, pero usaremos la versin 6.8 con lo cual no debera haber ningn problema. Ejemplo de identificadores legales:

Como podemos observar, el IDE no marca ningn error, con lo cual deducimos que las declaraciones son correctas aunque algo extraas. Ejemplo de identificadores no legales:

Como podemos observar, esta vez el IDE si marca error ya que obviamente no estamos siguiendo las reglas antes mencionadas para que un identificador sea legal. Las palabras reservadas de Java son 50 (hasta la versin 1.6) y las mencionamos en la siguiente tabla. Recordamos que no podemos llamar a un identificador como uno de estas palabras reservadas. abstra boolean ct char class double else for int goto break const byte continue case catch

default do finally float instanceo f package static throw

extends final if

implements import native new short

interfac long e

privat protecte public return e d strict super fp throws switch

synchronize this d void

transien try t

volatile while

assert enum Cabe mencionar que aunque todas las palabras de la tabla anterior estn reservadas no todas se usan en las versiones actuales de Java, y estn "apartadas" por si en algn momento se les da un uso. Convenciones de cdigo Java de Sun: Sun ha creado un conjunto de normas o convenciones o recomendaciones de codificacin de Java, y se publicaron las normas en un documento titulado ingeniosamente " Convenciones de cdigo Java", que se puede encontrar en la pgina de convenciones de cdigo de Oracle.

Aqu estn las normas de nomenclatura que Sun recomienda: Clases e interfaces: La primera letra debe ser mayscula, si varias palabras se unen para formar un nombre cada palabra que lo conforma debe empezar con la primera letra mayscula a esto se le llama "Camel Case", por ejemplo:

Perro Cuenta SistemaDeAdministracionFinanciero

Las interfaces son tpicamente adjetivos como:


Runnable Serializable Imprimible

Mtodos: la primera letra debe ser minscula y despus se puede aplicar la regla Camel Case, tpicamente son pares verbo-sustantivo, por ejemplo:

calculaPeso getBalance obtenConexion setNombreCliente

Variables: Similar a los mtodos, Sun recomienda nombres cortos, significativos y fciles de recordar.

nombreCompleto porcentajeInteres miString

Constantes: Las variables constantes son marcadas como " static" y "final". Estas se deben nombrarse con todas sus letras maysculas y separando cada palabra con guion bajo ( _), por ejemplo:

ESTE_VALOR_NUNCA_CAMBIARA VARIABLE_ESTATICA

Aunque no es necesario seguir estas convenciones para que nuestro cdigo compile, el hacerlo nos ayudar cuando realicemos proyectos en equipo o que otros entiendan ms fcilmente nuestro cdigo. Ahora hablaremos de los estndares JavaBeans que son acuerdos para nombrar a las clases, mtodos "set" (establecer un valor) y "get" (obtener un valor), etc. Que tampoco estamos obligados a seguirlas pero es una buena prctica para nosotros y para tener ms claro nuestro cdigo. Estndares JavaBeans Los JavaBean son clases que contienen propiedades, estas propiedades son nombradas variables de instancia y por lo regular, aunque deberamos tratar de que fuera siempre, las marcamos como privadas, la nica forma de acceder a estas propiedades desde fuera de la clase es a travs de mtodos, los mtodos que se utilizan para establecer valor a estas variables son llamados mtodos " setters" y los mtodos para obtener el valor son llamados "getters". Sun establece unas reglas para nombrar a las propiedades, as como a sus mtodos "setter" y "getter". Muchos frameworks se basan en que nuestro cdigo sigue estas reglas para poder funcionar correctamente y, como toda regla, si no las seguimos corremos el riesgo de que algo no funcione como lo esperamos. Reglas para nombrar a las propiedades: Si la propiedad no es de tipo booleano, el prefijo para un mtodo getter es "get", por ejemplo para una variable " nombre" su respectivo mtodo getter ser "getNombre" (aqu tambin se aplica la regla de Camel Case para separar cada palabra). Tambin hay que tener en cuenta que no hay necesidad de que el nombre que sigue despus del prefijo "get" sea igual al nombre de la propiedad, el nombre de la propiedad es irrelevante para el mtodo " get" o "set". Si la propiedad es un booleano el mtodo getter para esta propiedad puede llevar el prefijo " get" o "is", para una propiedad

booleana llamada "estado" su mtodo get vlido puede ser "getEstado" o "isEstado". El prefijo para el mtodo setter de una propiedad es "set", por ejemplo "setNombre" para un atributo "nombre". Como dije anteriormente en las propiedades tambin se aplica la regla camel case, es decir, despus del prefijo " set", "get" o "is" la primera letra seguida es una letra mayscula. El mtodo "setter" debe ser marcado pblico con un valor de retorno void (vacio) y con un argumento o parmetro que represente al tipo de la propiedad, por ejemplo: " public void setNombre(String nombre){ }" para una propiedad "nombre" del tipo String. Los mtodos "get" tambin deben ser marcados pblicos con un valor de retorno que represente al tipo de la propiedad y/o con el tipo de argumento o parmetro del mtodo set para esa propiedad, por ejemplo: "public String getNombre(){ return nombre; }" para una propiedad "nombre" del tipo String". Estas reglas se extieden para proporcionar relgas especiales para los eventos que nuestro sistema pueda lanzar (usualmente en aplicaciones stand alone o de escritorio) Un evento constituye un mtodo para que una clase notifique a los usuarios de un objeto que algo interesante sucede al objeto, como, por ejemplo, que se ha hecho clic en un control de una interfaz grfica de usuario. Los eventos JavaBean soportan especificaciones, que permiten a los componentes notificar a otros cuando algo sucede. El modelo de eventos se utiliza a menudo en aplicaciones de interfaz grfica de usuario (GUI) cuando un evento como un clic del ratn es de multidifusin a muchos otros objetos que pueden tener cosas que hacer cuando se produce el clic del ratn. Los objetos que reciben la informacin que se produjo un evento se llaman "listeners" (oyentes). Se necesita saber que los mtodos que se utilizan para aadir o eliminar los listeners de un evento tambin debe seguir las normas de nomenclatura JavaBean: Reglas para nombrar a los listeners:

Los nombres de los mtodos listeners usados para "registrar" un listener con un evento debe usar el prefijo " add" seguido del tipo de listener, por ejemplo: "addActionListener". Los nombres de los mtodos usados para "eliminar" un listener debe usar el prefijo "remove", seguido del tipo de listener. El tipo de listener que se registra o elimina debe ser pasado como argumento al mtodo. Los listeners deben terminar con la palabra " Listener".

Ejemplo de mtodos JavaBeans validos:


public public public public public

void setMyValue(int v) int getMyValue() boolean isMyStatus() void addMyListener(MyListener m) void removeMyListener(MyListener m)

Ejemplo de mtodos JavaBeans invalidos: void setNombreCliente(String s) // debe ser publico public void modificarMiValor(int v) // no puede usar 'modificar' public void addXListener(MyListener m) // No coinciden los tipos

Declaraciones de clases: Cuando escribimos cdigo Java escribimos clases o interfaces, dentro de estas escribimos las variables y mtodos. La forma en la que declaremos las clases, variables y mtodos tiene implicaciones en el comportamiento de nuestro cdigo, por ejemplo, cuando declaramos una clase como public (publico) esta clase puede ser accedida o vista desde cualquier clase dentro de nuestra aplicacin.

Reglas de declaracin de archivos de cdigo fuente:

10

Slo debe haber una clase declarada pblica dentro de un archivo de cdigo. Los comentarios pueden ir en cualquier lnea del cdigo. Si tenemos una clase declarada publica en un archivo, el nombre del archivo debe ser el mismo de la clase pblica, por ejemplo, para la clase "public class Empleado" el archivo debe tener el nombre "Empleado.java". Si la clase forma parte de un paquete, la declaracin de este paquete debe ir primero antes que la declaracin de la clase. Si no se manejan paquetes y se importan clases, con la palabra reservada "import" (importaciones) estos "import" debe declararse antes que la clase. Si no se tienen ni paquetes ni importaciones la declaracin de la clase debe aparecer en la primera lnea (aunque pueden ir comentarios antes de esta). Las importaciones y los paquetes se aplican para todas las clases declaradas en un archivo, no hay forma de que declares mltiples clases en un archivo y pertenezcan a diferentes paquetes o usen diferentes importaciones. Un archivo puede tener ms de una clase no pblica.

Declaracin de clases y modificadores Los

modificadores

se

dividen

en

dos

categoras:

Modificadores de acceso: public, private, protected. Modificadores de no acceso: algunos como final, static, abstract, etc. Primero debemos de tener en mente que con los modificadores de acceso podemos establecer si una clase puede ser accedida o no por un determinado recurso. Existen 4 niveles de acceso pero 3 modificadores de acceso. El cuarto nivel de acceso de control es el usado por defecto, que se establece cuando no declaramos ningn modificador de acceso a nuestra clase, es decir, todas las clases, variables y mtodos cuentan con un control de acceso. Los cuatro niveles de acceso funcionan para las variables y mtodos pero una clase solo puede ser declarada pblica o por defecto .

11

Acceso a las clases: Cuando decimos que una clase "A" tiene acceso a una clase "B", esto significa 3 cosas: Que la clase "A" puede crear una instancia de la clase "B". La clase "A" extiende de la clase "B", es decir es una sub clase de la clase "B". La clase "A" puede acceder a ciertos mtodos y variables de la clase "B", dependiendo de las controles de acceso que tengas estos en la clase "B".

Acceso por defecto o default:


Una clase con acceso por defecto es la que no tiene ningn modificador de acceso en su declaracin. Pensemos en el acceso por defecto como un nivel de acceso de paquete, es decir, una clase con acceso por defecto solo puede ser accedida desde otra clase en el mismo paquete. Por ejemplo si tenemos dos clases " A" y "B", que estn en diferentes paquetes y ambas tienen el nivel acceso por defecto (es decir, no tienen declarado ningn modificador de acceso), la clase " B" no puede crear una instancia de la clase "A" (ya que no puede verla), y la clase "A" ni siquiera sabe que la clase "B" existe (ya que tampoco puede verla). Por ejemplo, si tenemos las siguientes clases:

package autos; class Toyota{ } Y en un segundo archivo tenemos: package aviones; import autos.Toyota; public class Boing extend Toyota { } Como podemos, ver la clase "Toyota" y la clase "Boing" se encuentran en diferentes paquetes. La importacin en la parte superior de clase "Boing" est tratando de importar a la clase " Toyota", la clase "Toyota" compila bien, pero cuando tratamos de compilar la clase "Boing" tendremos un error como este:

12

Can't access class autos.Toyota Class or interface must be public, in same package, or an accessible member class. import autos.Toyota; La clase "Boing" no puede acceder a la clase " Toyota", ya que esta clase esta con el nivel de acceso por defecto y se encuentran en diferentes paquetes. Para lograr nuestro cometido podemos realizar 2 cosas: incluir a las dos clases en el mismo paquete o declarar a la clase "Toyota" como pblica. Acceso pblico: Una clase declarada como pblica puede ser accedida desde cualquier lugar de nuestra aplicacin . No debemos olvidar que para poder acceder a una clase publica desde otro paquete tenemos que importar esta clase pblica. Otros modificadores o modificadores de no acceso (Non Access Modifiers): Los modificadores de no acceso ( non access modifiers) sirven para un propsito distinto al de los modificadores de acceso, sirven para lograr diferentes funcionalidades, en resumen y para mencionar algunos explicaremos primero brevemente algunos de los modificadores de no acceso para dar una idea un poco ms claro de estos: El modificador static se aplica a los mtodos de clase y las variables para convertirlos en mtodos o variables de clases, y no de instancia. El modificador final se aplica a las clases, mtodos y variables para evitar que sean extendidos, sobreescritos o cambiados (en resumen modificados de alguna forma). El modificador abstract para crear clases y mtodos abstractos.

Hemos mencionado solo algunos de los modificadores de no acceso, mas adelante abarcaremos en ms detalle todos estos modificadores.

13

Como podemos ver algunos modificadores solo se pueden aplicar a mtodos y clases otros a clases, mtodos y variables, todo esto lo veremos a continuacin. Una clase puede tener slo los siguientes modificadores de no acceso:

final abstract strictfp

Estos modificadores son adicionales a los modificadores de acceso y podemos usarlos en combinacin con estos. Podemos, por ejemplo, declarar una clase como "public final". Sin embargo no siempre podemos combinar estos los modificadores de no acceso, como por ejemplo nunca debemos declarar una clase " abstract final", ya veremos porque ms adelante. Clases declaradas con el modificador final: Una clase declarada con el modificador " final" significa que no se pueden crear subclases a partir de ella. Un ejemplo de una clase declarada "final" es cuando queramos tener la absoluta garanta que los mtodos y variables de esta clase no puedan ser sobre escritos por otra clase. Una de las ventajas de tener clases no " final" es este escenario: imaginen que encontramos un problema con un mtodo en una clase que estamos utilizando, pero no tenemos el cdigo fuente, as que no podemos modificar el cdigo fuente para mejorar el mtodo. Pero se puede extender la clase y reemplazar el mtodo en su nueva subclase, y sustituir en todas partes la superclase original. Si la clase es "final", sin embargo, entonces estamos perdidos. Modificaremos nuestro ejemplo anterior: package autos; public final class Toyota{ }

14

Si ahora intentamos extender la clase " Toyota", que est marcada como "final", con la siguiente clase que tenemos en un segundo archivo: package aviones; import autos.Toyota; public class Boing extend Toyota { } Obtendremos un error como el siguiente Can't subclass final classes: class class Boing extends Toyota{ 1 error autos.Toyota

Tenemos este error ya que la clase "Toyota" tiene el modificador "final" (no puede tener subclases) y aun as la clase " Boing" est tratando de extenderla. Clases abstractas: Una clase abstracta nunca puede ser instanciada (osea que no podemos crear instancias de ella usando el operador " new"), su nico propsito, misin en la vida, su razn de ser, es ser extendida. Podemos usar una clase abstracta, por ejemplo, si tenemos una clase "Carro" que tiene los mtodos que deben ser implementados en todos los tipos de carros que extienden de l. Un ejemplo de clase abstracta es la siguiente: abstract class Carro { private double precio; private String modelo; private String anio; public abstract void irRapido(); public abstract void irLento();
// aqu va codigo adicional, importante y serio

15
}

En donde hemos declarado que la clase " Carro" es una clase "abstract", adems tiene dos mtodos que hemos marcado tambin como "abstract": "irRapido" e "irLento". El ejemplo anterior compila bien, pero si intentamos instanciar esta clase abstracta de la siguiente forma: Carro x = new Carro(); Obtendremos el siguiente error: AnotherClass.java:7: class Car is an abstract class. It can't be instantiated. Carro x = new Carro(); 1 error Notemos que los mtodos abstractos declarados terminan en punto seguido (;) en lugar de llaves ({}). Si un mtodo es declarado como abstracto, tanto el mtodo como la clase deben ser declarados abstractos, o sea que una clase debe ser abstracta si por lo menos uno de sus mtodos es abstracto. Si cambiamos los mtodos de abstracto a no abstracto no olvidemos de cambiar el punto y como al final por un par de llaves. Declaracin de interfaces: Cuando declaramos una interface es como si declarramos un contrato qu debe de hacer la clase sin decir como lo har. Si, por ejemplo, declaramos una interface " Rebotable" con sus mtodos "rebotar" y "setFactorRebote", la clase que implemente esta interface tiene la obligacin de implementar los mtodos " rebotar" y "setFactorRebote". Una interface puede ser implementada por cualquier clase. Podemos tener dos clases diferentes que extiendan de clases diferentes y que no estn relacionadas de ninguna forma, pero al decir que estas clases implementan la interfaz " Rebotable" queremos decir que estas dos clases deben de implementar los mtodos " rebotar" y

16

"setFactorRebote". Como dijimos al inicio: una interfaces es una clase 100% abstracta como mostramos en el siguiente ejemplo: Lo que nosotros declaramos: interface Rebotable { void rebote(); void setFactorRebote(int fr); } Lo que el compilador ve: interface Rebotable { public abstract void rebote(); public abstract void setFactorRebote(int fr); } Lo que la clase que implementa la interface debe hacer public class Pelota implements Rebotable { public void rebote(){}; public void setFactorRebote(int fr){}; } Mientras una clase abstracta puede definir mtodos abstractos (que deben ser implementados en la clase que utiliza esta clase abstracta) y mtodos no abstractos, una interface solo puede definir mtodos abstractos. Como todas las cosas que vimos anteriormente, las interfaces tambin cuentan con reglas especificas para su correcta implementacin: Todos los mtodos de una interfaces son implcitamente "public" y "abstract" (pblicos y abstractos), es decir, no es necesario escribir explcitamente estos dos modificadores, pero el mtodo sigue siendo pblico y abstracto.

17

Todas las variables definidas en una interface deben ser pblicas, estticas y finales ("public", "static", "final"), en otras palabras, las interfaces solo declaran constantes, no variables de instancia. Los mtodos de una interface NO deben ser estticos ("static"). Ya que los mtodos de una interface son abstractos, no pueden ser marcados con los modificadores "final", "strictfp" y "native". Una interface puede extender a una o ms interfaces. Una interface no puede implementar a otras interfaces. Una interfaz debe ser declarada con la palabra reservada "interface". Escribir una interface con el modificador " abstract" resulta redundante ya que al igual que sus mtodos una interface es implcitamente abstracta lo indiquemos o no, por ejemplo:

public interface Rebotable{} public abstract interface Rebotable{} Las dos declaraciones anteriores son legales.

Al igual que las declaraciones de las interfaces, los mtodos de las mismas no tienen necesidad de ser declarados como abstractos, ya que tambin resulta redundante porque estos son implcitamente abstractos y pblicos, por ejemplo: void rebote(); public abstract void rebote(); Estas dos declaraciones tambin son legales. Ejemplo de declaraciones legales: void rebote();

18

public void rebote(); public abstract rebote(); abstract public rebote(); Ejemplo de declaraciones no legales: final void rebote(); static void rebote(); private void rebote(); protected void rebote(); Declarando constantes en una interfaz: Una interface permite declarar constantes, la clase que implemente esta interfaz tiene acceso a las variables de la interfaz implementada como si se tratara de una relacin de herencia. Hay que recordar una regla para declarar constantes: estas deben de ser pblicas, estticas y finales ("public", "static" y "final"), pero no debemos preocuparnos de escribir explcitamente estos modificadores ya que, al igual que las clases y los mtodos, son implcitamente pblicos y abstractos, las variables de una interfaz son implcitamente pblicos, estticos y finales, lo escribamos o no. Pero hay que tener en cuenta lo siguiente: una constante una vez que se le es asignado un valor, este valor no puede ser modificado nunca, por ejemplo: public interface Rebotable { int NUMERO_CONSTANTE = 5; } En otro archivo public class Pelota implements Rebotable {

19

public void modificaNumeroConstante() { NUMERO_CONSTANTE = 8; }

Como dijimos anteriormente una constante una vez asignado el valor no puede cambiar, es decir NUMERO_CONSTANTE = 8; no compilar ya que al declararlo en la interfaz " Rebotable" se convierte en una constante y esta no puede ser modificada como se est tratando de hacer en la clase "Pelota". Declaracin de miembros de clase: Anteriormente vimos las reglas necesarias y a tener en cuenta para poder declarar correctamente una clase. Ahora veremos lo que se debe hacer para declarar correctamente una variable y los mtodos de una clase. Modificadores de acceso: Como los mtodos y las variables son accedidos de la misma forma, hablaremos de los dos en esta seccin. Recordemos que cuando declaramos una clase solo podemos usar dos de los cuatro niveles de acceso ( public y por defecto), con los miembros de clase (mtodos y variables) podemos usar los 4 niveles de acceso:

public protected private Por defecto

Recordemos que el acceso por defecto es cuando no se escribe explcitamente un modificador de acceso, o sea, no ponemos nada. Podemos acceder a los miembros de una clase desde otra clase de dos maneras:

20

La primera consiste en usar el operador punto ( .) para invocar a un mtodo o una variable, por ejemplo: public class Auto { public void muevete() { System.out.println("Me estoy moviendo!!!"); } } class Perro { public void usandoMueveteDeAuto() { Auto auto = new Auto();//podemos hacer esto porque tenemos acceso a la clase "Auto"
System.out.println("El perro dice" + auto.muevete());//podemos acceder al mtodo muevete porque este mtodo es publico } }

La segunda forma de acceder a los miembros de una clase es a travs de la herencia, por ejemplo: public class Auto { public void muevete()
} { System.out.println("Me estoy moviendo!!!"); }

class Perro extends Auto { public void usandoMueveteDeAuto() { System.out.println("El perro dice" + this.muevete()); //podemos hacer esto por que heredamos los mtodos publicos Auto auto = new Auto();//podemos hacer esto porque tenemos acceso a la clase Auto

21
System.out.println("El perro vuelve a decir" + auto.muevete());//podemos acceder al mtodo muevete porque este mtodo es publico } }

Acceso pblico: Cuando una variable o mtodo es declarado con el nivel de acceso pblico quiere decir que este puede ser accedido desde cualquier clase, independientemente del paquete en que est (asumiendo que la clase a la cual pertenece es visible). Como se muestra en la siguiente imagen:

Tambin podemos imaginar que la clase " AmoDelPerro" se encuentra en un diferente paquete que las 2 clases anteriores y no tendra ningn problema, siempre y cuando la clase como el mtodo a

22

la cual hace referencia sean pblicos (haciendo las importaciones necesarias). Acceso Privado: Los miembros de una clase marcados como privados no pueden ser accedidos desde ninguna otra clase que no sea la clase en la cual fue declarado, por ejemplo:

package transporteTerrestre; public class Auto { private void muevete() { //podemos poner cualquier cdigo aqu, pero solo la clase "Auto" la conocer
System.out.println("Me estoy moviendo!!!"); } }

Y en otro archivo declaramos: package transporteAereo; import transporteTerrestre.Auto public class Avion { public void vuela() { //Accedemos a la clase publica Auto auto = new Auto();

Auto,

porque

esta

es

System.out.println("Primero tiene que moverte" + auto.muevete()); //Error de compilacin } }

23

Si tratamos de ejecutar este cdigo obtendremos un error como el siguiente: Cannot find Symbol Symbol: method muevete(); Esto sucede ya que el compilador interpreta como si el mtodo "muevete()" no existiera, ya que un mtodo declarado como privado no existe para ninguna clase excepto para su propia clase. Algo similar sucede con la herencia: si un mtodo en la sper clase es declarado como privado este mtodo no puede ser heredado por la sub-clase; sin embargo, podemos declarar un mtodo con la misma firma en la subclase, no importa cmo se vea, este mtodo no est sobre escribiendo al de la sper clase (la sobre escritura depende de la herencia) es simplemente un mtodo que tiene el mismo nombre que el de la sper clase. package transporteTerrestre; public class Auto {

private void muevete() { //podemos poner cualquier cdigo aqu, pero solo la clase //Auto la conocer System.out.println("Me estoy moviendo!!!"); } }

Ahora el mtodo "muevete" se limita a la clase "Auto" incluso en el mismo paquete: package transporteTerrestre; public class Toyota extends Auto //Toyota y Auto se encuentran en el mismo paquete y la sper clase Auto es publica { private void hazAlgo() {

24

System.out.println(muevete()); compilacin } }

//Error

de

Esto

se

ve

ms

claramente

en

la

siguiente

imagen:

25

Miembros protegidos y por defecto: Los niveles de acceso protegidos y por defecto son muy parecidos pero tienen una gran diferencia. El miembro de una clase que tiene el nivel de acceso por defecto puede ser accedido por cualquier clase slo dentro del mismo paquete, los miembros con nivel de acceso protegido ("protected") tambin pueden ser accedidos desde otro paquete pero solamente por medio de la herencia. Veamos un ejemplo con las siguientes dos clases que se encuentran en diferente paquete.

package ejemplo; public class Auto

26

{ void muevete() { System.out.println("Me estoy moviendo"); } } package import acceso; //Diferente paquete ejemplo.Auto;

public class AccesandoAuto { public static void main (String[] args)


{ Auto auto = new Auto(); auto.muevete(); }

Como podemos apreciar, la clase " Auto" se encuentra en un paquete llamado "ejemplo" y tiene su mtodo llamado " muevete" que esta con el nivel de acceso por defecto (no est declarado explcitamente alguno modificador de acceso), despus tenemos la clase "AccesandoAuto" el cual est en un paquete diferente llamado "acceso", como dijimos anteriormente: debemos pensar en el nivel de acceso por defecto como un acceso a nivel de paquete y como el mtodo "muevete" se encuentre en un paquete diferente al tratar de ejecutarlo obtendremos el siguiente error: ERROR muevete() is not public in ejemplo.Auto; cannot be accessed from outside package Los niveles de acceso por defecto y protegidos solo difieren cuando hablamos de herencia (sub clases). Si usamos el nivel de acceso "protected" (protegido) para declarar a un miembro de clase, este miembro de clase puede ser accedido desde cualquier subclase no importando si la sper clase y la sub clase se encuentren en diferente paquete, los miembros de la sper clase siempre son visibles por las sub clases. En contraste con el nivel de acceso por defecto una subclase no puede acceder a los miembros de la sper clase a menos

27

que estn en el mismo paquete, el nivel de acceso por defecto no tiene ninguna consideracin para con la herencia. Para resumir: cuando pienses en acceso por defecto piensa en un acceso a nivel de paquete (sin excepciones) y cuando pienses en el nivel de acceso protegido piensa en un nivel de acceso por paquete y tambin de padre a hijo cuando hablamos de diferentes paquetes, para aclarar otro punto, si la subclase intenta acceder a la sper clase con el miembro protegido a travs de una instancia de la sper clase esto no es posible, aclarando por ltima vez: Solo es a travs de la herencia. Detalles de acceso a miembros de clase protegido: package ejemplo; public class Auto { protected int numero = 5; } El cdigo anterior declara una variable llamada " numero" con nivel de acceso protegido, como dijimos anteriormente esta variable solo puede ser accedido por clases dentro del paquete " ejemplo" y por clases fuera de ese paquete slo a travs de la herencia. Ahora creemos otra clase en otro paquete: package acceso;//Diferente paquete import ejemplo.Auto; public class AccesandoAuto extends Auto {
public void metodo () { System.out.println("El valor de numero es: " + numero) } }

El ejemplo anterior compila bien ya que estamos accediendo a un miembro de clase protegido en diferente paquete pero a travs de la herencia, pero qu sucede si intentamos acceder al miembro de la subclase a travs de una instancia de la sper clase?

28

package acceso;//Diferente paquete import ejemplo.Auto; public class AccesandoAuto extends Auto {
public void metodo() { System.out.println("El valor de numero: " + numero); Auto auto = new Auto();

System.out.println("El valor de numero: " + auto.numero); // Error de compilacin } }

En cdigo anterior tenemos un error en la lnea: System.out.println("El auto.numero); El error es el siguiente: Uncompilable source code access in ejemplo.Auto numero has protected valor de numero es: " +

at acceso. AccesandoAuto.metodo

Ya que a pesar de estar en una clase que hereda a la sper clase con miembro de clase protegido, y espero decirlo por ltima vez: SOLO ES VISIBLE A TRAVES DE LA HERENCIA. Detalles de miembros de clase con nivel de acceso por defecto:

29

Empezaremos con el nivel de acceso por defecto en la herencia, en el siguiente ejemplo modificaremos nuestra variable y le pondremos nivel de acceso por defecto, es decir, no le pondremos explcitamente un modificador de acceso. package ejemplo; public class Auto { int numero = 5; } Qu sucede si la clase padre y la clase hijo se encuentran en diferente paquete y en la clase padre tenemos una variable con nivel de acceso por defecto? package acceso; //Diferente paquete import ejemplo.Auto; public class AccesandoAuto extends Auto { public void metodo() { System.out.println("El valor de numero es: " + numero); // Error de compilacin Auto auto = new Auto(); System.out.println("El valor de numero es: " + auto.numero); // Error de compilacin
} }

El error es el siguiente numero is accessed not public in from outside ejemplo.Auto; cannot be package at acceso.

AccesandoAuto.metodo

En estos dos casos obtendremos el error descrito anteriormente ya que las variables de clase declaradas con el nivel de acceso por defecto solo son visibles dentro de las clases que se encuentren en el mismo paquete, incluso si hacemos uso de la herencia.

30

Variables locales y modificadoras de acceso Antes de todo debemos aclarar que se llaman variables locales a las variables declaradas dentro de un mtodo. Estas variables locales NUNCA deben ser declaradas con algn modificador de acceso, por ejemplo, algo como esto es incorrecto: package ejemplo; public class Auto { public void metodo()
{ private int numero; } }

El cdigo anterior nos dara el siguiente error Uncompilable source code illegal expression at ejemplo.Auto.metodo start of

Para resumir lo visto hasta el momento vamos a mostrar una tabla donde se explica los niveles de acceso: Visibilidad Desde la misma clase Public Si Protectec Si Si Si Si (slo herencia) No por Default Si Si Si No No Private Si No No No No

Desde cualquier clase dentro del mismo Si paquete Desde una subclase Si en el mismo paquete Desde una subclase Si desde otro paquete Desde distinta paquete una clase fuera del Si

31

Miembros de clase con modificadores de no acceso: Primero veremos este tipo de modificadores en los mtodos despus veremos estos mismos pero aplicados a variables de instancia.

Mtodos marcados con el modificador final: Los mtodos marcados con este modificador no pueden ser sobre escritos por una subclase, muchos mtodos de la API de Java no puedes ser sobre escritos ya que estn marcados con el modificador "final", como ejemplo veamos esta clase: public class SuperClase { public final void metodo()
}

{ System.out.println("Esto es un metodo"); }

No hay ningn problema en extender una clase, ya que no est marcada con el modificador final, pero un mtodo marcado con este modificador no puede ser sobre escrito. public class SubClase extends SuperClase {
public void metodo()// Tratamos de sobre escribir el metodo final de la super clase { System.out.println("Esto es un metodo"); } }

Al intentar ejecutar este cdigo obtendremos un error como este: The method void metodo () declared in class SubClass cannot override the final method of the same signature declared in class SuperClass. Final methods cannot be overridden.

32

public void metodo () { } 1 error Como dijimos anteriormente: los mtodos marcados con el modificador "final" no pueden ser sobre escritos. Argumentos marcados con el modificador final Los argumentos de los mtodos son los que aparecen entre los dos parntesis de un mtodo, por ejemplo: public void metodo(String argumento){} Y un mtodo con declarado con mltiples argumentos se ve de la siguiente manera: public void argumentoDos){} metodo(String argumentoUno, int

Los argumentos de un mtodo son esencialmente lo mismo que las variables locales, nosotros podemos marcar a los argumentos de un mtodo con el modificador "final", por ejemplo: public void metodo(String argumentoUno, final argumentoDos){} int

En el ejemplo anterior el argumento de nombre " argumentoDos" est marcado con el modificador "final", lo que quiere decir que este valor no puede ser cambiado dentro del mtodo, en otras palabras, el valor no puede ser otro que el valor que se le paso al mtodo al momento de ser llamado. Mtodos abstractos Un mtodo abstracto, como aclaramos anteriormente, es un mtodo que debe ser declarado pero no implementado y si recordamos lo dicho anteriormente un mtodo abstracto no tiene llaves (que es donde se coloca la lgica del mtodo), simplemente termina en punto y coma.

33

Declarando un mtodo como abstracto obligamos a las subclases que heredan de la clase abstracta a la cual pertenece a implementar estos mtodos. Por ejemplo si tenemos un mtodo llamado " muevete" en la sper clase "Auto", las sub clases que extiendan a esta clase estn obligadas a implementar el mtodo "muevete" en ellas. Tambin debemos recordar que si un mtodo es definido como abstracto la clase a la cual pertenece tambin tiene que ser abstracta, es decir, el cdigo siguiente es incorrecto public class SuperClase // La clase tambin debera ser marcada como abstracta { public abstract void metodo(); // metodo abstracto } El cdigo anterior nos arroja el siguiente error: SuperClase is not abstract and does not override abstract method metodo. Tambin debemos recordar que una clase abstracta puede contar con mtodos no abstractos (de los normalitos, llamados concretos) y abstractos, por ejemplo el siguiente cdigo es correcto: public abstract class SuperClase { public abstract void metodo(); // metodo abstracto
} public void otroMetodo(){ } // metodo no abstracto

No olvidemos que los mtodos no abstractos deben llevar llaves. Ahora prestemos atencin a una cosa, si tenemos una clase abstracta que a su vez hereda de otra clase abstracta, esta clase abstracta hija no tiene la necesidad de implementar los mtodos abstractos de la clase abstracta padre, pero en algn momento tendremos que declarar una clase concreta y esta clase concreta tendr que implementar TODOS los mtodos abstractos en el rbol de herencia

34

que no han sido implementados entre las clases abstractas, mejor veamos esto en un ejemplo: public abstract class Auto { private String tipo; public abstract void acelera(); // Metodo abstracto
public String getTipo() // Metodo no abstracto { return tipo; } } public abstract class Carro extends Auto

public abstract void acelera(); // Sigue abstracto public void hazAlgo() { // logica del metodo } } public class Toyota {
public void acelera () { // logica del metodo } }

extends Carro

La clase "Toyota" hereda los mtodos no abstractos " hazAlgo" y "getTipo" del rbol de herencia y tiene la necesidad de implementar el mtodo "acelera" de la sper clase "Auto" ya que este nunca fue implementado en la clase "Carro" por lo que sigue declarada abstracta en esta clase. Si tenemos mtodos abstractos que han sido implementados en las subclases abstractas no tenemos la necesidad de implementar estos mtodos, por ejemplo: public abstract class Auto { private String tipo;

35

public abstract void acelera(); // Metodo abstracto


public String getTipo() // Metodo no abstracto { return tipo; } public abstract void frena();// Metodo abstracto } public abstract class Carro extends Auto

public abstract void acelera(); // Sigue abstracto public void hazAlgo() // Metodo no abstracto { // logica del metodo } public void frena()//implementando mtodo abstracto { // logica del metodo } public abstract void atropella();//mtodo abstracto } public class Toyota extends Carro {

public void acelera() //implementacin obligatoria { // logica del metodo } public void atropella()//implementacin obligatoria { // logica del metodo } public void frena()// implementacin opcional { // logica del metodo }

36

Como podemos observar, la clase abstracta " Auto" cuenta con dos mtodos abstractos ("acelera" y "frena"). La clase abstracta "Carro", que extiende de "Auto", cuenta con un mtodo abstracto propio ("atropella"), adems podemos observar que implementa un mtodo abstracto de la sper clase abstracta (el mtodo " frena"), por lo cual la clase concreta "Toyota" tiene la obligacin de implementar los mtodos abstractos que no han sido implementados en el rbol de herencia (el mtodo abstracto " acelera" en la clase "Auto" y el mtodo abstracto "atropella" en la clase "Carro"). Para aclarar otro punto, el mtodo que implementa un mtodo abstracto heredado tiene que ser exactamente el mismo, veamos esto en otro ejemplo: public abstract class Carro { public abstract void acelera(); // metodo abstracto } public class Toyota extends Carro { public void acelera(String mensaje) { // logica del metodo } } Aunque parezca que el mtodo " acelera" en la clase "Toyota" est implementando el mtodo abstracto de la sper clase " Carro", esto no es as, lo que est haciendo el mtodo de la clase " Toyota" es sobrecargar el mtodo, lo cual no cumple con las reglas necesarias para que se considere que el mtodo " acelera" en la clase "Toyota" est implementando el mtodo abstracto de la sper clase " Carro". Para terminar debemos aclarar que un mtodo abstracto no puede ser marcado como "final" o "private" ya que obviamente, como dijimos anteriormente, un mtodo abstracto est destinado a ser implementado por la sub clase que lo herede, y marcarlo como "final" significa que no puede ser sobre escrito y " private" que no

37

puede ser visto por otra clase, lo cual tambin impide una posible implementacin Tambin debemos aclarar que un mtodo abstracto no puede ser marcado como "static", a continuacin presentamos declaraciones no legales de mtodos abstractos: private abstract void acelera(); final abstract void acelera(); abstract static void acelera(); Al ejecutar el cdigo anteriormente escrito nos dar el siguiente error de compilacin: Illegal static combination of modifiers: abstract and

Mtodos sincronizados Un mtodo marcado con el modificador "synchronized" quiere decir que este mtodo solo puede ser accedido por un hilo ( Thread) a la vez, solamente los mtodos pueden ser marcados como sincronizados, no variables, no clases SOLO MTODOS, un mtodo sincronizado se declara de la siguiente manera: public synchronized void acelera(); Tambin debemos tener en cuenta que un mtodo marcado como sincronizado puede tener cualquiera de los 4 niveles de acceso Mtodos nativos: Slo los mtodos pueden ser marcados como nativos (" native"), un mtodo nativo termina en punto y coma como los mtodos abstractos indicando que la implementacin de dicho mtodo es omitida. Los mtodos nativos NO son implementados usando cdigo Java, sino que hacen uso de algn otro lenguaje que genera cdigo "nativo" para el dispositivo en el que se ejecutar nuestra aplicacin, como C o C++. Mtodos scrictpf

38

Con "strictfp", se puede predecir cmo se comportarn los puntos flotantes, independientemente de la plataforma subyacente de laJVM que se est ejecutando. La desventaja es que si la plataforma subyacente es capaz de soportar una mayor precisin, un mtodo "strictfp" no ser capaz de tomar ventaja de ello. Bueno, como dijimos anteriormente: una clase puede ser marcada con este modificador ("scrictpf") y esto tambin funciona con los mtodos, no hay necesidad de marcar la clase con " scrictfp" para que declaremos mtodos con este modificador, tambin debemos recordar que una variable nunca debe ser marcada con este modificador. Mtodos con una lista variable de argumentos (var-args) Desde la versin 5.0 de Java se permite crear mtodos que pueden tener una cantidad variable de argumentos con un mecanismo llamado "var-args", esto es muy til cuando tenemos mtodos que reciben como una cantidad variable o grande de parmetros de un tipo especifico, con var-args solo es necesario definir el tipo de nombre y el nombre del parmetros para que esta pueda recibir una cantidad variable de argumentos del tipo declarado, tambin sigue un conjunto de reglas que se explicaran a continuacin: Primero vamos a aclarar la diferencia entre argumentos y parmetros: Argumentos: son las cosas que especificamos entre los parntesis cuando invocamos a un mtodo, por ejemplo: mensaje("El mensaje es: ", "Hola mundo");//invocando al //mtodo mensaje, "El mensaje es:" y "Hola mundo" son argumentos Parmetros: Las cosas que ponemos en un mtodo al declararlo, que determina qu es lo que el mtodo debe recibir cuando sea invocado, por ejemplo mensaje(String palabraUno, String palabraDos){} //especificamos que el mtodo recibir 2 Strings cuando sea invocado: palabraUno y palabraDos

39

Ahora vamos a establecer las reglas correctamente un parmetro varargs:

para

poder

declarar

Cuando declaremos un parmetro var-args debemos especificar el tipo de argumento(s) que el mtodo debe recibir, este tipo puede ser un tipo primitivo o un objeto. Para declarar correctamente un var-args primero debemos especificar el tipo que este ser seguido de tres puntos ( ...), un espacio y el nombre que llevar esta lista de parmetros en su conjunto. Es legal tener otros parmetros en un mtodo que usa un varargs. El var-args siempre debe ir como parmetro final y solamente se puede declarar un var-args en un mtodo. Al declarar un var-args decimos que podemos pasar cero o ms argumentos del tipo declarado al invocar al mtodo. Ejemplos de declaraciones correctas de var-args: public void mensaje(String... mensajes){}; public void mensaje2(int numeroMensajes, String... mensajes){}; public void numeroAutos(Auto... autos){}; Ejemplos de declaraciones incorrectas de var-args: public void mensaje(String mensajes...){}; public void mensaje2(String... numeroMensajes){}; public void mensaje3(String... numeroMensajes){}; mensajes, mensajes, int int...

Las declaraciones anteriores son incorrectas porque no siguen las reglas especificadas anteriormente como que los tres puntos seguidos (...) van entre el tipo de dato y el nombre que llevar (primer caso),

40

que los var-args siempre van como parmetro final (segundo caso) y que slo se puede declarar un var-args en un mtodo, uno y slo uno (tercer caso). Tambin es posible usar var-args donde normalmente usamos arreglos (ya que se trabajan de la misma forma) siempre y cuando se sigan las reglas indicadas anteriormente. Por ejemplo, normalmente declaramos nuestro mtodo "main" de la siguiente forma:

public static void main(String[] args) { //codigo } Usando var-args podemos declararlo de la siguiente forma:

public static void main(String... args) { //codigo } La cual tambin es una declarcin vlida. Declaracin de constructores Cada vez que creamos un nuevo objeto, usando el operador " new", el constructor del objeto es invocado. Todas las clases tienen un constructor, independientemente de que lo declaremos o no. Si no declaramos explcitamente un constructor para una clase, el compilador construir uno por defecto, veamos un ejemplo: public class Carro { public Carro(){} // este es un constructor public void Carro(){} // este es un mtodo con un nombre no muy adecuado, pero legal

41

Reglas y diferencias de los constructores (con los mtodos): La primera diferencia que debemos tener en cuenta entre los constructores y los mtodos es que los constructores nunca, nunca retornan un valor. Los constructores pueden ser declarados con cualquiera de los niveles de acceso. Un constructor puede tener argumentos, incluido los var-args. La gran regla de un constructor nos dice que este siempre debe tener el mismo nombre de la clase en la que es declarada . Los constructores no pueden ser declarados como " static", "abstract" o "final". Los constructores no se heredan.

Ejemplo de declaraciones legales e ilegales de constructores: Public class Auto { //Constructores legales Auto(){} private Auto(int numero){} Auto(int numero){} public Auto(int numero, String... tipos){} //Constructores ilegales void Auto(){} // esto es un mtodo Auto2(){} //No es un mtodo ni un constructor

static Auto(){} //No puede ser static final Auto(int numero){} // No puede ser final

42

abstract abstract

Auto(int

numero){}

//

No

puede

ser

Auto(String... tipos, int numero){} //mala sintaxis var-args } Declaracin de variables: Las variables son valores modificables, es decir, son nombres que representan un valor de cierto tipo y el valor asociado al nombre se puede variar dependiendo de cada caso. En Java contamos con dos tipos de variables: Variables primitivas: Una variable primitiva puede ser uno de los siguientes ocho tipos: "int", "char", "boolean", "byte", "short", "double", "float" y "long". Una vez que declaras una variable de tipo primitivo, podemos cambiar el valor, ms no el tipo, pero podemos asignarle valores de tipos equivalentes haciendo un cast, por ejemplo a un "int" podemos asignarle un valor " long" haciendo el cast apropiado: int i = (int)1000L; Variables de referencia: Es usada para hacer referencia a un objeto, una variable de referencia es declarada para que sea de un determinado tipo y este nunca puede ser cambiado. Puede ser usado para hacer referencia a cualquier objeto del tipo declarado o de un sub tipo del tipo que se declaro con un tipo compatible o equivalente a l, por ejemplo, tenemos una clase " Perro" que extiende de la clase "Animal" y podemos hacer esto. Animal animal = new Perro(); Declarando primitivos y rango de valores primitivos: Los valores primitivos pueden ser declarados como variables de clase,

43

parmetros de mtodo, variables de instancia o variables locales. Podemos declarar ms de una variable del mismo tipo en una sola lnea, por ejemplo: int numero; int numeroUno, numeroDos, numeroTres; Es importante saber que para los tipos enteros la secuencia de menor a mayor es "byte", "short", "int", "long", y que los "double" son ms grandes que los de tipo "float". Los seis tipos de nmeros en Java se componen de un nmero determinado de bytes de 8-bits cada uno, y son nmeros reales, lo que significa que puede ser negativo o positivo. El bit ms a la izquierda (la cifra ms significativa) se utiliza para representar el signo, donde 1significa negativo y 0 significa positivo, como se muestra en la siguiente figura:

La figura anterior nos muestra que de los 8 bits que se usan para representar un tipo de dato " byte" uno de estos bits se usan para representar el signo, y 7 para representar el valor de la variable. Lo que nos dice que un byte puede tener 27 posibles valores. Esto sumado al signo nos dice que el rango de valores de un byte es de -27 a 27-1 (este ltimo -1 ocurre porque el 0 se considera dentro de los nmeros positivos). Digmoslo de otra forma para que quede ms claro ya que el concepto en si es un poco extrao. El mnimo valor positivo que puede tener un byte es (representado en bits) 00000000, donde el primer 0 representa el signo (en este caso positivo). El mximo valor positivo delbyte sera 01111111 donde, nuevamente el

44

primer 0 representa el signo positivo. Por lo tanto tenemos que el rago positivo va desde00000000 hasta 01111111, o sea (traduciendolo a decimal) desde 0 hasta 127. Si brincaramos al siguiente nmero binario (10000000) ya estariamos colocando el ltimo bit en 1 lo que lo convertiria en un nmero negativo (en este caso -128) De la misma forma. El mnimo valor negativo que puede tener un byte es 10000000, donde el primer 1 representa el signo (en este caso negativo). Y el mximo valor negativo que puede tener es 11111111. O sea, va desde -128 hasta -1. O sea que un byte va desde -128 hasta 127.Cmo es esto posible? Pues esto es posible porque esto trabaja usando complemento a dos. La misma explicacin funciona para el resto de los tipos primitivos numricos. En la siguiente tabla mostramos los rangos de los nmeros primitivos existentes en java y su respectivo equivalente en bits, bytes y su rango mnimo y mximo: Tipo byte int long Bits 8 32 64 Bytes 1 2 4 8 4 8 Rango Inferior -27 (-128) -215 (-32768) -231 (-2147483648) Rango Superior 27-1 (127) 215-1 (32767) 231-1 (2147483647)

short 16

-263 (263-1 9223372036854775808 (9223372036854775807 ) ) n/a n/a n/a n/a

float 32 doubl 64 e

El rango de los nmeros de punto flotante es dificil de determinar (debido a que la cantidad de cifras despus del punto depende de la plataforma).

45

Para los tipos booleanos no hay un rango, solo pueden ser true o false. La cantidad de bits que se usan para representar estos valores depende de la implementacin de la mquina virtual, pero para efectos del exemen esto no es importante. El tipo char contiene un solo caracter unicode de 16 bits sin signo, o sea que sus valores van desde 0 hasta 65535 (a diferencia de losshort ya que estos ltimos usan un bit para el signo). Variables de instancia: Las variables de instancia son declaradas dentro de una clase pero fuera de algn mtodo y son inicializadas solo cuando la clase es instanciada. Las variables de instancia son los campos que diferencian a un objeto de otro, por ejemplo veamos los campos (variables de instancia) de nuestra clase "Auto":

public class Auto { // definiendo campos (variables de instancia) para Auto


private String color; private String marca;

El cdigo anterior nos dice que cada vez que instanciamos la clase "Auto" este tendr su propio "color" y su propia "marca", debemos tener en cuenta que "campo", "variable de instancia", "propiedad" o "atributo" significan lo mismo. A continuacin mostramos las reglas que debemos seguir para declarar correctamente estas variables de instancia:

Se puede usar cualquiera de los 4 niveles de acceso Puede ser marcado con "final"

46

Puede ser marcado con "transient" No puede ser marcado con "abstract" No puede ser marcado con "synchronized" No puede ser marcado con "strictfp" No puede ser macado con "native" No puede ser marcado con "static" porque de esa manera seria una variable de clase

Hasta el momento hemos aprendido que como declarar y que modificadores de acceso deben tener las clases, los mtodos, variables locales y las variables no locales (de instancia) para que sea correcta su declaracin, a continuacin mostramos una tabla en la que resumimos todo lo aprendido hasta el momento: CLASES VARIABLES LOCALES VARIABLES LOCALES NO MTODOS

final final static public final abstract final transient volatile protected private public static abstract native syncrhronized strictfp public private protected A continuacin vamos a hablar ms detalladamente sobre las variables locales:

47

Variables locales: Como dijimos anteriormente las variables locales son las variables que se declaran dentro de un mtodo. El ciclo de vida de estas variables empieza dentro del mtodo y termina cuando el mtodo ha sido completado. Las variables locales siempre se encuentran en el " stack" (la seccin de memoria que mantiene los valores de las variables para mtodos particulares), no en el "heap" (la seccin de memoria en donde se crean los objetos). Aunque el valor de una variable podra ser pasado a, digamos, otro mtodo que almacene ese valor en una variable de instancia la variable en s, vive solo dentro del mbito del mtodo. Solo no olviden que mientras la variable local se encuentra en el stack, si la variable es una referencia a un objeto, el objeto mismo seguir siendo creado en el heap, no existe una cosa como un objeto en el stack, solo una variable en el stack. Algunas veces escucharemos a programadores usar el trmino "objeto local" pero lo que en realidad quieren decir es "una variable de referencia declarada localmente" As que si escuchan a un programador usar esa expresin, sabrn que tan solo es flojo como para decir la frase en una forma tcnicamente precisa. En las declaraciones de las variables locales no se pueden utilizar la mayora de los modificadores que se pueden aplicar a las variables de instancia, como "public" (o los modificadores de acceso de otro tipo), "transient", "volatile", "abstract", o "static", sino como hemos visto en la tabla mostrada anteriormente, las variables locales pueden ser marcadas con el modificador " final", por ejemplo veamos un ejemplo de variables locales: public class Ejemplo { public void unMetodo()
{ int numero = 5; } }

Normalmente deberamos inicializar una variable local en la misma

48

lnea en que la declaramos, aunque tambin podramos inicializarla ms adelante en el mtodo. Lo que siempre debemos de recordar es que una variable local debe ser inicializada antes de ser usada. Si no hacemos esto, el compilador rechazara y marcara como error cuando intentemos usar una variable local sin antes haberle asignado un valor, ya que, a diferencia de una variable de instancia, la variable local no tiene un valor por defecto. Al compilar un mtodo que utiliza una variable con un valor sin inicializar obtendremos el siguiente error que nos dice que la variable en cuestin no ha sido inicializada: variable nombreVariable initialized might not have been

Debemos recordar que una variable local no puede ser usada o referenciada fuera del mtodo en el que se declar, por ejemplo: public class Ejemplo { public void unMetodo()
{ int numero = 5; }

public void otroMetodo(int otroNumero) { numero = otroNumero;//Error, numero no puede ser usado fuera //del mtodo unMetodo() } }

Al compilar el cdigo anterior obtendremos el siguiente error ya que el compilador no conoce a esa variable local en otro lugar que no fuese dentro del mtodo donde fue declarado, el error es el siguiente: cannot find symbol Una variable local puede llevar el mismo nombre que una variable de instancia, a esto se le conoce como ensombrecimiento (shadowing), por ejemplo: public class Ejemplo { int numero = 8;

49

public void unMetodo() { int numero = 5; System.out.println(numero); } public void otroMetodo() { System.out.println(numero); } public static void main(String[] args)
{ new Ejemplo().unMetodo() //variable local new Ejemplo().otroMetodo() //variable de instancia } }

Si ejecutamos el cdigo anterior la salida correspondiente es: 5 8 Ahora veamos qu ocurrira si hacemos esto:

public class Ejemplo { int numero = 8;


public void unMetodo(int numero) { numero = numero; // que numero es igual a q numero? }

Para no tener este tipo de problema cuando declaramos el mismo nombre de la variable a la variable local (que ocurre por ejemplo tpicamente en los mtodos " setter") debemos usar la palabra reservada "this", tambin conocido como el apuntador "this" o la referencia "this". La palabra reservada "this" siempre hace

50

referencia al objeto que se est ejecutando y en este caso hace referencia a la variable de instancia, por ejemplo: public class Ejemplo { int numero = 8;
public void unMetodo(int numero) { this.numero = numero; // this.numero hace referencia a la //variable de instancia } }

Declaracin de arreglos: En Java, los arreglos son objetos que guardan mltiples variables del mismo tipo, o variables que son todas las subclases del mismo tipo. Los arreglos pueden contener ya sea primitivos o referencias de objetos, un arreglo siempre ser un objeto , incluso si el arreglo fuera contenedor de elementos primitivos. En otras palabras, no hay tal cosa como un arreglo primitivo, pero podemos hacer un arreglo de elementos primitivos. Declarando un arreglo de elementos primitivos: int[] arreglo;// (recomendado) los corchetes antes del nombre

int arreglo[];// los corchetes despus del nombre (legal pero menos legible) Declarando un arreglo de referencias de objetos: String []arreglo; String arreglo[];

Tambin podemos declarar arreglos multi-dimensionales, que en realidad son arreglos de arreglos y se declaran de la siguiente manera: String []arreglo[];

51

String [][][]arreglo; Nuca debemos indicar el tamao del arreglo en la declaracin de este, se debe declarar cuando se realiza la instanciacin del arreglo con el operador new, por ejemplo: String []arreglo = new String[10]; String []arreglo2; arreglo2 = new String[10]; El siguiente ejemplo nos arrojara un error ya que estamos asignando el tamao del arreglo en la declaracin de este: String [10]arreglo; Si intentamos cmpilar un cdigo con un arreglo declarado de la forma anterior obtendremos el siguiente error: Arreglos.java:5: not a statement String [10]arreglo;
^ Arreglos.java:5: ';' expected String [10]arreglo; ^ 2 errors

Variables marcadas con el modificador " final" Cuando declaramos una variable con el modificador final, garantizamos que esta variable no pueda ser reinicializada (cambiar su valor) una vez que su valor ha sido asignado explcitamente (explcitamente, no por defecto). Para los tipos primitivos esto significa que el valor nunca podr ser cambiado por ejemplo, si asignamos una variable llamada "PI" como double y marcamos con "final" asignndole un valor de "3.1416", este valor nunca podr ser cambiado. Cuando marcamos una variable de referencia a un objeto con el modificadorfinal, quiere decir que esta variable no podr ser asignada para referir a otro objeto diferente, los datos del objeto puede ser modificados pero el objeto nunca podr ser cambiado, a continuacin pondremos unos ejemplos que explican cmo se

52

comporta

el

identificadorfinal en

diferentes

situaciones:

Variables marcadas con el modificador " transient" Cuando marcamos una variable con el modificador " transient" le estamos diciendo a la maquina virtual de java ( JVM) que ignore esta variable cuando se intenta serializar el objeto que lo contiene. La serializacin es una de las mejores caractersticas de Java que permite almacenar un objeto, escribiendo su estado (o sea el valor de sus variables de instancia) en un tipo especial de stream de I/O o sea en un flujo de lectura y escritura. Con la serializacin, podemos guardar un objeto en un archivo, o enviarlo a travs de un cable para deserializarlo al otro lado, en otra JVM. Variables con el modificador "volatile" Por el momento solo necesitamos saber que al igual que " transient" solo se aplican a variables de instancia. Variables y mtodos estticos:

53

El modificador esttico ("static") es usado para crear variables y mtodos que existen independientemente de cualquier instancia creada para la clase. Todos los miembros estticos existen antes de haya una nueva instancia de una clase, y habr un solo ejemplar de un miembro esttico, independientemente del nmero de instancias de esa clase. En otras palabras, todas las instancias de una clase comparten el mismo valor para cualquier variable dada esttica. Podemos marcar como esttico: Las variables de instancia Los mtodos Bloques de inicializacin Una clase anidada dentro de otra clase, pero no dentro de un mtodo.

Declarando Enums: Desde la versin 5.0 Java nos permite poder restringir una variable a unos cuantos valores predefinidos por nosotros, estos valores se obtienen a partir de una lista enumerada de valores declarados, se logra esto a travs de los ENUMS. Si por ejemplo queremos restringir el tamao de una persona con solo 3 valores podemos realizarlo de la siguiente manera: enum EstaturaPersona {ENANO, NORMAL, ALTO} Con el cdigo anterior estamos restringiendo que las variables de tipo "EstaturaPersona" slo pueden tener 3 valores porsibles: " ENANO", "NORMAL", "ALTO". Si deseamos asignarle un valor a la variable lo hacemos de la siguiente manera: EstaturaPersona ep = EstaturaPersona.ENANO; No es necesario que declaremos los valores con maysculas, pero si recordamos las convenciones de Sun nos dicen q las constantes debe ser escritas con maysculas.

54

Las enum pueden ser declaradas separadas de la clase, o como un miembro de clase, sin embargo, no debe ser declarada dentro de un mtodo, por ejemplo enum EstaturaPersona {ENANO, puede ser protected o private class Persona { EstaturaPersona estatura; } public class PersonaTest { public static void main(String[] args)
{ Persona persona = new Persona(); persona.estatura = EstaturaPersona.ENANO;// enum fuera de clase } }

NORMAL,

ALTO}

//

No

El punto clave a recordar es que una enum que no est encerrado en una clase se puede declarar slo con el modificador pblico y por defecto, al igual que una clase no interna. Ejemplo de declarar una enumeracin dentro de una clase: class Persona { enum EstaturaPersona {ENANO, NORMAL, ALTO} EstaturaPersona estatura; } public class PersonaTest { public static void main(String[] args)
{ Persona persona = new Persona(); persona.estatura = Persona.EstaturaPersona.ENANO;// requiere //nombre de clase } }

55

Debemos recordar que las enumeraciones pueden ser declaradas dentro de su propia clase, o encerrado en otra clase, y que la sintaxis para acceder a los miembros de una enumeracin depende del lugar donde fue declarada la enumeracin, el siguiente ejemplo es incorrecto: public class PersonaTest { public static void main(String[] args)
{ enum EstaturaPersona {ENANO, NORMAL, ALTO}//No declarar en un mtodo Persona persona = new Persona(); persona.estatura = Persona.EstaturaPersona.ENANO;

} }

Es importante saber que el punto y coma al final de la declaracin de un enum (;) es opcional: class Persona { enum EstaturaPersona Punto y coma opcional }

{ENANO,

NORMAL,

ALTO};//<--

public class PersonaTest { public static void main(String[] args)


{ Persona persona = new Persona(); persona.estatura = Persona.EstaturaPersona.ENANO;// requiere //nombre de clase } }

Tambin debemos recordar que un enum no es un String o un int, cada enumeracin (valor) de "EstaturaPersona" es una instancia de "EstaturaPersona", es decir "ENANO" es del tipo "EstaturaPersona". Debemos pensar en los enums como un tipo de clase. El enum de ejemplo se puede ver conceptualmente de la siguiente manera: public class EstaturaPersona

56

{ public static final EstaturaPersona ENANO = new EstaturaPersona("ENANO", 0); public static final EstaturaPersona NORMAL = new EstaturaPersona("NORMAL", 1); public static final EstaturaPersona ALTO = new EstaturaPersona("ALTO", 2); public EstaturaPersona(String nombre, int posicion) {} } Como dijimos anteriormente pensemos que cada valor del enum "EstaturaPersona" es del tipo "EstaturaPersona" y son representadas con "static" y "final" porque as nos dice Java que debemos declarar a las constantes. Adems cada enum conoce suposicin, es decir, el orden en que fueron declarados. Declarando constructores, mtodos y variables dentro de un enum Como dijimos anteriormente: un enum es algo as como un tipo especial de clase, y podemos hacer ms que slo declarar una lista de variables. Veremos que podemos declarar constructores, mtodos, variables y algo realmente extrao llamado cuerpo de un elemento especfico (constant specific class body). Para entender mejor esto veamos un ejemplo. Pensemos que tenemos una bebida que se vende en tres presentaciones: PERSONAL, MEDIANA y FAMILIAR, y que la bebida personal tiene como contenido 1 litro de bebida, la mediana 3 litro y la familiar 5 litros Podemos tratar estos valores e implementarlo de muchas maneras, pero lo manera ms simple es tratar estos valores (PERSONA, MEDIANA y FAMILIAR) como objetos y que cada uno de estos tengan sus propias variables de instancia. Esto lo logramos asignando estos valores en el momento que inicializamos el enum y pasando un valor al constructor del enum, como mostramos a continuacin en el siguiente cdigo:

57

enum BebidaPresentacion { //1, 3 y 5 son pasados al constructor, siguiente manera PERSONAL(1), MEDIANA(3), FAMILIAR(5); BebidaPresentacion (int litros)// Constructor { this.litros = litros; } private int litros; public getLitros() { return litros; } } class Bebida { BebidaPresentacion presentacion;
{ Bebida bebida1 = new Bebida(); bebida1.presentacion = BebidaPresentacion.PERSONAL; Bebida bebida2 = new Bebida(); Bebida2.presentacion = BebidaPresentacion.FAMILIAR; System.out.println(bebida1.presentacion.getLitros()); //1 System.out.println("********************"); for(BebidaPresentacion present : BebidaPresentacion.values()) { System.out.println(present + " " + present.getLitros()); }

de

la

public static void main(String[] args)

} }

El cdigo anterior imprimir como salida: 1 ******************** PERSONAL 1

58

MEDIANA 3 FAMILIAR 5 Como todo lo que hemos visto hasta ahora, los enums tambin tienen unas reglas que debemos seguir para su correcto uso: NUNCA debemos invocar al constructor del enum directamente. El constructor del enum se invoca automticamente, con los argumentos que se definen despus del valor constante. Por ejemplo, PERSONAL(1) invoca al constructor de "BebidaPresentacion" que toma un "int", pasando el "int" "1" al constructor. Podemos definir ms de un argumento al constructor, y podemos sobrecargar los constructores de un enum, tal como se puede sobrecargar un constructor de una clase normal. Para inicializar una "BebidaPresentacion" tanto con la cantidad de litros como el color del envase, se pasan dos argumentos al constructor como PERSONAL(1, "Azul"), lo que significa que tiene un constructor que recibe tanto un int y un String.

Aqu termina el primer post relacionado a conceptos bsicos de Java, hemos aprendido sobre cmo declarar correctamente los identificadores, las convenciones JavaBeans, denominaciones estndar, cmo declarar correctamente clases, mtodos y variables. Hemos visto como se declaran las interfaces, mtodos y clases abstractos, los modificadores de acceso y los de no acceso, los varargs, arreglos y finalmente los enums, en el siguiente post hablaremos sobre conceptos de Orientacin a Objetos en Java. Si tienen alguna duda comentario o sugerencia no duden en colocarlo en la seccin correspondiente y con todo gusto Alan o yo contestaremos lo ms pronto posible.

59

Sun Certified Java Programmer 6, CX-310-065 - Parte 2: Orientacin a Objetos En este segundo post para la certificacin de Java 6 hablaremos sobre conceptos de la orientacin a objetos, que abarca temas como la herencia, el polimorfismo, la cohesin, bajo acoplamiento (loose coupling), etc. Empezaremos hablando sobre la encapsulacin: ENCAPSULACIN La encapsulacin es un mecanismo que nos permite que, aunque nuestras clases utilicen muchas variables y mtodos para su correcto funcionamiento, no todas sean visibles desde el exterior. O sea que solo exponemos lo que los clientes necesitan para poder hacer uso de los objetos de nuestra clase. Para entender mejor este concepto imaginemos el siguiente escenario: Tenemos la siguiente clase: public class Examen { public String pregunta1; public static void main(String... args)
{

60
Examen examen = new Examen(); examen.pregunta1 = "Que es Java?"; // Legal pero no recomendable

} }

El ejemplo anterior compilar y se ejecutar de forma correcta. Sin embargo no es recomendable que los atributos de la clase (las variables de instancia) estn expuestas de forma que los clientes puedan leer y escribir sus valores directamente . De esta forma cualquiera podra colocar el valor que quisiera en la variable "pregunta1", an valores que nuestra aplicacin no puede o no est preparada para manejar. Ahora hagamos una pregunta: Cmo poder cambiar la clase cuando alguien cambia el valor de "pregunta1" por un valor incorrecto? La nica forma es volver a la clase e implementar un mtodo de slo escritura (un mtodo setter: "setPregunta1(String pregunta1)") y ocultar la variable "pregunta1" establecindola como privada, pero de esta manera al no establecerla desde un principio con los mtodos "set" o "get" todas las personas que han utilizado este cdigo anteriormente y de la manera en la que estaba implementada se encontraran perdidas. La capacidad de realizar cambios en el cdigo de aplicacin sin romper el cdigo de otras personas que utilizan este cdigo es un beneficio clave de la encapsulacin. Ocultando los detalles de la implementacin a travs de mtodos de acceso nos brinda la ventaja de poder rehacer el cdigo dentro de los mtodos sin forzar a las dems personas a cambios, ya que ellas usan dichos mtodos de acceso. Con todo esto obtenemos las dos promesas/beneficios de la programacin orientada a objetos: Flexibilidad y Mantenimiento, pero como vemos estos dos beneficios no llegan solos, nosotros tenemos que implementar nuestro cdigo de manera que brinde y soporte estos beneficios. Estas son algunas recomendaciones para lograr la encapsulacin: Mantener las variables de instancia protegidas (mayormente con el modificador de acceso private).

61

Mantener publicos los mtodos de acceso a las variables de instancia (modificador de acceso public), as forzamos a llamar a las variables de instancia y a la implementacin del mismo a travs de estos mtodos en lugar de ir directamente por las variables de instancia. Para los mtodos de acceso se recomienda usar las reglas de las convenciones de nombres de JavaBean:set<nombrePropiedad> y get<nombrePropiedad> ,de las cuales se habl en el post anterior.

A continuacin mostramos un ejemplo ms prctico de lo que nos estamos refiriendo:

La clase "A" no puede acceder a las variables de instancia de la Clase "B" sin antes ir a los mtodos de acceso (" set" y "get") de dichas variables, las variables de instancia son marcadas privadas y los mtodos de acceso son pblicos. Debemos tener presente que el cdigo de estos mtodos no solo se utiliza para devolver o establecer los valores de las variables de instancia, tambin se puede establecer lgica o implementacin de muchas ms cosas o reglas que queramos definir, por ejemplo: public void setTamanio(int tamanio) { this.tamanio = tamanio * 0.10; this.color = "negro"; }

62

El encapsulamiento es uno de los mecanismos que nos proporciona la orientacin a objetos para poder darle una funcionalidad rica a nuestras clases sin que las personas que las utilicen sepan los detalles exactos de cmo est implementada dicha funcionalidad. Sin embargo, este no es el nico mecanismo proporcionado por la orientacin a objetos. A continuacin veremos otro de ellos. La herencia. HERENCIA, ESUN, TIENEUN (ISA, HAS-A) En Java, la herencia se encuentra en todos. Es seguro decir que en Java casi nada se puede hacer sin hacer uso de la herencia. Para dar un ejemplo a continuacin usaremos del operador " instanceof" (por ahora no ahondaremos mucho en la explicacin del uso de este operador ya que se tocar ms adelante con mayor detalle, slo cabe recordar ahora que este operador devuelve un " true" si la variable puesta al principio es del tipo de la variable con la que se est comparando): public class Test { public static void main(String... args) { Test t1 = new Test(); Test t2 = new Test(); if(!t1.equals(t2)) { System.out.println("No son iguales") } if(t1 instanceof Object) { System.out.println("t1 es un objeto");

} } }

De dnde saca "t1" el mtodo "equals"? Si no hemos implementado ningn mtodo dentro de la clase con ese nombre, o s? Por otro lado se pregunta si " t1" es una instancia de la clase "Object" y de ser as, la condicin if ser exitosa.

63

Cmo puede ser "t1" del tipo "Object" si solo lo declaramos que sea del tipo de la clase " Test"? Esto ocurre porque todas las clases en java (las que ya estn escritas, las que escribimos y las que escribiremos) son una subclase de la clase " Object" (excepto por supuesto la clase "Object" misma) y siempre tendrn mtodos como "equals", "clone", "notify", "wait" y otros ms. Siempre que creamos una clase, esta hereda todos los mtodos de la clase "Object". El mtodo "equals" por ejemplo: Los creadores de Java asumieron correctamente que nosotros comnmente desearamos comparar instancias de las clases para comprobar la igualdad; si la clase "Object" no tuviera un mtodo "equals" tendramos que implementar nosotros mismos un mtodo para este propsito. Tambin debemos recordar que las 2 razones ms importantes para el uso de la herencia son:

Reutilizacin de cdigo Uso del polimorfismo

Empecemos con la reutilizacin. Un enfoque de diseo comn es crear una versin de una clase bastante genrica y despus crear subclases muy especializadas que hereden de esta, por ejemplo:

public class Animal { public void muevete() { System.out.println("Me estoy moviendo"); } } public class Perro extends Animal { public void ladra() {

64

System.out.println("Estoy ladrando"); } }

public class PruebaAnimal { public static void main(String... args)


{ Perro perro = new Perro(); perro.muevete(); perro.ladra();

La salida del cdigo anterior seria: Me estoy moviendo Estoy ladrando Como podemos ver, la clase "Perro" est heredando el mtodo "muevete" de la clase "Animal" y que tambin tiene su propio mtodo agregado, en este caso es el mtodo " ladra". Aqu se est haciendo uso de la reusabilidad al utilizar un mtodo genrico de una clase padre que en este caso es el mtodo " muevete", con esto podemos crear diferentes tipos de animales y todos van a poder utilizar el mtodo "muevete" sin necesidad de implementarlo ellos mismos. El segundo objetivo de la herencia es poder acceder a las clases polimrficamente. Imaginemos este escenario: digamos que tenemos una clase llamada "Entrenador" que quiere recorrer todos los diferentes tipos de animal e invocar al mtodo " muvete" en cada uno de ellos, al momento de escribir la clase " Entrenador" no sabemos cuntas clases de "Animal" podra haber y seguro no vamos a querer cambiar el cdigo slo porque a alguien se le ocurri crear un nuevo tipo de Animal. Lo bonito del polimorfismo es que podemos tratar cualquier tipo de "Animal" como un "Animal", en otras palabras podemos decir lo siguiente: No me importa qu tipo de Animal se pueda crear, siempre y cuando extienda (herede) de Animal todos van a poder moverse (mtodo "muevete").

65

Ahora miremos esto con un ejemplo: public class Animal { public void muevete() { System.out.println("Me estoy moviendo"); } } public class Perro extends Animal { public void ladra() { System.out.println("Estoy ladrando"); } } public class Gato extends Animal { public void ronronea() { System.out.println("Estoy ronroneando");
} }

Ahora imaginemos una clase llamada "Entrenador" que tiene un mtodo que tiene como argumento un "Animal", esto significa que puede tomar cualquier tipo de "Animal", cualquier tipo de animal puede ser pasado a un mtodo con un argumento del tipo " Animal", ejemplo:

public class Entrenador { public static void main(String... args) {

66

}
{ } }

Gato gato = new Gato(); Perro perro = new Perro(); mueveteAnimal(gato); mueveteAnimal(perro);

public static void mueveteAnimal(Animal animal)


animal.muevete();

La salida del cdigo anterior es: Me estoy moviendo Me estoy moviendo El mtodo "mueveteAnimal" est declarando un "Animal" como argumento pero se le puede pasar cualquier sub-clase o sub-tipo de esta clase "Animal", este mtodo podra invocar a cualquier mtodo dentro de la clase "Animal". Lo que si debemos de tener en cuenta es que slo podemos llamar a los mtodos declarados por " Animal", los mtodos declarados dentro de las subclases de " Animal" son dependiente del tipo declarado, esto significa que no podramos llamar al mtodo "ladra" incluso si el "Animal" que se est pasando es del tipo "Perro". RELACIONES IS A, HAS A IS A En Orientacin a objetos el concepto de " IS-A" (es-un) esta basado en la herencia de una clase ("extends") o en la implementacin de una interface ("implements"). IS-A es una forma de decir "Esta cosa es del tipo de esta cosa" (o estos dos tipos pueden ser equivalentes), por ejemplo un "Perro" es del tipo "Animal", en OO nosotros podemos decir: "Perro IS-A Animal", "Lechuga IS-A Vegetal", en java podemos expresar esta relacin IS-A por medio de las palabras reservadas " extends" (para la herencia de clases) y

67

de "implements" (para la implementacin de interfaces), veamos un ejemplo: public class Carro { //cualquier cdigo aqu } public class Toyota extends Carro {
/*Toyota est heredando de carro, no olvidemos que Toyota hereda los miembros de Carro incluido mtodos y variables*/ }

"Carro" tambin es un vehculo as que se podra implementar un rbol de herencia de la siguiente manera: public class Vehiculo{...} public class Carro extends Vehiculo{...} public class Toyota extends Carro{...} En trminos de OO podramos decir lo siguiente:

Vehculo es la sper clase de Carro Carro es la subclase de Vehculo Carro es la sper clase de Toyota Toyota es la subclase de Carro Toyota hereda de Carro y de Vehiculo Toyota deriva de Carro Carro deriva de Vehculo Toyota es subtipo de Carro y Vehiculo

Retornando a la relacin IS-A, lo siguiente es correcto: Toyota extends Carro significa Toyota IS-A Carro Carro extends Vehiculo significa Carro IS-A Vehiculo Ahora recordemos que al principio usamos el operador instanceof, bueno, si la expresin "Toyota instanceof Carro" es verdadera, entonces es lo mismo que decir " Toyota IS-A Carro", la expresin

68

"Toyota instanceof Vehiculo" tambin es verdadera aunque explcitamente no dice esto ya que "Toyota" extiende de "Carro", pero por otro lado "Carro" si extiende de "Vehiculo" a esto se le llama herencia indirecta ya que una clase puede ser hijo, nieto, bisnieto, etc. de otra clase, una clase puede extender o heredar de otra directa o indirectamente ya que en el rbol de herencia pueden haber clases intermedias. HASA La relacin HAS-A esta basada en el uso en lugar de la herencia, por ejemplo "A HAS-A B" si la clase "A" tiene una referencia a una instancia de la clase "B", por ejemplo: public class Animal{ } public class Gato extends Animal { CajaDeArena miCajaDeArena; } En el cdigo anterior la clase " Gato" tiene una referencia una variable de instancia del tipo "CajaDeArena", entonces podemos decir "Gato HAS-A CajaDeArena", en otra palabras, "Gato" tiene una referencia a una "CajaDeArena", la clase "Gato" puede tener un mtodo llamado "llenarCaja(Arena miArena)", de esta manera los usuarios de la clase "Gato" no podrn saber nunca que cuando se invoca al mtodo "llenarCaja" este mtodo delega toda la responsabilidad a la clase "CajaDeArena" llamando a su mtodo "llenarCaja", veamos esto con un ejemplo: public class Gato extends Animal {
private CajaDeArena miCajaDeArena; public void llenarCaja(Arena miArena) { miCajaDeArena.llenarCaja(miArena); /*delegando comportamiento al objeto CajaDeArena */ } }

69

public class CajaDeArena { public void llenarCaja(Arena miArena) { System.out.println("Llenando la caja de arena");
} }

En OO muchas veces no queremos que la gente se preocupe por cual clase u objeto est haciendo realmente el trabajo, los usuarios de la clase "Gato" hacen llamado del mtodo "llenarCaja" pero estos no saben si la misma clase hace el trabajo o no, sin embargo a ellos les parece que la propia clase "Gato" lo hace, no tienen ni idea de que existe algo como una clase llamada " CajaDeArena" que es quien realmente hace el trabajo. Toda la explicacin anterior nos servir para entender mejor otro de los conceptos de la programacin orientada a objetos, uno de los ms tiles si sabemos cmo utilizarlo correctamente: el polimorfismo. POLIMORFISMO Cualquier clase que pase la prueba de tener la relacin " IS-A" puede ser considerada polimrfica, todos los objetos en Java son polimrficos ya que pasan la prueba de la relacin IS-A, tanto para su propio tipo como para con la clase "Object". Debemos recordar la nica forma de a un objeto es a travs de una variable de referencia, hay algunas cosas claves que debemos recordar de las variables de referencia: Una variable de referencia puede ser slo de un tipo y una vez declarado este tipo nunca podr cambiar (aunque el objeto al que hace referencia puede cambiar). Una referencia es una variable, por lo cual su valor puede ser reasignada a otros objetos a menos que esta sea declarada como final. Un tipo de variable de referencia determina los mtodos que se pueden invocar en el objeto que esta variable est referenciando.

70

Una variable de referencia puede referirse a cualquier objeto del mismo tipo que el que est declarando, o puede referirse a cualquier subtipo del tipo declarado. Una variable de referencia puede ser declarado como un tipo de clase o un tipo de interfaz. Si la variable se declara como un tipo de interfaz, se puede hacer referencia a cualquier objeto de cualquier clase que implementa la interfaz.

Anteriormente creamos una clase "Animal" que era extendida por dos clases, "Perro" y "Gato", ahora imaginemos que tenemos una clase llamada "Gaviota", despus queremos hacer que algunos tipos de Animal vuelen o se puedan elevar en el aire y otros no como el caso de "Gaviota" que si puede elevar, por el contrario " Perro" y "Gato" no pueden hacerlo, para esto podramos crear una clase llamada "Elevable" con un mtodo "elevar" y hacer que unos tipos de Animales puedan elevarse y otros no, pero tambin queremos que todos los tipos de Animal se muevan que es lo que nos permite el mtodo "muevete" de la clase "Animal", pero esto no funcionara ya que Java solo soporta la herencia simple, esto significa que una sub clase slo puede tener una clase padre, es decir lo siguiente es incorrecto: public class Gaviota extends NO!! { //Cualquier codigo aqu } Animal, Elevable //

Una clase NO puede extender de ms que de slo una clase , ante estos casos la solucin sera crearse una interface llamada "Elevable" y solo las sub-clases de "Animal" que puedan volar implementen esta interfaz, dicha interfaz quedara de la siguiente manera: public interface Elevable { public void volar(); } public class Animal

71

{
{ } }

public void muevete()


System.out.println("Me estoymoviendo");

A continuacin mostramos la clase "Gaviota" que implementa esta interfaz: public class Gaviota extends Animal implements Elevable { public void volar() { System.out.println("Estoy volando!"); } } Ahora tenemos a la clase "Gaviota" que pasa la prueba de la relacin IS-A tanto para la clase "Animal" como para la interfaz "Elevable", esto significa que una "Gaviota" puede ser tratada polimrficamente como una de estas cuatro cosas en un momento dado, dependiendo del tipo declarado de la variable de referencia: Como un "Object" (ya que todas las clases heredan de la clase Object). Como un "Animal" (ya que est extendiendo de la clase Animal). Como una "Gaviota" (ya que es lo que es). Como un "Elevable" (ya que implementa de la interface Elevable).

Las siguientes declaraciones son legales: Gaviota gaviota = new Gaviota(); Object object = gaviota; Animal animal = gaviota; Elevable elevable = gaviota;

72

Aqu solo hay un objeto Gaviota pero cuatro diferentes variables de referencia, hagamos una pregunta: Cul de las cuatro variables de referencia puede llamar al mtodo "muevete"? Recuerden que las llamadas a mtodos permitidos por el compilador se basan nicamente en el tipo declarado de la referencia, con independencia del tipo de objeto. As que buscando en los cuatro tipos de referencia de nuevo, "Object", "Gaviota", "Animal" y "Elevable" cul de estos cuatro tipos puede llamar al mtodo " muevete()"? La respuesta es la siguiente: El objeto "animal" como el objeto "gaviota" son conocidos por el compilador para poder llamar o invocar al mtodo "muevete". Qu mtodos pueden ser invocados cuando el objeto " gaviota" est utilizando la referencia a la inteface " Elevable"? Slo al mtodo "volar()". Un beneficio es que cualquier clase desde cualquier lugar en el rbol de herencia puede invocar a la interface " Elevable", que sucedera si tenemos un mtodo que tiene como argumento declarado con tipo "Elevable", podras pasar una instancia de objeto del tipo " gaviota" y cualquier otra instancia de clase que implemente a la interface "Elevable", podra usar el parmetro del tipo " Elevable" para invocar al mtodo "volar()" pero no al mtodo "muevete()" o cualquier otro objeto que se sabe que el compilador conocer basado en el tipo de referencia. Otra cosa que debemos saber es que si bien el compilador solo conoce al tipo de referencia declarado, la JVM en tiempo de ejecucin sabe lo que el objeto realmente es, y eso significa que incluso si el objeto "gaviota" hace una llamada al mtodo " muevete" usando la variable de referencia "animal", si el objeto " gaviota" sobre-escribe el mtodo "muevete", la JVM invocara a esa versin del mtodo "muevete". La JVM mira al objeto real que se encuentra al otro extremo de la referencia, puede ver que se ha sobre escrito el mtodo del tipo de variable de referencia declarado e invoca al mtodo del objeto de la clase actual.

73

Siempre se puede hacer referencia a un objeto con un tipo de referencia variable ms general (una superclase o interfaz), pero en tiempo de ejecucin, las nicas cosas que son seleccionadas dinmicamente basndose en el objeto real (en lugar de tipo de referencia) son mtodos de instancia (no los mtodos estticos, no las variables), slo los mtodos de instancia sobre-escritos se invocan de forma dinmica en funcin del tipo de objeto real. Ejemplo: public class Animal { public void muevete() { System.out.println("Me estoy moviendo"); } public static void respirar() //Mtodo esttico { System.out.println("Estoy respirando"); } } public class Gaviota extends Animal { public void muevete() { System.out.println("La gaviota se mueve"); } public static void respirar() //Mtodo esttico { System.out.println("La gaviota est respirando"); } } public class Prueba { public static void main(String... args) { Animal a = new Gaviota();

74

a.muevete();
} } a.respirar();

Salida: La gaviota se mueve Estoy respirando El segundo resultado que vemos se trata de la invocacin de un mtodo esttico. El comportamiento que vemos pasa porque cuando el compilador ve que se est invocando un mtodo esttico, cambia la referencia al objeto por el tipo de la clase, o sea que al final queda de la siguiente manera: Animal a = new Gaviota(); Animal.respirar(); En el primer caso el mtodo que se invoca es el de " Gaviota" y no el de "Animal", ya que el mtodo "muevete" no es esttico sino un mtodo de instancia; por eso dice se dice que slo los mtodos de instancia sobrecargados se invocan de forma dinmica en funcin del tipo de objeto real. SOBRECARGA Y SOBRE ESCRITURA Sobre escritura de mtodos En cualquier momento que se tenga un mtodo que hereda de una superclase, se tendr la oportunidad de realizar una sobre-escritura de mtodos, a menos que el mtodo este marcado con la palabra reservada final. El principal beneficio de la sobre-escritura de mtodos es que se puede definir un comportamiento especial para un mtodo de una subclase. En el siguiente ejemplo veremos cmo la clase "Gaviota" sobre-escribe el mtodo "muevete" de la clase "Animal": public class Animal { public void muevete() {

75

System.out.println("Me estoy moviendo"); } }

public class Gaviota extends Animal { public void muevete() { System.out.println("La gaviota se mueve"); } } Para mtodos abstractos que se heredan desde una superclase, no se tiene otra opcin ms que sobre escribir-dichos mtodos. Se deben implementar los mtodos a menos que la subclase que los herede tambin este marcada como abstracta. Los mtodos abstractos deben ser implementados por una subclase concreta, esto quiere decir que la subclase concreta sobre-escribe el mtodo abstracto de la superclase. Entonces debemos pensar que los mtodos abstractos son mtodos que forzosamente deben ser sobre-escritos. El creador de la clase " Animal" podra haber decidido que, a efectos de polimorfismo, todos los subtipos de Animal deben implementar el mtodo "moverse", de una manera nica y especifica. Polimrficamente, cuando alguien tiene una referencia de Animal que no refiere a una instancia de Animal, sino a una instancia de una subclase de Animal, la persona que llama debe ser capaz de invocar al mtodo "muevete" en la referencia a "Animal", pero el objeto en tiempo de ejecucin real ejecutar su propio y especifico mtodo "muevete". Marcando el mtodo "muevete" como abstracto es la forma que el programador de la clase " Animal" dice a todos los desarrolladores de las dems subclases: " No tiene ningn sentido para el nuevo subtipo utilizar el mtodo genrico "muevete", por lo que t debes implementar tu propio mtodo "muevete". A continuacin mostraremos un ejemplo de clases no abstractas: public class Prueba { public static void main(String... args)

76

{ Animal a = new Animal(); Animal b = new Gaviota(); //Referencia a Animal, pero objeto Gaviota
} } public class Animal { public void muevete() { System.out.println("Me estoy moviendo"); } } public class Gaviota extends Animal a.muevete(); b.muevete();

public void muevete() { System.out.println("La gaviota se mueve"); } public void volar() { System.out.println("La gaviota vuela"); }

En el cdigo anterior la clase " Prueba" utiliza una referencia a "Animal" para invocar un mtodo en el objeto " Gaviota", hay que recordar que el compilador solo permitir que se invoquen mtodos que se encuentran en la clase " Animal", cuando usas una referencia a un "Animal". Por lo tanto, el siguiente cdigo no es legal: Animal c = new Gaviota(); c.volar(); // No puedes invocar a "volar()", Animal no tiene un mtodo volar Para reiterar: el compilar solo mira el tipo de referencia y no el tipo de instancia en tiempo de ejecucin . El polimorfismo nos permite tener referencias a sper tipos o a tipos ms abstractos (incluyendo

77

las interfaces) para referir a uno de estos subtipos (incluyendo implementacin de interfaces).

El mtodo que sobre-escribe no debe tener un modificador de acceso ms restringido que el del mtodo a ser sobre-escrito. Por ejemplo no se puede sobre-escribir un mtodo marcado con el modificador de acceso public y cambiarlo a protected. Pensemos en los siguiente: si la clase "Animal" tiene un mtodo "comer()" marcado "public" y alguien tiene una referencia de " Animal", es decir, una referencia declarada del tipo " Animal", se asume que es seguro llamar al mtodo "comer()" en la referencia de "Animal", independientemente de la instancia actual que la referencia a "Animal" est invocando. Si una subclase cambia el modificador de acceso del mtodo sobre-escrito, entonces cuando la JVM invoque la verdadera versin del objeto " Gaviota" en lugar de la versin del tipo de referencia "Animal", el programa perecer, a continuacin un ejemplo: public class Prueba { public static void main(String... args) { Animal a = new Animal(); Animal b = new Gaviota(); // Referencia a Animal, pero objeto Gaviota
} } public class Animal { public void muevete() { System.out.println("Me estoy moviendo"); } } public class Gaviota extends Animal a.muevete(); b.muevete();

private {

void muevete() //Marcado como privado

78

System.out.println("La gaviota se mueve"); } }

Si el cdigo anterior compilara (cosa que no har) obtendramos el siguiente error en tiempo de ejecucin: Animal b = new Gaviota(); //Referencia a Animal, pero objeto Gaviota a.muevete(); //Ocurre una crisis en tiempo de ejecucin La variable "b" es del tipo "Animal", el cual tiene un mtodo pblico llamado "muevete", pero hay que recordar que en tiempo de ejecucin, Java utiliza la invocacin de mtodos virtuales para seleccionar dinmicamente la versin real del mtodo que se ejecutar, basado en la del tipo del objeto en tiempo de ejecucin. Una referencia de "Animal" puede referir siempre a una instancia de "Gaviota" porque "Gaviota" IS-A "Animal" ("Gaviota" ES UN "Animal"). Lo que hace posible que una instancia de una superclase referencie a una instancia de la subclase es que la subclase es capaz de hacer todo lo que la superclase puede hacer (ya que recibe el comportamiento de esta por herencia). Esto quiere decir que cualquiera con una referencia a " Gaviota" usando una instancia deAnimal (Animal a = new Gaviota();) es libre de llamar a todos los mtodos accesibles de "Animal", no importa si "Gaviota" sobre-escribe los mtodos de "Animal" o simplemente los hereda. Un mtodo-sobre escrito debe cumplir con el contrato de la superclase. Las reglas para sobre escribir un mtodo son las siguientes: La lista de argumentos debe ser exactamente igual (del mismo tipo y en el mismo orden) que el mtodo a sobre-escribir. Si esta lista no coincide en realidad lo que se est haciendo es una sobrecarga del mtodo (que puede que no sea nuestra intencin). El tipo de retorno del mtodo sobre escrito debe ser el mismo o un subtipo del declarado en el mtodo de la sper clase. El modificador de acceso no debe ser ms restrictivo que del mtodo a sobre-escribir. Por ejemplo, si en la clase base tenemos un

79

mtodo "public" NO podemos sobre-escribirlo ponindole un modificador "protected". El modificador de acceso puede ser menos restrictivo que el del mtodo a sobre-escribir. Por ejemplo, si en la clase base tenemos un mtodo "protected" SI podemos sobre-escribirlo ponindole un modificador "public". Los mtodos de instancia solo pueden ser sobre-escritos si estos son heredados por la subclase. Una subclase dentro del mismo paquete que su superclase puede sobre escribir cualquier mtodo de la sper clase que NO est marcado comoprivate o final. Una subclase en diferente paquete puede sobre-escribir solo los mtodos no finales marcados public oprotected (los mtodos protected son heredados por la subclase). Los mtodos sobre-escritos no deben lanzar excepciones marcadas (en las que sean necesarias un try catch) que sean nuevas o ms amplias que aquellas declaradas en el mtodo que sobre-escribe. Por ejemplo un mtodo que declara un "FileNotFoundException" no puede ser sobre-escrito por un mtodo que declara un " SQLException", "Exception", "IOException" o cualquier otra excepcin a menos que esta sea una subclase de "FileNotFoundException". Los mtodos sobre-escritos pueden lanzar excepciones ms especficas (excepciones que extiendan de la excepcin que se est declarando) o lanzar menos excepciones. Solo porque el mtodo a sobre-escribir puede lanzar excepciones no quiere decir que el mtodo sobre-escrito lanzara estas mismas excepciones, un mtodo sobreescrito no tiene que declarar una excepcin que nunca lanzar. Independientemente de que el mtodo a sobre-escribir las declare. No se puede sobre-escribir un mtodo marcado con final. No se puede sobre-escribir un mtodo marcado con static. Si un mtodo no puede ser heredado entonces no puede ser sobre-escrito. Hay que recordar que la sobre-escritura implica que se est re-implementando un mtodo que est heredanddo. Por ejemplo el siguiente cdigo no es legal: public class Prueba { public static void main(String... args)

80

{ Gaviota a = new Gaviota(); a.muevete(); //No es legal porque Gaviota no hereda muevete(), muevete() es declarado private en Animal
} }

public class Animal { private void muevete() { System.out.println("Me estoy moviendo"); } } public class Gaviota extends Animal {}

Invocando a la versin de la superclase de un mtodo sobre escrito Tal vez queramos tomar ventaja de una parte del cdigo en la versin de la sper clase de un mtodo y aun as sobre-escribirlo para proveer algn comportamiento especifico adicional. Esto es como decir: "Ejecuta la versin de la superclase de un mtodo, despus vuelve y termina con mi cdigo para hacer un comportamiento adicional en el mtodo de la subclase", esto es fcil de hacer utilizando la palabra reservada super como en el ejemplo siguiente: public class Prueba { public static void main(String... args) { Gaviota a = new Gaviota(); a.muevete(); } } public class Animal { public void muevete() { System.out.println("Me estoy moviendo"); }

81

} public class Gaviota extends Animal { public void muevete() { //podemos agregar alguna cosa mas super.muevete(); //invoca al cdigo de la superclase (Animal) } } Usar la palabra reservada "super" para invocar a un mtodo sobreescrito slo se aplica para mtodos de instancia, hay que recordar quelos mtodos marcados con static no pueden ser sobre escritos. Sobrecarga de mtodos La sobrecarga de mtodos permite usar el mismo nombre de un mtodo en una clase, pero con diferentes argumentos y, opcionalmente, un tipo de retorno diferente, el cdigo asume la carga de hacer frente a diferentes tipos de argumentos en lugar de obligar a la persona que llama a hacer las conversiones antes de invocar el mtodo. Las reglas a seguir para la sobrecarga son simples: Para sobrecargar mtodos se debe cambiar la lista de argumentos. Para sobrecargar mtodos se puede cambiar el tipo de datos de retorno. Para sobrecargar mtodos se puede cambiar el modificador de acceso. Para sobrecargar mtodos se pueden declarar excepciones que sean nuevas o ms amplias. El mtodo puede ser sobrecargado en la misma clase o en una subclase. Si la clase "A" define un mtodo "hacerAlgo(int i)", la subclase "B" puede definir un mtodo llamado " hacerAlgo(String s)", sin sobre-escribir el mtodo " hacerAlgo(int i)" de la sper clase que toma como argumento un entero. Dos mtodos con el mismo nombre pero en diferentes clases pueden ser consideradas

82

sobrecargados, si la sub-clase hereda una versin del mtodo y luego declara otra versin sobrecargada en la definicin de la clase. Formas legales de sobrecargar un mtodo El mtodo que queremos sobrecargar es el siguiente: public void unMetodo(String str, int t, double d){} Los siguientes mtodos son legales para sobrecargar el mtodo anterior: public void unMetodo(String str, int t){} public int unMetodo(String str, double d){} public void IOException{} unMetodo(String str, int t)throws

Invocando a mtodos sobrecargados Cuando un mtodo es invocado, ms de un mtodo con el mismo nombre puede existir para el tipo de objeto que estamos invocando. Por ejemplo la clase "Gaviota" puede tener tres mtodos con el mismo nombre pero con diferente lista de argumentos, estos sern mtodos sobrecargados. Definir cul de los diferentes mtodos se desea invocar depender de la lista de argumentos que contenga. Si estamos invocando a un mtodo que tiene un " String" como argumento, el mtodo que tiene un " String" como argumento ser llamado. A continuacin veremos un ejemplo: public class UnaClase { public int unMetodo(int x, int y) { return x + y; } public double unMetodo(double x, double y) { return x + y; }

83

} public class Prueba { public static void main(String... args)


{ UnaClase a = new UnaClase(); int x = 9; int y = 5;

int resultado = a.unMetodo(x, y); //Qu versin de "unMetodo" es invocado? double otroResultado = a.unMetodo(5.2, 3.6); //Qu versin es

invocada? } }

En el primer caso se llama a la primera versin del mtodo "unMetodo(x,y)" ya que se est pasando a dos enteros y en el segundo caso se llama a la versin del mtodo " unMetodo" que recibe dos argumentos del tipo double. La invocacin de mtodos sobrecargados que reciben objetos en lugar de tipos primitivos es un poco ms interesante. Si tenemos un mtodo sobrecargado, que en un mtodo toma un objeto del tipo " Animal" y otro en el que toma un objeto del tipo " Gaviota" (subclase de "Animal"). Si pasamos un objeto del tipo Gaviota cuando invocamos al mtodo, invocaremos a la versin sobrecargada que toma una "Gaviota". O al menos eso parece a primera vista: class Animal{}

class Gaviota extends Animal{} public class UsaAnimales { public void muevete(Animal a) { System.out.println("Estas en la versin de Animal"); } public void muevete(Gaviota g)

84

{ System.out.println("Estas en la versin de Gaviota"); } public static void main(String... args) { UsaAnimales ua = new UsaAnimales(); Animal a = new Animal(); Gaviora g = new Gaviota():
} } ua.muevete(a); ua.muevete(g);

La salida es la que esperamos: Estas en la versin de Animal Estas en la versin de Gaviota Pero qu sucede si usamos una referencia de " Animal" para un objeto "Gaviota"? Animal animalReferenciaGaviota = new Gaviota(); ua.muevete(animalReferenciaGaviota); Cul de las versiones del mtodo " muvete" es invocada?, podramos pensar que la respuesta es: "El que toma una " Gaviota", ya que es un objeto del tipo " Gaviota" el que en tiempo de ejecucin est siendo pasado al mtodo". Pero no es as como funciona. El cdigo anterior imprime lo siguiente: Estas en la versin de Animal El objeto en tiempo de ejecucin es una " Gaviota" y no un "Animal". La eleccin de cul mtodo sobrecargado se deber llamar no es decidido dinmicamente en tiempo de ejecucin . Slo hay que recordar que el tipo de referencia, no el tipo de objeto, determina qu mtodo sobrecargado es llamado.

85

Para resumir lo que hemos visto hasta ahora de sobrecarga y sobreescritura: cul versin de un mtodo sobre-escrito es llamado (en otras palabas, desde cul clase en el rbol de herencia) es decidido en tiempo de ejecucin por el tipo de objeto; pero cul versin del mtodo sobrecargado se llamar est basado en el tipo de referencia del argumento que se pasa en tiempo de compilacin. Si invocamos un mtodo pasando una referencia de "Animal" para un objeto "Gaviota", el compilador solo sabe que est recibiendo un " Animal", por lo que elige la versin sobrecargada que toma un " Animal". No importa que en tiempo de ejecucin realmente se pase una "Gaviota". Polimorfismo en mtodos sobrecargados y sobre-escritos Cmo trabaja el polimorfismo en mtodos sobrecargados? Si pasamos una referencia de "Animal", el mtodo que toma un "Animal" ser llamado, incluso si el objeto actual pasado es una "Gaviota". Una vez que la "Gaviota" disfrazada de "Animal" entra en el mtodo, sin embargo, el objeto " Gaviota" sigue siendo una "Gaviota" a pesar de ser pasado a un mtodo que recibe un "Animal". Entonces es verdad que el polimorfismo no determina que versin del mtodo sobrecargado ser llamado. El polimorfismo entra en juego cuando se decide cul versin de un mtodo sobre-escrito es llamado. Pero algunas veces un mtodo puede ser ambos, sobreescrito y sobrecargado. Imaginemos lo siguiente:

public class Animal { public void comer() { System.out.println("El Animal genrico est comiendo"); } } public class Gaviota extends Animal

86

{ public void comer() { System.out.println("La comiendo"); }


{ } }

gaviota

est

public void comer(String s)


System.out.println("La gaviota est comiendo esto: " + s);

Noten que la clase "Gaviota" tiene sobrecargado y sobre-escrito el mtodo "comer" (el mtodo sobrecargado es el que recibe unString y el sobre-escrito el que no recibe nada). La siguiente tabla muestra cul de las versiones del mtodo " comer" ser llamado dependiendo de cmo sea invocado: Cdigo mtodo de Invocacin = al new Resultado "el Animal comiendo" genrico est

Animal a Animal(); a.comer(); Gaviota g Gaviota(); g.comer(); Animal ag Gaviota(); ag.comer();

new

"La gaviota est comiendo"

"La gaviota est comiendo" new Polimorfismo trabajando: El objeto actual ("Gaviota"), no el tipo de referencia ("Animal"), es usado para determinar cul versin del mtodo "comer" es llamado. new "La gaviota est comiendo esto: peces" El mtodo sobre cargado "comer(String s)" es llamado. de compilacin!!

Gaviota gp = Gaviota(); gp.comer("peces") Animal a2 =

new Error

87

Cdigo mtodo

de

Invocacin

al

Resultado El compilador mira la clase Animal y ve que no tiene un mtodo comer que reciba un String.

Animal(); a2.comer("manzanas");

Error de compilacin!! El compilador ve solo la referencia y sabe que Animal ag2 = new "Animal" no tiene un mtodo "comer" que Gaviota(); reciba unString. Al compilador no le ag2.comer("peras"); importa que el objeto actual pueda ser una "Gaviota" en tiempo de ejecucin.

Diferencias entre mtodos sobre cargados y sobre escritos: Mtodo sobrecargado Argumento(s) Debe cambiar. Tipo retorno de Puede cambiar. Mtodo sobre-escrito No debe cambiar. No puede cambiar, excepto para retornos covariantes. Pueden no declarar una excepcin del mtodo que sobre-escribe, o declarar una subclase de esta excepcin. No debe lanzar nuevas excepciones o ms "amplias" que aquellas declaradas en el mtodo que sobre-escribe. No pueden tener un nivel de acceso ms restrictivo, pero si uno igual o menos restrictivo.

Excepciones

Puede cambiar.

Nivel Acceso Invocacin

de

Puede cambiar.

El tipo de referencia El mtodo real que se invoca es determina cul mtodo todava una invocacin de mtodo sobrecargado es virtual que siempre sucede en seleccionado, basado en el tiempo de ejecucin, pero el tipo de argumentos compilador ya sabe la firma del

88

Mtodo sobrecargado

Mtodo sobre-escrito

mtodo que se invoca. El tipo de declarados. Sucede en objeto determina cul mtodo es tiempo de compilacin. seleccionado. Sucede en tiempo de ejecucin. CASTING DE VARIABLES DE REFERENCIA Hasta el momento hemos visto como es posible y comn el uso de variables de referencia genricas para referirse a tipos de objetos ms especficos, eso es el corazn del polimorfismo, como ejemplo veamos la siguiente lnea de cdigo: Animal animal = new Gaviota(); Pero qu sucede si queremos usar una variable de referencia "Animal" para invocar un mtodo que solo la clase " Perro" tiene? Sabemos que nos estamos refiriendo a un Perro y queremos hacer una cosa especfica de Perro. En la siguiente lnea de cdigo tendremos un arreglo de Animales y cada vez que encontremos una Perro en el arreglo, haremos algo especial de Perro. Asumamos por el momento que todo el cdigo escrito a continuacin est correcto, solo que no estamos seguros de la lnea de cdigo que invocara al mtodo " hacerRuido". public class Animal { void hacerRuido() { System.out.println("Ruido Generico"); } } public class Perro extends Animal { void hacerRuido() {

89

System.out.println("Ladrando"); } void hacerseElMuerto() { System.out.println("Muriendo"); } } public class Prueba { public static void main(String... args) { Animal[] a = {new Animal(), new Perro(), new Animal()};
for(Animal animal: a) { animal.hacerRuido(); if(animal instanceof Perro) { animal.hacerseElMuerto(); //Se intenta invocar un metodo de Perro? } } } }

Cuando compilemos el cdigo anterior, el compilador nos enviara un mensaje como: Cannot find Symbol El compilador nos est diciendo: "Oye, la clase " Animal" no tiene un mtodo "hacerseElMuerto"". Ahora modifiquemos el bloque de la condicional if: if(animal instanceof Perro) { Perro perro = (Perro) animal; // Casteando la variable de referencia perro.hacerseElMuerto();

90

} El nuevo y mejorado bloque de cdigo contiene un " cast", el cual en algunos casos es llamado down casting, porque estamos bajando en el rbol de herencia a una categora mas especifica. Ahora el compilador no nos dar problemas, casteamos la variable "animal" a un tipo "Perro". El compilador nos est diciendo lo siguiente: "Sabemos que nos estamos refiriendo a un objeto del tipo Perro, est bien hacer una nueva variable de referencia de tipoPerro, para referirnos a este objeto", en este caso estamos bien, porque antes de intentar el " cast" hicimos una prueba deinstanceof para asegurarnos. Es importante saber que el compilador confa en nosotros cuando hacemos un down casting aun cuando pudiramos hacer algo como lo siguiente: public class Animal{} public class Perro extends Animal{} public class Prueba { public static void main(String... args)
{ Animal animal = new Animal(); Perro perro = (Perro)animal; //Compila pero fallara despus } }

Este cdigo compilara correctamente pero cuando intentemos ejecutarlo vamos a obtener una excepcin como la siguiente: java.lang.ClassCastException Por qu no podemos confiar en el compilador para que nos ayude en esto? No puede ver que animal es del tipo "Animal"? Todo lo que el compilador puede hacer es comprobar que los dos tipos pertenezcan al mismo rbol de herencia, por lo que dependiendo de lo que el cdigo puede hacer antes del down casting, es posible de que " animal" sea del tipo "Perro". El

91

compilador puede permitir cosas que posiblemente puedan funcionar en tiempo de ejecucin. Sin embargo si el compilador sabe con certeza que el cast no puede funcionar, entonces la compilacin fallara. El siguiente bloque de cdigo NO compilar: Animal animal = new Animal(); Perro p = (Perro) animal; String s = (String) animal; //animal nunca puede ser un String En este caso obtendremos un error como el siguiente: inconvertible types A diferencia del down-casting, el up-casting (convertir a un tipo ms general en el rbol de herencia) trabaja implcitamente porque cuando estmos haciendo up-casting estamos implcitamente restringiendo el numero de mtodos que podemos invocar, lo que impide que ms adelante podamos invocar a un mtodo ms especifico, por ejemplo: class Animal{} class Perro extends Animal{} class Prueba { public static void main(String args)
{ Perro p = new Perro(); Animal a1 = p; //upCasting, se puede sin conversin explicita Animal a2 = (Animal)p; // upCasting, se puede con conversin explicita } }

Ambos up-castings anteriores compilaran y se ejecutaran sin excepcin, porque un "Perro" ES-UN "Animal", lo que significa que lo que cualquier "Animal" pueda hacer, el "Perro" lo har. Un "Perro" puede hacer ms, pero en este punto, cualquiera con una referencia de "Animal" puede llamar con seguridad a los mtodos de "Animal" en una instancia de "Perro". Los mtodos de "Animal"

92

pueden haber sido sobre-escritos en la clase " Perro", pero todo lo que importa ahora es saber que un " Perro" puede hacer todo lo que un "Animal" puede hacer. El compilador y la JVM saben esto tambin, entonces el up-casting implcito es siempre legar para la asignacin de un objeto de un subtipo para una referencia a su sper tipo o interface. Si "Perro" implementa la interface "Mascota", y "Mascota" define el mtodo "seAmigable()", un "Perro" puede implcitamente hacer casting a una "Mascota", pero el nico mtodo de "Perro" que se podr invocar es "seAmigable()", el cual "Perro" fue forzado a implementar porque "Perro" implement a la interface "Mascota". Una cosa ms, si "Perro" implementa a la interface "Mascota", y si "Sabueso" extiende a "Perro", pero "Sabueso" no declara que este implementando a "Mascota", "Sabueso" sigue siendo una "Mascota", "Sabueso" es una "Mascota" simplemente porque este est extendiendo a "Perro". La clase "Sabueso" puede siempre sobre-escribir cualquier mtodo que este heredando desde " Perro", incluyendo los mtodos que " Perro" implementa para cumplir con el contrato de la interfaz. Por ltimo, si "Sabueso" declara que implementa a " Mascota", es solamente para que los que busquen en " Sabueso" puedan ver fcilmente que "Sabueso" ES-UNA "Mascota", sin tener que mirar a la superclase de "Sabueso". "Sabueso" no tiene la necesidad de implementar el mtodo "seAmigable()" si la clase "Perro" (sper clase de "Sabueso") ya se ha ocupado de eso. En otras palabras, si "Sabueso" ES-UN "Perro", y "Perro" ES-UNA "Mascota", entonces "Sabueso" ES-UNA "Mascota", y ya ha cumplido con los mtodos de "Mascota" para la aplicacin del mtodo "seAmigable()", ya que hereda el mtodo "seAmigable()". El compilador es suficientemente inteligente para decir: "S que Sabueso es un Perro, pero est bien para que sea ms obvio". As que no hay que dejarse engaar por el cdigo que muestra una clase concreta que declara que implementa una interfaz, pero no implementa el mtodo de la interface, antes de poder decir si el cdigo es legal, debemos mirar cul es la superclase de la clase que esta implementado esta interface. Si alguna clase en el rbol de herencia ya ha implementado mtodos concretos y ha declarado que ella (la

93

superclase) implementa la interfaz, entonces la subclase no tiene ninguna obligacin de volver a implementar (sobre-escribir) los mtodos. IMPLEMENTANDO UNA INTERFACE Cuando implementamos una interface estamos aceptando el contrato definido por la interfaz. Eso quiere decir que estamos obligados a implementar todos los mtodos definidos por la interfaz y que cualquiera que conozca los mtodos de la interfaz (no como lo implementa pero si como se llaman y que retornan) puede invocar estos mtodos en una clase que implementa la interfaz. Por ejemplo si creamos una clase que implemente a la interfaz "Runnable" (para que el cdigo pueda ejecutarse en otro un hilo), debemos implementar el mtodo "public void run()", de lo contrario el hilo ver que no tenemos implementado el mtodo " run" de la interface "Runnable" y provocar un error. Afortunadamente, Java impide que esta crisis se produzca mediante la ejecucin de un proceso de comprobacin, en el compilador, de cualquier clase que pretende implementar una interfaz. Si la clase est implementando una interface, deberamos tener una implementacin para cada mtodo de la interface, con unas pocas excepciones que veremos en un momento. Por ejemplo, imaginemos que tenemos la interfaz " Rebotable", con dos mtodos "rebotar()" y "setFactorRebote", la siguiente clase compilar: public class Pelota implements Rebotable //palabra reservada implements { public void rebotar(){} public void setFactorRebote(int fr){} } El contrato garantiza que una clase tenga todos los mtodos de una interface pero no garantiza que tenga una buena o correcta implementacin en el cuerpo del mtodo. El compilador nunca se fijar que haya algo entre las llaves del mtodo, nunca dir que es un

94

mtodo y que debera hacer algo, solo se fija en que tenga los mtodos que se describen en la interface, nada ms. Las clases que implementan una interface deben de seguir las mismas reglas para una clase que extiende a una clase abstracta. Las reglas para que una clase NO abstracta correctamente una interface son las siguientes:

implemente

Proveer implementacin para todos los mtodos declarados en la interface. Seguir todas las reglas para una sobre-escritura legal (overrides). No declarar excepciones, en la implementacin del mtodo, que no estn en el mtodo definido en la interface. Se pueden declarar subclases de las declaradas por el mtodo de la interfaz. Mantener el mismo nombre del mtodo de la interface y el mismo tipo de dato de retorno (o un subtipo). Una clase que implementa una interface puede ser abstracta. Por ejemplo: abstract class Pelota implements Rebotable{} Notan que algo falta?, pues no estamos implementando los mtodos de la interfaz "Rebotable", y no tenemos ningn error. Si la clase que implementa una interface es una clase abstracta esta puede simplemente pasar la implementacin de los mtodos a la primera clase concreta que se implemente. Por ejemplo si tenemos la clase " PelotaPlaya" y esta extiende de la clase "Pelota", entonces la clase "PelotaPlaya" debe de implementar todos los mtodos de la interfaz " Rebotable": public class PelotaPlaya extends Pelota { /*A pesar que no se dice en la declaracin anterior (no hay implements a Rebotable) PelotaPlaya tiene que implementar a la interface Rebotable ya que la super clase

95

abstracta de PelotaPlaya (Pelota) implementa a Rebotable */ public void rebotar(){} public void setFactorRebote(int fr){}
/*Si la clase Pelota hubiera declarado algun mtodo abstracto, entonces ese mtodo debera estar implementado aqu tambin*/ }

A menos que la implementacin sea de una clase abstracta, la implementacin debe tener todos los mtodos definidos por la interface. Hay dos reglas ms que debemos saber: 1. Una clase puede implementar ms de una interface, por ejemplo:

public class Pelota implements Rebotable, Runnable, Serializable{} Podemos extender solo a una clase, pero implementar a muchas interfaces. Pero recuerden que la sub-clasificacin (extends) define quin y qu es nuestra clase, mientras que la implementacin (implements) define una funcin que puede desempear o un sombrero que nuestra clase puede usar, a pesar de lo diferente que podra ser de la otra clase que implemente la misma interface (pero de un rbol de herencia diferente). Por ejemplo: " Persona extends SerHumano", pero una "Persona" tambin puede ser (implements) "Programador", "Empleado", "Pariente". 2. Una interface puede tambin extender a otra interface, pero nunca implementar nada, por ejemplo:

public interface Rebotable extends Movible{} Qu significa esto? La primera clase concreta que implemente "Rebotable" debe implementar todos los mtodos de esta interface, pero tambin debe implementar todos los mtodos de la interface

96

"Movible". La sub-interface simplemente est agregando ms requerimientos al contrato de la super-interface. Una interface puede extender a ms de una interface, pero sabemos que cuando hablamos de clases esto es ilegal, por ejemplo: public class Programador Geek{} //Esto es ilegal!! extends Empleado,

Como mencionamos anteriormente una clase no permite extender a ms de una clase en Java, una interface, sin embargo, puede implementar a ms de una interface, por ejemplo: public interface Rebotable extends Movible, Inflable //OK { void rebotar(); void setFactorRebote(int fr); } interface Movible { void muevete(); } interface Inflable { void inflate(); } En el prximo ejemplo, "Pelota" requiere implementar la interface "Rebotable", pero tambin debe implementar todos los mtodos de las interfaces que "Rebotable" ha extendido, incluyendo cualquier interface que aquellas interfaces extienden, y as sucesivamente hasta llegar a la parte superior de la pila. " Pelota" debe tener lo siguiente: public class Pelota implements Rebotable { public void rebotar(){} //implementando metodos de la

97

public void //interface Rebotable

setFactorRebote(int

fr){}

public void muevete(){} //implementando de Movible public void inflate(){} //implementando de Inflable

Si la clase "Pelota" no implementa cualquiera de los mtodos de "Rebotable", "Movible" o "Inflable", el compilador encontrar errores, al menos que "Pelota" sea una clase abstracta. En el caso de que "Pelota" sea una clase abstracta esta puede implementar todos, algunos o ningn mtodo de las interfaces, puede dejar la implementacin a una sub clase concreta de "Pelota", por ejemplo: abstract class Pelota implements Rebotable { public void rebotar(){} //define comportamiento de la public void setFactorRebote(int fr){} //interface Rebotable
/*no implementa el resto de los mtodos, deja el resto para una subclase concreta*/ } class PelotaFutbol extends Pelota { /*implementando mtodos que pelota no hizo*/ public void muevete(){} //implementando de Movible public void inflate(){} //implementando de Inflable /*PelotaFutbol puede implementado en pelota*/ public void rebotar(){} } elegir sobre escribir el mtodo rebotar

Comparando ejemplos de abstracto y concreto de extender e implementar:

98

Ya que "PelotaFutbol" es la primera clase concreta que Implementa "Rebotable", esta debe implementar todos los mtodos de la interface "Rebotable", excepto aquellos definidos en la clase abstracta "Pelota". Ya que "Pelota" no provee implementacin de los mtodos de la interface "Rebotable", es necesario que "PelotaFutbol" implemente todos aquellos mtodos. TIPOS CORRECTOS DE RETORNO El objetivo de esta seccin es cubrir dos aspectos de los tipos de retorno:

1. Qu se puede declarar como un tipo de retorno, y 2. Qu se puede retornar como un valor.

Qu se puede y no se puede declarar es muy sencillo, pero esto depende si estamos sobre-escribiendo un mtodo, heredado o simplemente declarando un mtodo nuevo (el cual incluye sobrecarga

99

de mtodos). Daremos solo una pequea mirada a las diferencias entre las reglas para los tipos de retorno para mtodos sobre cargados y sobre-escritos (overloaded y overriding). Declaracin de tipos de retorno Veremos qu est permitido para declarar como un tipo de retorno, lo cual depende primero en que si estamos sobre-escribiendo, sobrecargando o declarando un nuevo mtodo. Tipos de retorno en mtodos sobre cargados Recuerden que un mtodo sobrecargado no es ms que una forma para la reutilizacin del mismo nombre de un mtodo pero con diferentes argumentos. Un mtodo sobrecargado es un mtodo completamente diferente de cualquier otro mtodo con el mismo nombre. Si heredamos un mtodo pero lo sobrecargamos este en una subclase, este no est sujeto a las restricciones de sobre-escritura, lo cual implica que podemos declarar cualquier tipo de retorno. Lo que no podemos hacer es cambiar solo el tipo de retorno. Para sobre cargar un mtodo solo debemos cambiar la lista de argumentos, por ejemplo:

public class Animal { void muevete(){}; } public class Perro extends Animal { String muevete(int numeroPasos) { return null;
} }

Noten que el mtodo de "Animal" tiene un tipo de retorno diferente que el mtodo de " Perro". Esto est bien. En el momento que se ha cambiado la lista de argumentos, hemos sobrecargado el mtodo,

100

entonces el tipo de retorno no tiene que coincidir con el del mtodo de la sper clase, pero por ejemplo, lo siguiente no est permitido: public class Animal { void muevete(){}; } public class Perro extends Animal {
retorno { } } String muevete() //Error, no se puede cambiar solo el tipo de return null;

Sobre escritura y tipos de retorno, retornos covariantes Cuando una clase quiere cambiar la implementacin del mtodo de un mtodo heredado (sobre-escrito), la subclase debe definir un mtodo que debe coincidir exactamente con el mtodo heredado. O, a partir de Java 5, se le permite cambiar el tipo de retorno en el mtodo sobre-escrito siempre y cuando el nuevo tipo de retorno es un subtipo del tipo de retorno declarado en el mtodo a sobre-escribir (superclase). class Alpha { Alpha hacerAlgo(char c)
{ } } return new Alpha();

class Beta extends Alpha

Beta hacerAlgo(char c) //sobre escritura legal en Java 1.5 { return new Beta(); } } En Java 5 este cdigo compilara sin problemas, pero si intentamos compilar este cdigo con Java 1.4, mostrar el siguiente error:

101

attempting to use incompatible return type Otras reglas aplicadas a la sobre-escritura, incluyen aquellas para modificadores de acceso y declaracin de excepciones. Retornando un valor Tenemos que recordar solo 6 reglas para retornar un valor: 1. Podemos retornar null a los mtodos que tengan como tipo de retorno un objeto, ejemplo:

public Button hacerAlgo { return null; } 2. Podemos declarar un arreglo como tipo de retorno sin problemas, ejemplo:

public String[] go() { return new String[] {"Alan", "Alex", "Diego", "y Peke", "Henry", "Cesar", "Pedroww"}; } 3. En un mtodo con un tipo de retorno primitivo, podemos retornar cualquier valor o variable que pueda ser implcitamente convertido al tipo de retorno declarado, ejemplo:

public int go() { char c = c; }


return c; //char es compatible con int

4. En un mtodo con un tipo de retorno primitivo, podemos retornar cualquier valor o variable que pueda ser explcitamente convertido al tipo de retorno declarado, ejemplo:

102

public int go() { float f = 32.5f; return (int) f } 5. No debemos retornar nada desde un mtodo que tiene como tipo de retorno "void", ejemplo:

public void go() { return "Esto es todo"; //Error, no es legal } 6. En un mtodo con un tipo de retorno con referencia a un objeto, podemos retornar cualquier tipo de objeto que pueda ser implcitamente convertido al tipo de retorno declarado, ejemplo:

public Animal getAnimal() { return new Perro(); extiende de Animal } public Object getObject() { int[] nums = {1, 2, 3};

//Asumiendo

que

Perro

return nums; //retorna un arreglo de enteros, el cual sigue siendo un //objeto

} public interface Rebotable{} public class Pelota implements Rebotable{} public class TestPelota { //Mtodo con una interface como tipo de retorno public Rebotable getRebotable() {

103

return new Pelota(); implementador de la interface } }

//Retorna

un

Y con eso terminamos todo lo concerniente con las reglas para el retorno de tipos de valor. Ahora veremos algunos detalles interesantes sobre constructores e instanciacin de objetos. CONSTRUCTORES E INSTANCIACIN Los objetos son construidos. No podemos crear un nuevo objeto sin invocar a un constructor. No podemos crear un nuevo objeto sin invocar al constructor del objeto actual y a los constructores de sus superclases. Los constructores son cdigo que se ejecuta cuando usamos la palabra reservada "new". Tambin pueden ser bloques de inicializacin que se ejecutan al escribir " new" (son como constructores pero un poco distintos), pero vamos a cubrir estos (bloques de inicializacin), y sus homlogos de la inicializacin esttica, ms adelante. Vamos a ver cmo se codifican los constructores y como trabajan en tiempo de ejecucin. Constructores Bsicos Todas las clases, incluyendo las clases abstractas, deben tener un constructor, pero solo porque una clase deba de tener un constructor no quiere decir que necesitamos escribirlo explcitamente. Un constructor es algo como esto: public class Rectangulo { Rectangulo(){} //Constructor Rectangulo }

para

la

clase

Un constructor no retorna ningn tipo de valor. Hay dos cosas que siempre debemos recordar acerca de los constructores:
1.

1. No retornan ningn tipo de valor y

104

2. Llevan el mismo nombre que la clase. Tpicamente los constructores son utilizados para inicializar el estado de las variables de instancia, por ejemplo:
2.

public class Rectangulo { private int ancho;


private int altura; public Rectangulo (int ancho, int altura) { this.ancho = ancho; this.altura = altura; } }

En el caso anterior, la clase " Rectangulo" no tienen un constructor sin argumentos, lo que significa que el siguiente cdigo fallar en tiempo de compilacin: Rectangulo r = new Rectangulo(); //No compila, no coinciden el constructor Pero el siguiente cdigo, compilar correctamente: Rectangulo r = problema, los constructor new Rectangulo(4,2); argumentos coinciden //No con hay el

Es muy comn (y deseable) tener un constructor sin argumentos, independientemente del nmero de constructores sobrecargados que nuestra clase pueda tener (s, los constructores pueden ser sobrecargados). De vez en cuando se tiene una clase en la que no tiene sentido crear una instancia sin necesidad de suministrar informacin al constructor. Un "java.awt.Color", por ejemplo, no puede ser llamado mediante una llamada a un constructor sin argumentos, porque es como decirle a la JVM: "Hazme un nuevo color, y no me importa qu tipo de color sea este... tu decide", realmente creen que la JVM pueda tomar decisiones de ese tipo? Encadenamiento de constructors

105

Sabemos que los constructores son invocados en tiempo de ejecucin cuando usamos la palabra reservada "new" de la siguiente manera: Perro p = new Perro(); Pero qu es lo que realmente sucede cuando escribimos " new Rectangulo()"? Asumamos que "Perro" extiende (hereda) de "Animal" y "Animal" extiende de "Object" 1. El constructor de "Perro" es invocado. Cada constructor invoca al constructor de su superclase con una llamada implcita a "super()", a menos que el constructor invoque a un constructor sobrecargado de la misma clase, pero esto lo veremos ms adelante. 2. 2. El constructor de "Animal" es invocado ("Animal" es la superclase de "Perro") 3. 3. El constructor de "Object" es invocado ("Object" es la ltima superclase de todas las clases, entonces " Animal" extiende de "Object" a pesar de que no escribimos explcitamente "extends Object" en la declaracin de la clase "Animal". Esto es Implcito), en este punto estamos en el cima de la pila. 4. 4. Se asignan los valores explcitos a las variables de instancia. Nos referimos a las variables que declaramos como "int x = 24", donde 24 es el valor explicito (en comparacin con el valor por defecto) de la variable de instancia. 5. 5. El constructor de Object se completa. 6. 6. Se asignan los valores explcitos a las variables de instancia de "Animal" (si tuviera). 7. 7. El constructor de "Animal" completa. 8. 8. Se asignan los valores explcitos a las variables de instancia de "Perro" (si tuviera). 9. 9. El constructor de "Perro" completa. A continuacin mostramos como trabajan los constructores en la pila de llamadas:
1.

106 1. 2. 3. 4.

4.- Object() 3.- Animal() llamada a super() 2.- Perro() llamada a super() 1.- main() llamada a new Perro()

Reglas para los constructores: A continuacin mostramos lo que debemos recordar acerca de los constructores: Un constructor puede tener cualquier modificador de acceso, incluso "private" (un constructor privado quiere decir que solo el cdigo dentro de la clase misma puede instanciar un objeto de ese tipo, as que si la clase con constructor privado quiere permitir que una clase pueda instanciarla, la clase debe proveer un mtodo esttico o variable que permitan acceder a una instancia creada dentro de la clase). El constructor debe tener el mismo nombre que la clase. El constructor no debe retornar ningn tipo de valor. Es legal (pero tonto) tener un mtodo con el mismo nombre de la clase, pero esto no hace que sea un constructor. Si ven que tiene un tipo de retorno, entonces es un mtodo en vez de un constructor . Podemos tener ambos, un mtodo y un constructor con el mismo nombre (el nombre de la clase) en una misma clase y esto no es problema para Java.

Si no escribimos ningn constructor dentro de la clase, un constructor por defecto ser automticamente generado por el compilador. El constructor por defecto SIEMPRE es un constructor sin argumentos. Si deseamos tener un constructor sin argumentos y ya hemos escrito algn otro constructor dentro de la clase, el compilador NO proporcionara un constructor sin argumentos (o cualquier otro constructor). Es decir si hemos escrito algn constructor con argumentos no vamos a tener un constructor sin argumentos a menos que nosotros mismos lo declaremos.

107

Todos los constructores tienen como primera declaracin ya sea una llamada a un constructor sobre cargado " this()" o una llamada al constructor de la superclase "super()", aunque recuerden que esta llamada puede ser insertada por el compilador. Si escribimos un constructor (en lugar de confiar en el constructor por defecto generado por el compilador), y no escribimos una llamada a this() o a super(), el compilador insertar una llamada sin argumentos a super() por nosotros, como una primera declaracin en el constructor. Una llamada a super() puede ser una llamada sin argumentos o podemos incluir argumentos en l. Un constructor sin argumentos no es necesariamente el constructor por defecto, aunque el constructor por defecto siempre es uno sin argumentos. El constructor por defecto es el nico que el compilador provee, pero podemos insertar nuestro propio constructor sin argumentos. No podemos hacer una llamada a un mtodo de instancia o acceder a una variable de instancia hasta que se ejecute el constructor super. Solo variables y mtodos estticos pueden ser accedidos como parte de la llamada a super() o this(). Por ejemplo: super(Animal.NOMBRE) est bien ya que NOMBRE est declarada como variable esttica. Las clases abstractas tienen constructores, y estos constructores son siempre llamados cuando una clase concreta es instanciada. Las interfaces no tienen constructores , las interfaces no son parte del rbol de herencia de un objeto. La nica forma de que un constructor pueda ser invocado es dentro de otro constructor, en decir, no podemos invocar a un constructor de la siguiente manera:

public class Perro { public Perro(){} //Contructor


{ } } public void hacerAlgo() Perro(); //llamada al constructor. Error!!

108

Determinar si un constructor por defecto ser creado El siguiente cdigo muestra a la clase "Perro" con dos constructores: public class Perro { public Perro(){}
}

public Perro(String nombre){}

El compilador insertar un constructor por defecto para la clase anterior? NO!! Y para la siguiente modificacin en la clase?

public class Perro { public Perro(String nombre){} } Ahora el compilador insertar un constructor por defecto? NO!! Y qu hay de esta clase? public class Perro { } El compilador SI generar un constructor por defecto para la clase anterior, porque la clase no tiene ningn constructor definido. Y ahora Qu hay de esta siguiente clase?: public class Perro { void Perro(){} } Podra parecer que el compilador no crea un constructor ya que ya hay un constructor en la clase "Perro". Pero realmente hay un

109

constructor? Revisemos nuevamente la clase anterior, pues eso no es un constructor, es solo un mtodo que tiene el mismo nombre que la clase. Recordemos que el tipo de retorno es un claro indicador de que eso es un mtodo y no un constructor . Cmo se sabe con seguridad que un constructor por defecto ser creado? Porque no declaramos NINGUN constructor en nuestra clase . Cmo se ver el constructor por defecto creado por el compilador? El constructor por defecto tiene el mismo modificador de acceso que la clase. El constructor por defecto no tiene argumentos. El constructor por defecto incluye una llamada sin argumentos al sper constructor (super()).

Qu

sucede

si

el

sper

constructor

tiene

argumentos?

Los constructores pueden tener argumentos as como los mtodos, si tratamos de invocar a un mtodo que toma por ejemplo un int y no le pasamos nada al mtodo, el compilador se comportar de la siguiente manera: class Ejemplo { void tomaUnInt(int valor){} } class Test { public static void main(String args)
{ Ejemplo e = new Ejemplo(); e.tomaUnInt(); //Se est tratando de invocar al metodo "tomaUnInt()" sin argumentos } }

El compilador nos dir que estamos tratando de invocar a

110

"tomaUnInt()" sin pasarle ningn un int. El compilador, segn la versin de la JVM, responder ms o menos de la siguiente manera: Test.java:7: tomaUnInt(int) applied to ()e. tomaUnInt(); in Ejemplo cannot be

Esto quiere decir que debemos pasar los mismos valores o variables que el mtodo acepta y en el mismo orden en el que est declarado en el mtodo, a lo que queremos llegar es que este mecanismo funciona exactamente igual en los constructores. A continuacin mostramos el cdigo que genera el compilador con respecto a los constructores: Cdigo de clase (lo que nosotros Cdigo de constructor generado por el escribimos) compilador class Perro { Perro() { super(); } } class Perro { Perro() { super(); } } public class Perro { public Perro() { super();

class Perro { }

class Perro { Perro(){} }

public class Perro { }

111

Cdigo de clase (lo que nosotros Cdigo de constructor generado por el escribimos) compilador } } class Perro { Perro(String nombre) { super(); } }

class Perro { Perro(String nombre){} }

class Perro { Perro(String Nada, el compilador no necesita insertar nombre) nada { super(); } } class Perro { void Perro(){}
class Perro { } void Perro(){} } }

Perro()
{ super();

("void Perro()", es un mtodo no un constructor) Si el sper constructor (el constructor de su inmediata sper clase o clase padre) tiene argumentos, debemos escribir en la llamada asuper() los argumentos adecuados.

112

Un punto crucial: si nuestra superclase no tiene un constructor sin argumentos, debemos escribir un constructor en la clase (subclase), porque necesitamos un lugar donde insertar, en la llamada a super, los argumentos adecuados. El siguiente cdigo da un ejemplo del problema: class Animal {
} Animal(String nombre){}

class Perro extends Animal

Perro() { super(); //He aqu el problema }

El compilador nos arrojar algo como esto: Perro.java:7: cannot resolve symbol symbol : constructor Animal () location: class Animal super(); // Problema!
^

Otra forma de explicar es la siguiente: Si nuestra superclase no tiene un constructor sin argumentos entonces la subclase no ser capaz de usar el constructor por defecto que te provee el compilador, es decir, el compilador puede solo insertar una llamada a " super()" sin argumentos, no ser capaz de compilar algo como esto: class Animal { Animal(String nombre){} } class Perro extends Animal{}

113

El compilador nos arrojar algo como esto: Animal.java:4: cannot resolve symbol symbol : constructor Animal () location: class Animal class Perro extends Animal { } ^ El compilador explcitamente est haciendo el cdigo que se muestra a continuacin, donde vamos a proveer a " Perro" el mismo constructor que el compilador le proveer: class Animal { Animal(String nombre){} }

class Perro extends Animal { //el constructor a continuacin es idntico al que el compilador proveer Perro() { super(); //Se invoca al constructor sin argumentos de "Animal" el cual no existe!! } } Una ltima cosa que debemos recordar es que los constructores no se heredan, estos no son mtodos los cuales si se heredan. Estos no pueden ser sobre-escritos pero, como hemos visto a lo largo de esta parte de constructores, estos si se pueden sobrecargar. Sobrecarga de constructores Sobrecargar un constructor quiere decir que escribimos diferentes versiones del constructor, cada uno tiene diferente nmero de argumentos, como por ejemplo:

114

class Perro { Perro(){} Perro(String nombre){} } La clase "Perro" anterior muestra dos constructores sobrecargados, uno de ellos toma un String como argumento y el otro no tiene argumentos; este es exactamente igual al constructor que el compilador provee, pero recordemos que una vez que nosotros escribamos un constructor en la clase, como por ejemplo el que toma el String, el compilador ya no nos proveer un constructor por defecto. Si queremos un constructor sin argumentos para sobrecargar el que tiene argumentos nosotros mismo tendremos que escribirlo como en el ejemplo anterior. Sobrecargando un constructor proveemos diferentes maneras de que se pueda instanciar un objeto de nuestra clase, por ejemplo si sabemos el nombre del Perro, podemos pasarselo a un constructor de "Perro" que toma una cadena. Pero si no sabemos el nombre podemos llamar al constructor sin argumentos para que nos provea un nombre por defecto, a continuacin mostramos un ejemplo de lo que estamos hablando: 1. public class Perro { 2. String nombre; 3. Perro (String nombre){ 4. this.nombre = nombre; 5. } 6. 7. Perro(){ 8. this(tomaNombreAleatorio()); 9. } 10. 11. static String tomaNombreAleatorio(){ 12. int x = (int) (Math.random() * 5); 13. String nombre = new String[] {"fido", "tango", "aguao", "rex" , "lassy"}[x]; 14. return nombre; 15. }

115

16. 17. 18. 19. 20. 21. 22. 23. }

public static void main(String args){ Perro a = new Perro(); System.out.println(a.nombre); Perro b = new Perro("flafy"); System.out.println(b.nombre); }

Ejecutando el cdigo unas cuantas veces tendremos un resultado como el siguiente: % java Perro tango flafy % java Perro lassy flafy % java Perro rex flafy % java Perro aguao flafy A continuacin mostramos la pila de llamadas para la invocacin del constructor cuando un constructor es sobrecargado.

4. Object 3. Perro(String nombre) llamada a super() 2. Perro() llamada a this(tomaNombreAleatorio()) 1. main() llamada a new Perro()

Ahora vamos a describir el cdigo desde la parte superior: Lnea 2: declaramos una variable de instancia nombre de tipo String Lnea 3 5: El constructor toma un String y lo asigna a la variable de instancia nombre

116

Lnea 7: Aqu viene lo interesante. Asumiendo que cada Animal necesita de un nombre, pero la persona que lo invoca no siempre debe saber que nombre ser (cdigo de llamada), entonces nosotros establecemos un nombre aleatorio. El constructor sin argumentos genera un nombre aleatorio invocando al mtodo "tomaNombreAleatorio()". Lnea 8: El constructor sin argumentos invoca a su propio constructor sobrecargado que toma un String llamandolo de la misma manera que sera llamado si se hace una nueva instancia del objeto, pasndole un String para el nombre. La invocacin al constructor sobrecargado se hace mediante la palabra reservada "this", pero la utiliza como si fuese un nombre de mtodo, this(). Entonces en la lnea 8 simplemente se est llamando al constructor sin parmetros que est en la lnea 3, pasndole un nombre aleatorio en lugar nosotros establecer el nombre. Lnea 11: Notemos que el mtodo "tomaNombreAleatorio()" est marcado como esttico (static), eso es porque no podemos invocar a un mtodo de instancia (en otras palabras, no esttico) o acceder a una variable de instancia hasta despus de que el super constructor haya sido ejecutado, y hasta que el sper constructor sea invocado desde el constructor en la lnea 3, en lugar que de la lnea 7, la lnea 8 puede usar solo un mtodo esttico para generar un nombre. Si nosotros quisiramos especificar algn nombre en especfico en vez de que se genere aleatoriamente, por ejemplo, " Thor", entonces en la lnea 8 simplemente pondramos this("Thor") en lugar de llamar al mtodo "tomaNombreAleatorio()" para generar un nombre aleatorio. Lnea 12: Esto no tiene nada que ver con el constructor pero es bueno aprenderlo, esto genera un numero entero aleatorio entre 0 y 4; Lnea 13: Estamos creando un nuevo String, pero queremos que este String sea seleccionado aleatoriamente desde una lista, entonces necesitamos hacer esto. Para explicarlo con mejor detalle, en esta sola lnea de cdigo hacemos lo siguiente: 1. 1. Declaramos una variable del tipo String. 2. 2. Creamos un arreglo de Strings (annimo ya que no asignamos el arreglo)

117

3. Obtenemos el String en index [x] (x contiene el numero aleatorio generado en la lnea 12) del recientemente creado arreglo de Strings. 4. 4. Asignamos el String obtenido del arreglo a la variable de instancia "nombre". Esto hubiera sido ms fcil de leer si hubiramos escrito esto:
3.

String[] lista = {"fido", "rex" , "lassy"}; String nombre = lista[x];


"tango",

"aguao",

Lnea 18: Invocamos al constructor sin argumentos (generando un nombre aleatorio que despus ser enviado al constructor que recibe un String). Lnea 20: Invocamos al constructor sobrecargado que toma un String que representa el nombre. El punto cable en el cdigo antes escrito est en la lnea 8, en lugar de llamar a super(), estamos llamando a "this()", y "this()" siempre significa llamar a otro constructor en la misma clase. Pero qu sucede al momento de llamar a " this()"? Tarde o temprano el constructor "super()" ser llamado, una llamada a "this()" solo significa que estamos retrasando lo inevitable, ya que algn constructor en algn lugar debe hacer una llamada a " super()". La regla clave: la primera lnea de un constructor debe ser una llamada a "super()" o una llamada a "this()". Sin excepciones. Si el constructor " A()" tiene una llamada a "this()", el compilador sabe que el constructor " A()" no ser e que invoque a "super()". La regla anterior significa que un constructor nunca podr tener ambas llamadas, es decir no podr llamar a la vez a " this()" y a "super()". Porque alguna de estas llamadas debe ser la primera sentencia en un constructor, no podemos usar ambas en un mismo constructor. El

118

compilador tampoco insertar una llamada a " super()" si el constructor tiene una llamada a "this()". Qu suceder si nosotros tratamos de compilar el siguiente cdigo? class A { A() {
} A(String s) { this(); } }

this("test");

El compilador puede que no capte el problema (esto depende del compilador). Este asume que sabemos lo que estamos haciendo. Captan el problema? Sabemos que el sper constructor siempre debe de ser llamado, entonces Dnde podra ir la llamada a "super()"? Recordemos que el compilador no inserta ningn constructor por defecto si nosotros ya insertamos uno o ms constructores en nuestra clase, y cuando el compilador no inserta un constructor por defecto todava puede insertar una llamada a super() en algn constructor que no tenga explcitamente una llamada a super(), a menos que, el constructor ya tenga una llamada a this() y recordemos que un constructor no puede tener ambas llamadas (this() y super()). Entonces en el cdigo anterior Dnde va la llamada a super()? Si los nicos dos constructores en la clase tienen una llamada a this(), en efecto nosotros tenemos el mismo problema que tendramos si escribimos los siguientes mtodos: public void ir() { hacerAlgo(); } public void hacerAlgo()

119

{ } ir();

Ahora pueden ver el problema? Podemos ver que el stack explota. Ya que se llamaran uno a otro indefinidamente hasta que la maquina o la JVM estalle (esperamos que lo segundo suceda primero o lo primero?). Regresando al ejemplo de los constructores podemos decir que dos constructores sobrecargados y ambos con una llamada a this() son dos constructores llamndose uno a otro una y otra y otra vez, resultando en: % java A Exception in java.lang.StackOverflowError

thread

"main"

El beneficio de tener constructores sobrecargados es que ofrecen formas flexibles de poder instanciar un objeto de nuestra clase. El beneficio de que un constructor invoque a otro constructor sobrecargado es evitar duplicacin de cdigo, en el ejemplo anterior no hubo algn otro cdigo para establecer el nombre, pero imaginen que despus de la lnea 4 an hay muchas cosas que podramos hacer. VARIABLES Y MTODOS ESTTICOS El modificador "static" tiene un impacto tan profundo en el comportamiento de un mtodo o una variable que debemos tratando como un concepto totalmente separado de los dems modificadores. Para entender la manera en que un miembro esttico trabaja, primero veremos las razones por las cuales utilizaramos alguno. Imaginen que tenemos una clase de utilidad la cual siempre se ejecuta de la misma manera, su nica funcin es devolver, digamos, un nmero al azar. No nos importa en cul instancia de la clase se ejecuta el mtodo, ya que siempre se comporta de la misma manera. En otras palabras, el comportamiento del mtodo no tiene ninguna dependencia en el estado (valores de variable de instancia) de un objeto. Entonces Porque es necesario un objeto cuando el mtodo

120

nunca ser instanciado? Por qu no solo pedimos a la clase en si misma que ejecute el mtodo? Ahora imaginemos otro escenario: supongamos que queremos mantener un contador corriendo para todas las instancias de una clase en particular. En dnde incluiremos la variable? No va a trabajar si la incluimos como variable de instancia dentro de las clases en las cuales se quiere hacer el seguimiento, ya que el contador siempre se iniciara a un nuevo valor por defecto cada que nosotros creamos una nueva instancia. La respuesta a los dos escenarios anteriores (el mtodo de utilidad que se ejecuta siempre de la misma manera, y el contador para mantener el total de instancias actualizado) es utilizar el modificador static. Las variables y mtodos marcados con static pertenecen a la clase y no a una instancia en particular . Podemos usar un mtodo o variable static sin tener instancias de esa clase , solo necesitamos que la clase est disponible para invocar un mtodo static o acceder a una variable static. Una variable esttica de una clase ser compartida por todas las instancias de esa clase, slo hay una copia. El siguiente cdigo utiliza una variable static que ser utilizada como contador: class Oveja { static int contadorOvejas = 0; //Declaramos e inicializamos la variable esttica public Oveja() { contadorOveja += 1; //Modificamos el valor en el constructor } public static void main(String args) {

121

new Oveja(); new Oveja(); new Oveja();


System.out.println("El contador de ovejas est ahora en: " + contadorOveja); } }

En el cdigo anterior, la variable esttica " contadorOveja" es establecida en cero cuando la clase "Oveja" es cargada por primera vez por la JVM, antes de que cualquier instancia de " Oveja" sea creada (en realidad no se necesita inicializar la variable esttica en cero, las variable estticas obtienen los mismos valores por defecto que las variables de instancia obtienen). Cada vez que una instancia de la clase "Oveja" es creada el constructor de "Oveja" es ejecutado y la variable "contadorOveja" es incrementada. Cuando este cdigo es ejecutado, tres instancias de " Oveja" son creadas en el "main()", y el resultado es el siguiente: El contador de ovejas est ahora en: 3 Ahora imaginemos que sucedera si la variable " contadorOveja" fuera una variable de instancia (es decir, una variable no esttica): class Oveja { int contadorOvejas = 0; //Declaramos inicializamos la variable de instancia

public Oveja() { contadorOveja += 1; //Modificamos el valor en el constructor } public static void main(String... args) { new Oveja(); new Oveja(); new Oveja();

122
System.out.println("El contador de ovejas est ahora en: " + contadorOveja); } }

Cuando este cdigo es ejecutado, este puede todava crear tres instancias de la clase "Oveja" en el "main()", pero el resultado es un error de compilacin. No podemos compilar este cdigo, mucho menos ejecutarlo, porque obtenemos el siguiente error: Exception in thread "main" java.lang.RuntimeException: Uncompilable source code non-static variable contadorOveja cannot be referenced from a static context System.out.println("El contador de ovejas est ahora en: " + contadorOveja); ^ La JVM no sabe a cul objeto "contadorOveja" de "Oveja" estamos intentando de acceder. El problema est en que el mtodo " main()" en s mismo es un mtodo esttico. Un mtodo esttico no puede acceder a una variable no esttica (variable de instancia) porque hay que recordar que para acceder a una variable esttica no se necesita una instancia de la clase, ya que la variable pertenece a la clase misma. Eso no quiere decir que no hay instancias de la clase con vida en la heap, si las hay, pero el mtodo esttico no sabe nada de ellas. Lo mismo se aplica a los mtodos de instancia, un mtodo esttico no pueden invocar directamente un mtodo no esttico. Piensen que static=clase, no-esttico=instancia. Haciendo que el mtodo llamado por la JVM ("main()") sea un mtodo esttico laJVM no tiene que crear una instancia de la clase slo para iniciar la ejecucin de cdigo. Accediendo a mtodos y variables estticos Puesto que no es necesario tener una instancia para invocar un mtodo esttico o acceder a una variable esttica, entonces cmo invocar o utilizar un miembro esttico? Cul es la sintaxis? Sabemos

123

que con un mtodo de instancia regular, se utiliza el operador punto "." en una referencia, por ejemplo: class Oveja { static int contadorOvejas = 0; //Declaramos e inicializamos la variable esttica public Oveja() { contadorOveja += 1; //Modificamos el valor en el constructor } public static void main(String... args) { new Oveja(); new Oveja(); new Oveja();

System.out.println("El contador de ovejas est en: " + Oveja.contadorOveja); } }

Pero para que sea realmente confuso, el lenguaje Java tambin permite el uso de una variable de referencia de objeto para acceder a un miembro esttico: Oveja ov = new Oveja(); ov.contadorOveja; //Accediendo a la variable esttica contadorOveja usando o En el cdigo anterior, hemos instanciado una " Oveja" asignando "new Oveja" a la variable de referencia "ov", y despus usamos la referencia "ov" para invocar al mtodo esttico. Pero a pesar de que se est utilizando una instancia especfica para acceder al mtodo esttico, las reglas no han cambiado. Esto no es ms que un truco de sintaxis que nos permite utilizar una variable de referencia a objeto (pero no el objeto que se refiere) para llegar a un mtodo o variable esttica, pero el miembro esttico sigue ignorando la instancia utiliza para invocar al miembro esttico. En el ejemplo de la Oveja, el compilador sabe que la variable de referencia " ov" es del tipo

124

de Oveja, por lo que el mtodo esttico de la clase Oveja se ejecuta sin conocimiento o inters para la instancia de la Oveja del otro extremo de la referencia "ov". En otras palabras al compilador le importa solo que la variable de referencia " ov" sea del tipo Oveja. El compilador en realidad hace una transformacin a la llamada que acabamos de hacer. Cuando ve que estamos tratando de invocar a un miembro esttico, reemplaza la variable de instancia que estamos usando por la clase en la que est dicho miembro. Esto quiere decir que reemplazara esta llamada: Oveja ov = new Oveja(); ov.contadorOveja; Por esta otra: Oveja ov = new Oveja(); Oveja.contadorOveja; As que como podemos ver, finalmente quedamos con una llamada al miembro esttico, a travs de la clase a la que pertenece dicho miembro. La siguiente imagen describe el efecto del modificador static en mtodos y variables:

125

Finalmente, recuerden que los mtodos estticos no pueden ser sobrescritos. Pero esto no significa que no puedan ser redefinidos en una subclase. Redefinir y sobrescribir no son la misma cosa. A continuacin mostramos un ejemplo sobre redefinir (no sobrescribir) un mtodo marcado como static: class Animal { static void hacerAlgo() { System.out.println("a"); } }

126

class Perro extends Animal { static void hacerAlgo() { System.out.println("b"); redefinir, no sobrescribir }

//Esto

es

public static void main(String... args) { Animal[] a = {new Animal(), new Perro(), new Animal()}; for(int i = 0; i < a.length; i++ )
{ a[i].hacerAlgo(); //Invoca al mtodo esttico } } }

Ejecutando el cdigo anterior se produce la siguiente salida: a a a

Recuerden, la sintaxis "a[i].hacerAlgo()" es solo un atajo (un truco de sintaxis). El compilador lo sustituye con algo como "Animal.hacerAlgo()", como vimos hace un momento. COHESIN Y ACOPLAMIENTO (COUPLING AND COHESION): Estos dos temas, la cohesin y el acoplamiento, tienen que ver con la calidad de un diseo orientado a objetos . En general un buen diseo pide un acoplamiento bajo y evita un acoplamiento estrecho y un buen diseo orientado a objetos pide una alta cohesin y evita la baja cohesin. Como con la mayora de las discusiones sobre diseo de orientacin a objetos, las metas de una aplicacin son:

Facilidad de creacin. Facilidad de mantenimiento.

127

Facilidad de mejora.

Acoplamiento Vamos a empezar haciendo un intento sobre la definicin de acoplamiento. Acoplamiento es el grado en el cual una clase sabe acerca de otra clase. Si el nico conocimiento que la clase " A" tiene acerca de la clase "B", es lo que la clase "B" ha puesto de manifiesto a travs de su interface, se dice que la clase " A" y "B" estn dbilmente acopladas, lo cual es algo bueno. Si por otro lado, la clase " A" se basa en una parte de la clase "B", que no es parte de la interface de la clase "B", entonces el acoplamiento entre estas dos clases es ms estrecho, y esto no es algo bueno. En otras palabras, si la clase " A" sabe ms de lo que debera de la forma en que se implement " B", entonces "A" y "B" estn estrechamente acopladas. Usando este segundo escenario, imaginemos lo que sucede cuando la clase "B" sea mejorada. Es muy posible que el desarrollador que mejore a "B" no tenga conocimiento acerca de " A" Y porque debera de tenerlo? El desarrollador de la clase " B" debe pensar (y es correcto) que todas las mejoras que no quiebren o rompan la interfaz de la clase deben ser seguras, por lo que podra cambiar alguna parte que no tenga que ver con la interface en la clase. Si las dos clases estn estrechamente acopladas, este cambio provocara que la clase " A" se quiebre. Veamos un ejemplo obvio de acoplamiento fuerte, que ha sido posible gracias a una pobre encapsulacin: class Impuestos { float ratio; float calculaImpuestoVentaPeru() { RatioImpuestoVentas RatioImpuestoVentas(); ratio = riv.ratioVentas; debera ser una llamada a un

riv

new

/*Mal, esto

128

mtodo get para obtener la variable, por ejemplo: riv.getRatioVentas("PE"); } } */ ratio =

class RatiosImpuestoVentas { public float ratioVentas; //Debera ser privado public float ajusteRatioVentas; //Debera ser privado public float getRatioVentas(String pais)
ratioVentas = new Impuestos().calculaImpuestoVentaPeru(); //Mal otra vez hacer basado en los clculos de la regin. return ajusteRatioVentas; {

} }

Todas las aplicaciones orientadas a objetos que no son triviales son una mezcla de muchas clases e interfaces trabajando juntas. Idealmente, todas las interacciones entre los objetos en un sistema orientado a objetos deben utilizar sus APIs, en otras palabras, los contratos de las clases de los objetos respectivos. Tericamente, si todas las clases en una aplicacin tienen bien diseada sus APIs, entonces debera ser posible para todas las interacciones entre las clases el uso de las APIs de forma exclusiva. Como hemos comentado anteriormente en este captulo, un aspecto de buen diseo de una clase y el diseo de la API es que las clases deben estar bien encapsuladas. Cohesin Mientras que el acoplamiento tiene que ver con cmo las clases interactan con las otras clases, la cohesin es acerca de cmo una simple clase es diseada. El termino cohesin es usado para indicar el grado para el cual una clase tiene un propsito simple bien enfocado . Hay que mantener en mente que la cohesin es un concepto subjetivo al igual que el acoplamiento. Cuanto ms enfocada

129

sea la clase en una sola tarea, mayor su cohesin, lo cual es bueno. El beneficio clave de la alta cohesin, es que estas clases son mucho ms fciles de mantener (y menos frecuentes a ser cambiadas) que las clases con baja cohesin. Otro beneficio de la alta cohesin es que las clases con un propsito bien enfocado tienden a ser ms reutilizables que otras clases. Veamos un ejemplo: class ReportePresupuesto { void conectarBaseDatos(){} void generarReportePresupuesto(){} void guardarArchivo(){} void imprimir(){} } Ahora imaginen que su jefe llega y dice, "Eh, sabes de la aplicacin de contabilidad que estamos trabajando? Los clientes decidieron que tambin van a querer generar un informe de proyeccin de ingresos, y que quieren hacer algunos informes de inventario tambin". Tambin les dice que se aseguren de que todos estos informes les permitan elegir una base de datos, seleccione una impresora, y guardar los informes generados a ficheros de datos ... Ouch! En lugar de poner todo el cdigo de impresin en una clase de informe, probablemente hubiera sido mejor usar el siguiente diseo desde el principio: class ReportePresupuesto { Opciones getOpcionesReporte() {} void generarReportePresupuesto(Opciones o){} } class ConexionBaseDatos { ConexionBD getConexion(){} } class Impresion {

130

OpcionesImpresion getOpcionesImpresion(){} } class AlmacenArchivos { OpcionesGuardado getOpcionesGuardado(){} } Este diseo es mucho ms cohesivo. En lugar de una clase que hace todo, hemos roto el sistema en cuatro clases principales, cada uno con un rol muy especfico (cohesin). Como hemos construido estas clases especializadas, reutilizables, y va a ser mucho ms fcil escribir un nuevo informe, dado que ya tenemos la clase de conexin de bases de datos, la clase de impresin, y la clase para guardar archivos, y eso significa pueden ser reutilizados por otras clases pueden desear imprimir un informe. Este es el fin del segundo tutorial, donde hemos aprendido un poco sobre algunos de los conceptos ms importantes de la orientacin a objetos que son necesarios para el examen de certificacin, y para nuestra vida como programadores en general. El la prxima entrega seguiremos aprendiendo ms temas importantes para nosotros como programadores Java y en particular para los que deseen certificarse como programadores Java. Muchas gracias a Alan Cabrera Avanto, de Trujillo Per por este tutorial. Saludos.

You might also like