Ir a contenido principal

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. Cierta información de su perfil de developerWorks será mostrada públicamente, pero usted puede editar la información en cualquier momento. Su nombre, apellido (a menos que usted elija ocultarlo) y nombre de usuario acompañarán el contenido que usted publique.

Toda la información enviada es segura.

  • Cerrar [x]

La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

Toda la información enviada es segura.

  • Cerrar [x]

Introducción a la programación Java, parte 2: Construcciones para aplicaciones del mundo real

Funciones más avanzadas del lenguaje Java

J. Steven Perry, Consultor Director, Makoto Consulting Group, Inc.
J. Steven Perry
J. Steven Perry es desarrollador de software, arquitecto y fanático general de Java que ha estado desarrollando software profesionalmente desde 1991. Sus intereses profesionales abarcan desde el funcionamiento interno de la JVM hasta el modelo UML y todo lo que está en el medio. Steve tiene una pasión por escribir y ser mentor. Es el autor de Java Management Extensions (O'Reilly), Log4j (O'Reilly) y los artículos de developerWorks de IBM "Joda-Time" y OpenID for Java Web applications". Pasa tiempo libre con sus tres hijos, anda en bicicleta y enseña yoga.

Resumen:  En la Parte 1 de este tutorial, el programador Java™ profesional J. Steven Perry presentó la sintaxis del lenguaje Java y las bibliotecas que usted necesita para escribir aplicaciones Java simples. La Parte 2, todavía orientada a desarrolladores nuevos en el desarrollo de aplicaciones Java, presenta las construcciones de programación más sofisticadas requeridas para crear complejas aplicaciones Java del mundo real. Los temas que se cubren incluyen manejo de excepciones, herencia y abstracción, expresiones regulares, genéricos, E/S Java y serialización Java.

Ver más contenido de esta serie

Fecha:  10-12-2012
Nivel:  Introductoria PDF:  A4 and Letter (927 KB | 54 páginas)Get Adobe® Reader®

Comentario:  

Herencia

Usted se ha encontrado con ejemplos de herencia algunas veces ya en este tutorial. Esta sección revisa algunos de los materiales de la Parte 1 sobre la herencia y explica en más detalle cómo funciona la herencia, — incluye jerarquía de herencia, constructores y herencia y abstracción de herencia.

Cómo funciona la herencia

Las clases en el código Java existen en jerarquías. Las clases que están por encima de una clase dada en una jerarquía son las superclases de esa clase. Esa clase en particular es una subclase de cada clase que está más arriba en la jerarquía. Una subclase hereda de sus superclases. La clase java.lang.Object está en la parte superior de la jerarquía de clases, es decir que cada clase Java es una subclase de, y hereda del, Object.

Por ejemplo, suponga que usted tiene una clase Person que se parece a la del Listado 12:


Listado 12. Clase Person pública

package com.makotogroup.intro;

// . . .
public class Person {
 public static final String GENDER_MALE = "MALE";
 public static final String GENDER_FEMALE = "FEMALE";
 public Person() {
 //Nothing to do...
 }
 private String name;
 private int age;
 private int height;
 private int weight;
 private String eyeColor;
 private String gender;
// . . .

}

La clase Person en el Listado 12 hereda implícitamente del Object. Debido a que eso se asume para cada clase, no necesita escribir extends Object para cada clase que usted define. Pero, ¿qué significa decir que una clase hereda de su superclase? Significa simplemente que Person tiene acceso a las variables y métodos expuestos en sus superclases. En este caso, Person puede ver y usar métodos y variables públicas de Object y métodos y variables protegidas de Object.


Definición de una jerarquía de clases

Ahora suponga que tiene una clase Employee que hereda de Person. Su definición de clase (o gráfico de herencia) se parecería a algo como esto:

public class Employee extends Person {

 private String taxpayerIdentificationNumber;
 private String employeeNumber;
 private BigDecimal salary;
// . . .
}

Herencia múltiple versus simple

Los lenguajes como C++ soportan el concepto de herencia múltiple: en cualquier punto de la jerarquía, una clase puede heredar de una o más clases. El lenguaje Java soporta solo la herencia simple, que significa que solo puede usar la palabra clave extends con una sola clase. Por lo tanto, la jerarquía de clases para cualquier clase Java dada consiste siempre en un línea derecha hasta el java.lang.Object.

Sin embargo, el lenguaje Java soporta la implementación de interfaces múltiples en una sola clase, lo que le da un método alternativo de tipos para la herencia simple. Le presentaré las interfaces múltiples más adelante en el tutorial.

El gráfico de herencia Employee insinúa que Employee tiene acceso a todas las variables y métodos públicos y protegidos en Person (porque lo extiende directamente), así como también en Object (porque en realidad extiende también esa clase, aunque indirectamente). Sin embargo, debido a que Employee y Person están en el mismo paquete, Employee también tiene acceso a las variables y métodos privados del paquete (a veces denominados amigables) en Person.

Para profundizar un paso más en la jerarquía de clases, podría crear una tercera clase que extiende Employee:

public class Manager extends Employee {
// . . .
}

En el lenguaje Java, cualquier clase puede tener, como mucho, una superclase pero una clase puede tener cualquier cantidad de subclases. Eso es lo más importante de recordar sobre la jerarquía de herencia en el lenguaje Java.


Constructores y herencia

Los constructores no son plenos miembros orientados a objetos, por lo tanto no son heredados. En cambio, usted debe implementarlos explícitamente en subclases. Antes de eso, revisaré algunas reglas básicas sobre cómo se definen e invocan los constructores.

Conceptos básicos sobre los constructores

Recuerde que un constructor siempre tiene el mismo nombre que la clase que suele construir y no tiene un tipo de retorno. Por ejemplo:

public class Person {
 public Person() {
 }
}

Cada clase tiene por lo menos un constructor y, si no define explícitamente un constructor para su clase, el compilador generará uno por usted, denominado el constructor predeterminado. La definición de la clase anterior y ésta son idénticas en el modo en que funcionan:

public class Person {
}

Invocación de un constructor de superclase

Para invocar a un constructor de superclase distinto del constructor predeterminado, debe hacerlo explícitamente. Por ejemplo, suponga que Person tiene un constructor que toman el nombre del objeto Person que se está creando. Desde el constructor predeterminado de Employee, usted podría invocar al constructor Person que se muestra en el Listado 13:


Listado 13. Inicialización de un nuevo Employee

public class Person {
 private String name;
 public Person() {
 }
 public Person(String name) {
   this.name = name;
 }
}

// Meanwhile, in Employee.java
public class Employee extends Person {
 public Employee() {
   super("Elmer J Fudd");
 }
}

Sin embargo, probablemente usted nunca quiera inicializar un nuevo objeto Employee de este modo. Hasta que se sienta más cómodo con los conceptos orientados a objetos, y la sintaxis Java en general, es una buena idea implementar constructores de superclases en subclases, si piensa que los necesitará, e invocarlos homogéneamente El Listado 14 define un constructor en Employee que se parece al de Person para que coincidan. Es mucho menos confuso desde el punto de vista del mantenimiento.


Listado 14. Invocación homogénea de una superclase

public class Person {
 private String name;
 public Person(String name) {
   this.name = name;
 }
}
// Meanwhile, in Employee.java
public class Employee extends Person {
 public Employee(String name) {
   super(name);
 }
}

Declaración de un constructor

Lo primero que hace un constructor es invocar al constructor predeterminado de su superclase inmediata, a menos que usted, — en la primera línea de código del constructor, — invoque un constructor diferente. Por ejemplo, estas dos declaraciones son funcionalmente idénticas, así que elija una:

public class Person {
 public Person() {
 }
}
// Meanwhile, in Employee.java
public class Employee extends Person {
 public Employee() {
 }
}

O:

public class Person {
 public Person() {
 }
}
// Meanwhile, in Employee.java
public class Employee extends Person {
 public Employee() {
   super();
 }
}

Constructores sin argumentos

Si usted proporciona un constructor alternativo, debe proporcionar explícitamente el constructor predeterminado, sino no estará disponible. Por ejemplo, el siguiente código le daría un error de compilación:

public class Person {
 private String name;
 public Person(String name) {
   this.name = name;
 }
}
// Meanwhile, in Employee.java
public class Employee extends Person {
 public Employee() {
 }
}

Este ejemplo no tiene un constructor predeterminado porque proporciona un constructor alternativo sin incluir explícitamente el constructor predeterminado. Esta es la razón por la que el constructor predeterminado a veces se denomina el constructor sin argumento (o no-arg); debido a que hay condiciones bajo las cuales no se incluye, no es realmente predeterminado.

Cómo los constructores invocan a constructores

Un constructor puede invocar a otro constructor de adentro de una clase al usar la palabra clave this, junto con una lista de argumentos. Del mismo modo que super(), la llamada this() debe ser la primera línea del constructor. Por ejemplo:

public class Person {
 private String name;
 public Person() {
   this("Some reasonable default?");
 }
 public Person(String name) {
   this.name = name;
 }
}
// Meanwhile, in Employee.java

Verá este modismo frecuentemente, en el que un constructor delega a otro, al pasar algún valor predeterminado si se invoca ese constructor. También es una excelente forma de agregar un constructor nuevo a una clase mientras se minimiza el impacto en el código que ya usa un constructor más antiguo.

Niveles de acceso de los constructores

Los constructores pueden tener cualquier nivel de acceso que usted quiera y se imponen ciertas reglas de visibilidad. La Tabla 1 resume las reglas de acceso de los constructores:


Tabla 1. Reglas de acceso de los constructores
Modificador de acceso de los constructores Descripción
públicoCualquier clase puede invocar un constructor
protegidoCualquier clase en el mismo paquete o cualquier subclase puede invocar un constructor.
Sin modificador (privado del paquete)Cualquier clase en el mismo paquete puede invocar un constructor.
privadoSolo la clase en que se definió el constructor puede invocar a un constructor.

Tal vez pueda pensar en casos de uso en los que los constructores se declararían protegidos o incluso privados del paquete pero, ¿de qué modo es un constructor privado útil? He usado constructores privados cuando no quería permitir una creación directa de un objeto por medio de la palabra clave new al implementar, digamos, el patrón Factory (vea Resources). En ese caso, se usaría un método estático para crear instancias de la clase y se le permitiría a ese método, al estar incluido en la clase misma, invocar el constructor privado:


Herencia y abstracción

Si una subclase altera temporalmente a un método de una superclase, ese método está esencialmente oculto porque llamar a ese método por medio de una referencia a la subclase invoca a la versión de subclase del método, no a la versión de la superclase. Esto no quiere decir que el método de la superclase ya no sea accesible. La subclase puede invocar el método de superclase al prologuear el nombre del método con la palabra clave super (y a diferencia de lo que sucede con las reglas del constructor, esto se puede hacer desde cualquier línea en el método de subclase o incluso en un método completamente diferente). De forma predeterminada, un programa Java llamará al método de subclase si se invoca por medio de una referencia a la subclase.

Lo mismo corresponde para las variables, siempre que el interlocutor tenga acceso a la variable (es decir, la variable es visible al código que intenta acceder a ella). Esto puede causarle un sinfín de dolor mientras gana competencia en la programación Java. Sin embargo, Eclipse proporcionará varios avisos de que está ocultando una variable de una superclase o de que una llamada de método no llamará lo que usted piensa que hará.

En un contexto OOP, abstracción se refiere a generalizar datos o comportamientos a un tipo más alto en la jerarquía de herencia que la clase actual. Cuando usted mueve variables o métodos de una subclase a una superclase, se dice que está abstrayendo esos miembros. La razón principal para hacer esto es reutilizar un código común al empujarlo tanto alto como se pueda en la jerarquía. Tener un código común en un lugar hace que sea más fácil de mantener.

Clases y métodos abstractos

Hay momentos en los que querrá crear clases que solo sirven como abstracciones y no necesariamente tienen que crearse alguna vez como instancias. Dichas clases se denominan clases abstractas. Del mismo modo, descubrirá que hay momentos en los que ciertos métodos tienen que implementarse de forma diferente para cada subclase que implemente la superclase. Dichos métodos son métodos abstractos. Aquí hay algunas reglas básicas para clases y métodos abstractos:

  • Cualquier clase se puede declarar abstracta.
  • No se pueden crear instancias de las clases abstractas.
  • Un método abstracto no puede contener un cuerpo del método.
  • Cualquier clase con un método abstracto debe declararse abstracta.

Uso de la abstracción

Suponga que usted no quiere permitir que la clase Employee se cree como instancia de modo directo. Simplemente la declara con la palabra clave abstract y listo:

public abstract class Employee extends Person {
// etc.
}

Si usted intenta ejecutar este código, obtendrá un error de compilación:

public void someMethodSomwhere() {
 Employee p = new Employee();// compile error!!
}

El compilador reclama que el Employee es abstracto y no se puede crear como instancia.

El poder de la abstracción

Suponga que necesita un método para examinar el estado de un objeto Employee y que se asegure que sea válido. Esta necesidad parecería ser común a todos los objetos Employee pero se comportaría de modo bastante diferente entre todas las subclases potenciales que no habría potencial para la reutilización. En ese caso, se declara el método validate() como abstracto (lo que obliga a todas las subclases a implementarlo):

public abstract class Employee extends Person {
 public abstract boolean validate();
}

A cada subclase directa de Employee (tal como Manager) ahora se le requiere implementar el método validate(). Sin embargo, una vez que una subclase ha implementado el método validate(), ninguna de sus subclases necesita implementarlo.

Por ejemplo, suponga que tiene un objeto Executive que amplía a Manager. Esta definición sería perfectamente válida:

public class Executive extends Manager {
 public Executive() {
 }
}

Cuándo (no) abstraer: Dos reglas

Como primera regla general, no abstraiga en su diseño inicial. Usar clases abstractas prematuramente en el diseño lo obliga a recorrer cierta ruta y eso puede restringir su aplicación. Recuerde, el comportamiento común (que es todo el punto de tener clases abstractas) siempre puede refactorizarse aun más en el gráfico de herencia. Casi siempre es mejor hacer esto una vez que haya descubierto que sí lo necesita. Eclipse tiene un maravilloso soporte para la refactorización.

En segundo lugar, con lo poderosos que son, resístase al uso de las clases abstractas cuando pueda. A menos que sus superclases contengan muchos comportamientos comunes, y por sí solos no son realmente significativos, deje que permanezcan no abstractas. Los profundos gráficos de herencia pueden hacer que el mantenimiento de códigos sea difícil. Considere el equilibrio entre las clases que son muy grandes y los códigos plausibles de ser mantenidos.


Asignaciones: Clases

Cuando asigne una referencia de una clase a una variable de un tipo que pertenece a otra clase, puede hacerlo pero hay reglas. Observemos este ejemplo:

Manager m = new Manager();
Employee e = new Employee();
Person p = m; // okay
p = e; // still okay
Employee e2 = e; // yep, okay
e = m; // still okay
e2 = p; // wrong!

La variable de destino debe ser de un supertipo de la clase que pertenece a la referencia de origen o sino el compilador le dará un error. Básicamente, lo que sea que se encuentre al lado derecho de la asignación debe ser una subclase o la misma clase de lo que está a la izquierda. Si no es así, es posible que las asignaciones de los objetos con diferentes gráficos de herencia (tales como Manager y Employee) se asignen a una variable del tipo incorrecto. Considere este ejemplo:

Manager m = new Manager();
Person p = m; // so far so good
Employee e = m; // okay
Employee e = p; // wrong!

Mientras un Employee es una Person, definitivamente no es un Manager y el compilador fuerza esto.

5 de 14 | Anterior | Siguiente

Comentario



static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=tecnologia Java
ArticleID=850709
TutorialTitle=Introducción a la programación Java, parte 2: Construcciones para aplicaciones del mundo real
publish-date=12102012
author1-email=steve.perry@makotoconsulting.com
author1-email-cc=