Está en la página 1de 23

9.

- Modificadores y genéricos 1

GUIONES DE CLASE
PROGRAMACIÓN JAVA

Iñaki Martín

CAPÍTULO 9
MODIFICADORES
©

Y CLASES GENERICAS
Temario de curso JavaSE

© Iñaki Martín
9.- Modificadores y genéricos Ambito (scope) de una variable 2

Ambito de una variable


• Una variable “nace” y “vive” en el ámbito (bloque) en el que declara, no donde se inicializa
• El ámbito esta limitado al bloque de llaves cuya llave de inicio es la mas cercana a la declaración de la variable,

siempre contando desde ésta hacia atrás en el programa


• Una variable puede tener varios estados
Iñaki Martín

✴ Muerta. Una variable muere por dos razones:


a) Su valor se pierde. Solo ocurre si es una referencia (tipo objeto), y ocurre cuando su valor pase a null
b) Se acaba la ejecución del ámbito donde vive
En ambos casos, la variable o referencia queda sin valor a espera de que el recolector de basura la depure.
©

public class OperacionesTontas {


✴ Viva, dentro de ámbito. Se puede tratar de : static int varglobal = 3;
Temario de curso JavaSE

a) Una variable de instancia, cuyo ámbito es static public int intAleatorio (int min, int max) {
int sol = (int) (Math.random () * (max - min + 1) + min);
toda la clase, return sol;
}
b) Una variable de método, cuyo ámbito es public static int calcular (int valorparametro) {
todo el metodo, // Aqui dentro, var1 esta viva, pero fuera de ámbito
// No se puede acceder a var1, pero si a varglobal
c) Una variable de bloque cuyo ámbito es int varlocal;
if (varglobal < valorparametro) {
todo el bloque varlocal = varglobal++;
int res = varglobal;
return res;
✴ Viva, fuera de ámbito. Puede darse por ejemplo }
return 0;
durante la ejecución de un método, y desde éste }

la invocación a otro método. Durante la ejecución public static void main (String[] args) {
int res = calcular (10);
del segundo método, una variable local del primer // A estas alturas varlocal ya no existe, esta muerta
método no está visible, pero esta viva, no está int var1 = intAleatorio (10, 50);
int h1 = intAleatorio (1 , 60);
muerta, y volverá a estar visible cuando el segundo // varglobal sigue existiendo aqui

método termine y vuelva el control al primero }


}
9.- Modificadores y genéricos Valores por defecto de variables 3

Valores por defecto

• Esta lista explica los valores por defecto que toman diferentes tipos de variables, según donde se declaren:
Tipos de variables Valores que toman

Variables de instancia Tipos primitivos: byte, short, int, long 0


Iñaki Martín

float, double 0.0


boolean false
char \u0000'
Tipos referencia (objeto) null
(objetos)
©

arrays null
Variables locales (a Tanto primitivas como referencias a objetos, NO tienen valor por
defecto.
Temario de curso JavaSE

métodos o a bloques)
Una vez declaradas y sin asignacion, no tienen valor alguno, ni
siquiera null
public static void main (String[] args) {
Date unDate;
if (unDate == null) // da ERROR, pues unDate ni siquiera es null
System.out.println("unDate es null");
}

• Valores por defecto del Contenido de los arrays.


Aunque una variable array (que es un objeto) sea null, o no sea nada, los componentes del array SIEMPRE se
inicializan con valores por defecto como si fueran variables de instancia

int [] aa = new int [3];


System.out.println(aa[2]); // ok, no da error, e imprime 0
9.- Modificadores y genéricos Modificadores de acceso. Tabla resumen I 4

Modificadores de acceso
• Son unas palabras reservadas de Java que indican donde son visibles los elementos a los que afectan
• Se pueden aplicar a las clases, a los atributos y metodos, y a las variables de un método

Default (nada) : Se puede acceder al elemento desde dentro del mismo paquete
Iñaki Martín

Private : Solo se puede acceder al elemento desde dentro de la clase, no pueden ser
llamados desde fuera de la clase
Public : Se puede acceder desde cualquier paquete.
Protected : Es como private, pero sí pueden acceder a ellos las clases que
hereden de la clase donde se define el protected.
©

• Qué modificadores puede usar cada elemento


Temario de curso JavaSE

Clase Atributo Variable_local Métodos


Private o Protected NO SI NO SI
Default (nada) SI SI SI SI
Public SI SI NO SI
9.- Modificadores y genéricos Modificador static (I) 5

static
La palabra static se usa para identificar que un miembro de la clase pertenece únicamente a la clase, y no a los diferentes
objetos (instancias) que de dicha clase se creen en el futuro. Según donde apliquemos static hace cosas diferentes :

static como atributo


Aplicando static a un atributo, permite definir y diferenciar los llamados atributos de clase de los atributos de instancia
Iñaki Martín

y de las constante de clase (ejemplos en páginas siguientes):


✴ atributos de clase son variables precedidas de la palabra clave static:
✓ En ese caso la variable es única para todos los objetos de la clase (ocupa un único lugar en memoria).
✓ Un atributo de clase (estático) hemos de declararlo en cabecera de clase, nunca dentro de un método .
©

✓ Al igual que los métodos de clase, un atributo de clase existe y puede utilizarse aunque no existan objetos de
la clase. Se puede leer su valor con el nombre de la clase directamente: NombreClase.atributo
Temario de curso JavaSE

✓ Cualquier objeto puede acceder y modificar este valor, pero si uno lo modifica lo modifica para todos
✓ Dado que los atributos son accesibles a nivel de clase, y sin necesidad de instancias nada, los atributos
estáticos no deben inicializarse al crear un objeto, sino al cargar la clase.
✓ Se usa para tener control de conceptos que afectan a toda la clase. Un ejemplo es el de contar las instancias
que hay de la propia clase (incluyendo un atributo static que haga de contador, en el constructor de la clase)
✴ atributos de instancia (o variables de instancia) son variables sin el uso de static (como se han visto
normalmente hasta ahora los atributos),
✓ En este caso se crea una variable distinta para cada instancia (ocupan diferente espacio en memoria).
✴ constante de clase es una constante (final) combinada con static, static final.
✓ En este caso se crea un atributo común para todos los objetos de esa clase, y fijo (no se puede modificar
su valor una vez asignado, ni pueden ser sobrescritos ni redefinidos). Además, siempre debe ser inicializada
en el momento de declararse.
9.- Modificadores y genéricos Modificador static (II) 6

static como método


Aplicando static a un método, permite definir y identificar los llamados métodos de clase. Un método de clase es
por lo tanto un método al que se le aplica el calificador static y que tiene estas características:

• Para usar un método estático no es necesario instanciar un objeto de la clase. Puesto que existe una copia
única de el, se puede invocar directamente con el simple nombre de la clase: NombreClase.metodo()
Iñaki Martín

Ya se ha visto el uso de métodos estáticos antes: Integer.parseInt(), Math.abs() (de clases Math e Integer:)
• Como se pueden llamar sin crear un objeto, dentro de un método static no se pueden usar variables
normales de clase (por que puede que no exista ni una instancia!), pues estas se crean al crear un objeto.
Por eso si se llama a miembros externos del metodo estos han de ser siempre static
©

• No puede usarse this ni super dentro de un método estático.


public class EjEstaticos {
Temario de curso JavaSE

Ejemplos de public static void main (String[] args) {


// segun el ejemplo, cada vez que creo un objeto de la clase estoy aumentando
atributos y // el valor del atributo estatico
métodos ClaseConEstaticos ce1 = new ClaseConEstaticos ();
ClaseConEstaticos ce2 = new ClaseConEstaticos ();
estáticos // para invocar el metodo estatico de la clase ClaseConEstaticos puedo hacerlo
// sin crear un obeto de la clase:
int s = ClaseConEstaticos.intAleatorio (10, 50);
class ClaseConEstaticos { // para invocar los atributos estaticosde la clase ClaseConEstaticos
// puedo hacerlo sin crear un obeto de la clase:
// atributo de instancia double x = ClaseConEstaticos.CERO_ABSOLUTO;
int indiceInicial = 1; int n = ClaseConEstaticos.numInstancias;
// atributo statico }
static int numInstancias = 0; }
// atributo statico y final
static final double CERO_ABSOLUTO = -273.15;
// metodo statico
static public int intAleatorio (int min, int max) { Sobrescribir estáticos
return (int) (Math.random () * (max - min + 1) + min);

Temas avanzados
} Un método static no puede ser sobrescrito. Vale.
// constructor. Pero eso no impide que pueda ser redefinido en
public ClaseConEstaticos() {
numInstancias ++; una clase heredada: En tal caso no es
System.out.println (“Num. actual de instancias: "+ numeroInstancias); sobrescritura (ni siquiera sobrecarga), es una
} nueva definición del método (como si fueran
} clases diferentes sin ninguna relación)
9.- Modificadores y genéricos Modificador static (III) 7

import static
• Se trata de una operativa similar a la de una import normal, solo que afecta solo a los miembros estáticos
de clase (un import normal importa clases enteras con todos sus miembros).

• Con ello, se puede luego usar un miembro estático sin necesidad de especificar el espacio de
nombres en el que se encuentran. import static java.lang.Math.*;
Iñaki Martín

public class Pruebas {


public static void main (String[] args) {
// Asi se usan miembros estáticos sin import static
int absoluto1 = Math.abs (3);
System.out.println (Math.PI);
// Asi puedo usarlo al poner el import static
int absoluto2 = abs (3);
System.out.println (PI);
©

}
}
bloques static
• Se puede incluir en una clase un bloque static, que no es mas que un bloque (delimitado por llaves) precedido
Temario de curso JavaSE

de la palabra static. Este bloque se ejecutará una sola vez, cuando se carga la clase en memoria (no cuando
se instancia un objeto, sino mucho antes, antes justo de usar por vez primera la clase, y antes de cualquier
constructor de la misma)
public class Pruebas {
public static void main (String[] args) {
System.out.println (“Linea desde el main"); /* — Ejemplo de ejecución del programa:
MiClaseCoche p1 = new MiClaseCoche (); Linea desde el main
MiClaseCoche p2 = new MiClaseCoche (); Linea desde el bloque static
} Linea desde el constructor de la clase
} Linea desde el constructor de la clase */

class MiClaseCoche {
static {
System.out.println ("Linea desde el bloque static");
}
public MiClaseCoche () {
System.out.println ("Linea desde el constructor de la clase”);
}
}
9.- Modificadores y genéricos Modificador static (IV) 8

Otros tipos de bloques


bloques de inicialización
• Un bloque de inicialización es casi idéntico al bloque estático. Se trata simplemente de un par de llaves dentro

de la clase (pero no dentro de un método, esos son simples bloques), y sin el identificador static por delante..
Iñaki Martín

• Este bloque se ejecuta en cada instancia de objeto que se crea, justo después de la llamada a super() del
constructor (sea implícita o explícita), pero por lo tanto, antes de la ejecución del código del propio constructor
• Puede haber más de un bloque de inicialización en una clase, y en este caso, el orden en que se encuentran
sí que importa, pues es el orden en que se ejecutan.
©

public class Pruebas { /* — Ejemplo de ejecución del programa:


public static void main (String[] args) { Esta linea sale desde el main
System.out.println ("Esta linea sale desde el main"); Esta linea sale desde el bloque statico de inicializacion
new MiClaseCoche (); Esta linea sale desde el 1er bloque inicializacion
new MiClaseCoche (“Opel”);
Temario de curso JavaSE

Esta linea sale desde el 2do bloque inicializacion


} Esta linea sale desde el constructor SIN argumentos de la clase
} Esta linea sale desde el 1er bloque inicializacion
Esta linea sale desde el 2do bloque inicializacion
class MiClaseCoche {
Esta linea sale desde el constructor CON argumentos de la clase */
{ System.out.println ("Esta linea sale desde el 1er bloque inicializacion "); }
static { System.out.println ("Esta linea sale desde el bloque statico de inicializacion "); }
{ System.out.println ("Esta linea sale desde el 2do bloque inicializacion "); }

public MiClaseCoche (String marca) {


System.out.println ("Esta linea sale desde el constructor CON argumentos de la clase");
}
public MiClaseCoche () {
System.out.println ("Esta linea sale desde el constructor SIN argumentos de la clase");
}
}

bloques de código (de método)


• Se trata simplemente de dos llaves que encierran código dentro. No tienen nombre ni parámetros.
• Pueden usarse para delimitar el ámbito de vida de variables
• Solo se ejecutan como una instrucción más en el orden en que están en el código
9.- Modificadores y genéricos Modificadores de acceso. Ampliación (I) 9

Modificadores de acceso. Ampliación


private, public, protected, default
Los modificadores de acceso determinan desde qué clases se puede tener acceso (se puede consultar y usar) un

determinado elemento. En Java tenemos 4 tipos básicos:


• public: permite el acceso al elemento sobre el que se aplica desde cualquier clase, independientemente de que esta
Iñaki Martín

pertenezca o no al paquete en que se encuentra el elemento.


• private: es el modificador más restrictivo y especifica que los elementos que lo usan sólo pueden ser usados desde
la clase en la que se encuentran. Este modificador sólo puede utilizarse sobre los miembros de una clase y sobre
interfaces y clases internas, no sobre clases o interfaces de primer nivel, dado que esto no tendría sentido.
©

Es importante destacar también que private convierte los elementos en privados para otras clases, pero no
para otras instancias (objetos) de la misma clase. Dicho de otro modo, un objeto de una determinada clase
puede acceder a los miembros privados de otro objeto de la misma clase, así que esto es perfectamente válido:
Temario de curso JavaSE

class ejemploClase1 {
private int atributo1 = 0;

ejemploClase1 (ejemploClase1 otroObjeto) {


atributo1 = otroObjeto.atributo1;
}
}

• protected : indica que los elementos sólo pueden ser accedidos desde su mismo paquete (como el acceso por
defecto) y desde cualquier clase que extienda la clase en que se encuentra, independientemente de si esta se
encuentra en el mismo paquete o no. Este modificador, como private, no tiene sentido a nivel de clases o interfaces
no internas.
• (default): el tipo por defecto (si no se indica uno de los anteriores), que no tiene ninguna palabra clave asociada,
pero se suele conocer como default o package-private.Consiste en que el elemento puede ser accedido sólo desde
las clases que están en el mismo paquete que el elemento que modificamos.
9.- Modificadores y genéricos Modificadores de acceso. Ampliación (II) 10

strictfp
• strictfp es un modificador de lo más esotérico, muy poco utilizado y conocido cuyo nombre procede de strict
floating point, o punto flotante estricto.

• Su uso sobre una clase, interfaz o método sirve para mejorar su portabilidad haciendo que los cálculos con
números flotantes se restrinjan a los tamaños definidos por el estándar de punto flotante de la IEEE (float y
Iñaki Martín

double), en lugar de aprovechar toda la precisión que la plataforma en la que estemos corriendo el programa
pudiera ofrecernos.
• No es aconsejable su uso a menos que sea estrictamente necesario.
©

native
Temario de curso JavaSE

• native es un modificador utilizado cuando un determinado método está escrito en un lenguaje distinto a
Java, normalmente C, C++ o ensamblador para mejorar el rendimiento.
• Sólo se puede aplicar a métodos.
• La forma más común de implementar estos métodos es utilizar JNI (Java Native Interface).

transient
• transient es utilizado para indicar que los atributos de un objeto no son parte persistente del objeto o bien
que estos no deben guardarse y restaurarse utilizando el mecanismo de serialización estándar.
• Esto puede afectar a la hora de guardar objetos serializados en ficheros, que pueden tener atributos
transiten…
9.- Modificadores y genéricos Modificadores de acceso. Ampliación (IV) 11

volatile
• volatile se usa en el tratamiento de atributos en hilos. No debe confundirse con synchronized, que es uno de
los mecanismos de sincronización básicos de Java.

• volatile se usa sobre los atributos de los objetos para indicar al compilador que es posible que dicho atributo
vaya a ser modificado por varios Threads de forma simultánea y asíncrona, y que no queremos guardar una
Iñaki Martín

copia local del valor para cada Thread a modo de caché, sino que queremos que los valores de todos los
Threads estén sincronizados en todo momento, asegurando así la visibilidad del valor siempre
actualizado, a costa de un pequeño impacto en el rendimiento.
• volatile es más simple y más sencillo que synchronized, lo que implica también un mejor rendimiento. Sin
©

embargo volatile, a diferencia de synchronized, no proporciona atomicidad, lo que puede hacer que sea
más complicado de utilizar.
Temario de curso JavaSE

• Esto es, un atributo que sea volatile, puede efectuar operaciones no atomices sin control de sincronismo.
• Una operación como el incremento, por ejemplo, no es atómica (la operación se divide en realidad en 3
instrucciones distintas: primero se lee la variable, después se incrementa, y por último se actualiza el valor).
Así que un incremento de un atributo volatile no puede asegurar que el acceso al dato sea continuo en las tres
operaciones sin interferencia de otros hilos.
• Sin embargo, en las operaciones atómicas, volatile actúa como un synchronized, bloqueando el acceso a la
variable (asignacion, lectura, etc)
• Si necesitamos asegurar atomicidad podemos usar synchronized (a nivel de atributo o método, ver capítulo
de Procesos)
• Comentar que ambos pueden aplicarse sobre atributos, pero si los atributos son primitivos, sólo puede usarse
volatile
9.- Modificadores y genéricos Modificadores de acceso. Ampliación (V) 12

abstract
Como ya se dijo en el capítulo de POO, abstract se puede aplicar a:
• una clase: en tal caso, la clase puede (o no) incluir métodos abstractos.
✴ Una clase abstracta no puede ser instanciada

✴ Una clase abstracta si puede ser superclase de otra.


Iñaki Martín

✴ Una clase abstracta puede (y debe) tener constructores. Aunque no se instancia, una clase abstracta al ser
heredada necesitará que sus subclases llamen a super() en su constructor, así que debe tener un constructor. De
hecho, la JVM añadirá uno por defecto si no se incluye uno explícitamente
• un método: es un método declarado sin implementarse, sin llaves, acabado en punto y coma
✴ Un método abstracto en una clase obliga a esta a ser declarada abstracta
©

comparando clases abstractas e interfaces


Temario de curso JavaSE

Son similares en que no puedes instanciar ninguna de las dos y tienen métodos abstractos, y se diferencian en que los
atributos de la interfaz solo pueden ser public, static, y final, pero, cuando haya dudas… ¿Cuál usar?
• Mejor usar clases abstractas si:
◦ Se desea compartir código entres varias clases similares.
◦ Las clases que hereden la clase abstracta tienen muchos methods comunes o necesitan acceso a los
miembros mas restrictivo que publico (protected o private).
◦ Se desea declarar miembros no static's ni final. This enables you to define methods that can access and
modify the state of the object to which they belong.
• Mejor usar interfaces si:
◦ Deseas generar métodos a usar por clases muy diferentes (las interfaces Comparable y Cloneable por
ejemplo son usadas por muchas clases muy distintas).
◦ Se necesita explicar el comportamiento de un modelo de dato específico, sin importar quien lo vaya a usar
después.
◦ Se necesita multiherencia.
9.- Modificadores y genéricos Modificadores de acceso. Ampliación (VI) 13

final
• Indica que una variable, método o clase no se va a modificar, o se quiere proteger para no permitir que se modifique

final con clases


• Si se desea que una clase no pueda ser heradada, se añade, al declararla, el modificador final delante de class
Iñaki Martín

public final class ClaseNoHeredable(){…}

final con métodos


• Al igual que ocurre con las clases (cuando no se deseaba que pudieran heredarse), se puede usar el modificador
final para evitar que un método pueda ser sobrescrito desde una subclase.
©

• Un método static en una superclase tampoco puede sobrescribirse en la subclase.


Temario de curso JavaSE

final con atributos / variables


• Si una variable se marca como final, no se podrá asignar un nuevo valor a la variable.
• Se puede usar final con un atributo de instancia o una variable local (esta última si no es con static añadido)
• Un atributo final debe ser inicializado antes de que termine la ejecución del constructor de la clase.

Un valor final puede cambiar !


• También se puede usar final en un parámetro de un método, a la hora de declararlo (un parámetro, para el método, no es más que una variable local) :
void mover(int x, final int angulo) {…}
ZONA DE TRAMPAS

• Una variable declarada final no pueda cambiar de referencia, pero SI DE VALOR. Más en detalle:
a. Una variable de tipo primitivo, declarada final, no puede cambiar el valor (pues guardan solo valor, no referencia)
b. Una variable de un tipo objeto, declarada final, quiere decir que no puede cambiar su referencia, pero sí el objeto al que apunta la referencia. Esto
se resume en que no se puede reasignar (usar el operador = ) con un objeto final. Pero sí cambiar el contenido del objeto al que referencia:
final Persona p = new Persona();
Persona q = new Persona();
p = q; // ERROR no puedo cambiar la referencia
p.setNombre ( “JUAN”) ; // OK ! Puedo actualizar el contenido de la referencia
9.- Modificadores y genéricos Resumen static, final, abstract, public, private…. 14

Resumen de modificadores

STATIC FINAL ABSTRACT


aplicado a un El atributo es único para todos los objetos El atributo no se puede cambiar (es una
Iñaki Martín

No se puede usar en un atributo


ATRIBUTO de la clase constante)
aplicado a un El método se puede invocar sin
El método no se puede sobrescribir El método no tiene código
METODO implementar objeto
aplicado a una
No se puede usar en una clase La clase no se puede heredar La clase no se puede instanciar
CLASE
©

Si uso en una variable el modificador …


Temario de curso JavaSE

Se puede acceder a esa variable desde …? public protected default private

Un miembro de la misma clase? SI SI SI SI

Un miembro de una clase del mismo paquete? SI SI SI NO

Un miembro de una subclase del mismo paquete? SI SI SI NO

SI SI, pero solo a través de


Un miembro de una subclase de distinto paquete? NO NO
herencia
Un miembro de un clase (que no es subclase) de
SI NO NO NO
distinto paquete?
9.- Modificadores y genéricos Clases genéricas (I) 15

Clases Genéricas

• Cuando se crean objetos de algunas clases, como por ejemplo colecciones, puede no tener que indicarse el tipo de dato que
se almacena, puedo hacer como una colección genérica donde meter cualquier cosa. Por ejemplo:

ArrayList a = new ArrayList ();


a.add ("HOLA"); // puedo meter valores de diferente tipo, aqui String
a.add (123); // o int
Iñaki Martín

a.add (23.43); // o double


for (Object unobjeto : a) {
System.out.println (unobjeto.toString ()); //aqui se hará el toString de cada tipo
}

• Claro que, si al declarar la colección, se indica el tipo, ya no se puede meter en la colección más que lo que haya declarado
ArrayList<String> b = new ArrayList (); // Se define la coleccion para que solo contenga String
©

b.add ("HOLA");
// b.add(123); // Dazor pues el array es de String

• Hay que tener en cuenta que en una colección con un tipo definido, gracias al polimorfismo, puedo meter dentro de dicha
Temario de curso JavaSE

colección cualquier objeto del tipo definido…. o de cualquiera de sus herederas.


ArrayList<Number> c = new ArrayList (); // tras el new NO indico que tipo construye la lista
c.add ( 1); // puedo meter un int
c.add (2.3); // y tambien puedo meter un double
c.add (true); // Da error pues el array no admite tipos boolean

• Bien, esto con clases de JSE, como estas colecciones. ¿Pero y con mis clases?. Cuando se han creado clases, hasta ahora,
no tenían un “tipo” de dato asociado. El tipo de dato lo tenia asignado cada miembro (atributo o método) en particular.
• Lo que se hace con ArrayList en el primer ejemplo, esto es, NO decir que tipo de dato va a almacenar, no se puede hacer con
los miembros de mi clase. Éstos han de tener un tipo. Pero lo que se puede hacer es no usar un tipo específico, sino
uno genérico.
• ¿Y esto de genérico, cuando se convierte en un tipo real? Pues será a la hora de crear posteriormente objetos de
nuestra clase cuando se decida qué tipo de datos va a almacenar.
• Se trata de usar los llamados Prototipos o Tipos Genéricos cuando se trabaje con la clase,
36
9.- Modificadores y genéricos Clases genéricas (II) 16

¿Cómo crear clases genéricas? Usando un marcado <T> tras el nombre de la clase y al usar sus miembros. Ejemplo:
class MiClaseSinTipo<T> { // Con esta T indico que luego al crear objetos diré qué tipo de dato es T
// T es un tipo “formal” que luego será sustituido por el tipo “real” cuando se cree un objeto.
private T informacion; // Ese tipo de dato T será el que tendrá mi atributo “info”

public MiClaseSinTipo (T informacion) { // El parámetro del constructor sera, claro, de tipo T


this.informacion = informacion;
}
public T getInfo () { // igual que el parámetro de devuelve el getter
return this.informacion;
Iñaki Martín

}
public void setInfo (T informacion) { // o el parámetro que necesita el setter
this.informacion = informacion;
}
public String toString ( // aquí devuelve el toString() del atributo, sea cual sea éste
return (informacion != null) ? informacion.toString () : null;
}
}
©

Y será luego, al crear los objetos, cuando definamos qué tipo de dato vamos a usar… en cada objeto que creemos.
Temario de curso JavaSE

// Al crear un objeto de la clase MiClaseSinTipo, es cuando le digo que tipo voy a usar sustituyendo a T
MiClaseSinTipo<String> miObjetoString = new MiClaseSinTipo<String> ("HOLA");
String data = miObjetoString.getInformacion();
System.out.println (data); // imprime HOLA

// Ahora en vez de un String como antes, uso un Integer para que sustituya el tipo formal T
MiClaseSinTipo<Integer> miOBjetoInteger = new MiClaseSinTipo<Integer> (3456);
int s = miOBjetoInteger.getInformacion();
System.out.println (s); // imprime 3456

// Ahora meto en un array varios tipos diferentes


MiClaseSinTipo[] miLista = new MiClaseSinTipo[2];
miLista[0] = new MiClaseSinTipo<String> ("Juan");// imprime Juan
miLista[1] = new MiClaseSinTipo<Integer> (34);// imprime 34

String data2 = (String) miLista[0].getInformacion();


int s2 = (int) miLista[1].getInformacion();

System.out.println (data2);
System.out.println (s2);

CUIDADO: Al usar tipos genéricos, se necesita que la referencia sean objetos, por lo que NO se admiten tipos primitivos
(int, double,… ), se deben usar sus clases envoltorio (Integer, Double, …)
9.- Modificadores y genéricos Clases genéricas (III) 17

• Cuidado, al usar el atributo dentro de la propia clase, hay que tener en cuenta que no esta definido que tipo
tiene, por lo que en el ejemplo anterior, no puedo concatenar un elemento con un String, no puedo hacer
public T getInfo () {
return this.informacion + " con sufijo"; // DA ERROR, no sabe si informacion es String

• ¿Y cómo sería un método, fuera de mi clase, en el que estemos utilizando genéricos? Pues debemos en la
Iñaki Martín

declaración atender al tipo de dato que vamos a procesar:


public static void main (String args[]) {
MiClaseSinTipo<String> miObjetoString = new MiClaseSinTipo<String> ("HOLA");
MiClaseSinTipo<Integer> miObjetoInteger = new MiClaseSinTipo<Integer> (3456);
System.out.println (ponerAlgoDelante (miObjetoString));
}
©

static public String ponerAlgoDelante (MiClaseSinTipo<String> mi) {


return "Algo por delante de " + mi;
}
Temario de curso JavaSE

• Tener en cuenta, claro, que he llamado al método con el objeto miObjetoString creado como
MiClaseSinTipo<String>, que es lo que pide el método como parámetro, si hubiera usado miObjetoInteger en la
llamada, no me hubiera dejado ni compilar.
• Existen una serie de convenciones para nombrar a los genéricos (aunque se puede usar lo que se quiera):
E – Element (usado bastante por Java Collections Framework)
K – Key (Llave, usado en mapas)
N – Number (para números)
T – Type (Representa un tipo, es decir, una clase)
V – Value (representa el valor, también se usa en mapas)
S,U,V etc. – usado para representar otros tipos.
NOTA : También se puede usar más de un argumento de tipo: class MiClaseSinTipos <T,U> { …. }
38
9.- Modificadores y genéricos Clases genéricas (IV) 18

Tipos acotados (bounded types)


• Se puede querer tener una clase (o una coleccion) que sea de tipo genérico pero restringiendo algo más su ámbito. Para
ello se puede acotar el tipo con el uso de extends y super, de este modo

ArrayList <? extends Vehiculo> unVehiculo;


Iñaki Martín

• Esto quiere decir que el objeto unVehiculo podrá ser un ArrayList de “cualquier cosa (el ?) que herede de Vehículo”
• Hay que tener en cuenta que eso es muy útil en casos como el siguiente. Si tenemos estas clases:
class ClaseMadre { class ClaseHija extends ClaseMadre {
String aa; String bbb;
public ClaseMadre(String aa) { public ClaseHija(String aa, String bbb) {
©

this.aa = aa; super(aa);


this.bbb = bbb;
} }
} }
Temario de curso JavaSE

• Veamos estas dos instrucciones:


ClaseMadre uno = new ClaseHija ("texto", "Otrotexto"); // SI funciona
ArrayList<ClaseMadre> unobjeto = new ArrayList<ClaseHija>(); // DA ERROR !!!!

• Ello es porque ArrayList<ClaseHija> no es un subtipo de ArrayList<ClaseMadre>… no se esta analizando el polimorfismo de


ClaseMadre y ClaseHija, se esta tratando con diferentes ArrayList, de cosas diferentes.
Repetimos, ArrayList<ClaseHija> no es un subtipo de ArrayList<ClaseMadre>.
En ArrayList<ClaseMadre> sólo pueden entrar objetos que pertenezcan a la clase ClaseMadre.

• Si cambiamos la ultima linea por esta; ArrayList < ? extends ClaseMadre> ob = new ArrayList<ClaseHija>();
ahora ya sí funciona.
• Otro ejemplo, recordando (o aprendiendo) que Integer es una subclase de Number,
ArrayList < Integer> miLista = new ArrayList<Integer>( ); // NO compila siquiera, mientras que
ArrayList < ? extends Integer> miLista = new ArrayList<Integer>( ); // SI funciona correctamente
9.- Modificadores y genéricos Clases genéricas (V) 19

Tipos acotados : uso de super


• Pero, ¿qué ocurre si lo que queremos es lo contrario, es decir, que la lista admita objetos de la clase Integer y su superclase,
pero no ninguna otra subclase de Number?

En ese caso, haríamos lo siguiente:


Iñaki Martín

ArrayList<? super Integer>


con lo que la lista admitirá objetos de la clase Integer y de cualquier otra clase superior a ella, como Number.

¿Y qué ocurre si hacemos esto?:


List<?>
Lo que estamos indicando ahora es que la lista admitiría cualquier tipo de objetos.
©

No confundir con “objetos de la clase Object”.


Temario de curso JavaSE

Es decir, esto:
List<Object>
sólo admite objetos que pertenezcan a la clase Object (no a clases creadas por un usuario), y no podemos hacer lo siguiente:
List<Object> miLista = new List< MiClaseA >( ); // no compila, MiClaseA es una clase mia, de usuario, no de Object

• Si usamos el tipo comodín, hay que cuidado pues el comodín representa cualquier tipo de dato, por lo que no se puede
añadir elementos de un tipo específico a una colección que contenga un tipo marcado con comodín, pues no sabe que
tipo ha de añadir (consultar y recuperar datos si es posible, o usar como paso de parámetros)

public int calcular( ArrayList <? extends Vehiculo> listaVehiculos ){


Coche co = new Coche(); 

listaVehiculos.add( co ); // Error: No sabe añadir un Coche
// por que la lista esta definida como de muchas cosas
}
9.- Modificadores y genéricos Clases genéricas (VI) 20

Métodos Genéricos
• No solo se pueden declarar clases genéricas, tambien se pueden crear métodos genéricos. Si tenemos este ejemplo donde
una clase Utilidades tiene un metodo que nos dice si un elemento dado esta o no en un array:

class Utilidades {
public boolean existe(int[] elementos, int elemento) {
boolean res = false;
Iñaki Martín

for (int i = 0; i < elementos.length && (!res); i++) {


if (elementos[i] == elemento) res = true;
}
return res;
}
}
©

• ¿Como hacer para que el metodo funcione con parametros, no de tipo int, sino genericos? Pues añadiendo la marca del tipo
generico antes del tipo de retorno del metodo, y usando dicho tipo generico ya directamente en el metodo, tanto en sus
parametros como en su codigo:
Temario de curso JavaSE

class Utilidades {
// MARCAR EL GENERICO (<T> O EL QUE SEA) ANTES DEL TIPO DE RETORNO DEL METODO
public <T> boolean existe(T[] elementos, T elemento) {
boolean res = false;
for (int i = 0; i < elementos.length && (!res); i++) {
if (elementos[i] == elemento) res = true;
}
return res;
}
}

• Esta seria la llamada a los metodos genericos vistos anteriormente:


String[] cadenas = { "uno", "dos", "tres", "cuatro"};
Integer[] numeros = { 1, 2, 3, 4, 5, 6, 7, 8 };
Utilidades ut = new Utilidades();
boolean solucion1 = ut.<String> existe(cadenas, "seis");
boolean solucion2 = ut.<Integer> existe(numeros, 3);
9.- Modificadores y genéricos Clases genéricas (VI) 21

Arrays Genéricos y otras restricciones

• En Java NO se pueden crear arrays de tipo generico. En Java los arrays (a direfencia de los genericos) necesitan saber su tipo
en tiempo de ejecucion, asi que se necesita saber el tipo cuando se crea el array.

class Baul<T> {
private T nombre; // Puedo crear atributos de tipo generico
Iñaki Martín

static T unavarestatica; // Pero NO puedo crear variables estaticas de tipo generico

void pruebas(T[] arraydeparametro, T param) { // Puedo recibir arrays de tipo generico


T otraVariable; // Puedo crear variables locales de tipo generico
T[] unarray = new T[10]; // Pero NO puedo crear arrays de tipo generico
otraVariable = 3534; // No puedo asignar valor a una variable de tipo generico,
// pues el tipo no esta definido
©

// Si puedo usar instanceof para probar el tipo generico


if (param instanceof String) {
System.out.println("tipo string");
}
Temario de curso JavaSE

}
}
9.- Modificadores y genéricos Reglas a aplicar en modificadores de acceso 22

default y protected con paquetes


• Los miembros de una clase (métodos y atributos de instancia) pueden tener cuatro niveles de acceso; public, protected,
default, private.
• Se puede acceder a los miembros public desde cualquier otra clase, incluso si estan en otro paquete

1. Si el miembro de una superclase es publico, sus subclases lo van a heredar, independientemente de en que paquete
esten superclase y subclase (o los import que haya)
Iñaki Martín

2. Los miembros “default” y protected se diferencian solo cuando se procesan subclases:


I. Un miembro default puede ser visible por cualquier clase del mismo paquete
II. Un miembro protected puede ser visible por cualquier clase del mismo paquete, y ademas, por todas las subclases,
sin importer en que paquete esten (Esto es, protected = default + herencia)
III. Si una subclase esta en un paquete distinto a la superclase, cuidado !, ,mirar estos dos casos;
a) los miembros protected solo pueden ser invocados mediante herencia, esto es, la subclase no puede
©

acceder a un miembro protected usando una referencia a una instancia de la superclase, o dicho de otro modo,
Temas avanzados

la herencia es el único mecanismo para que alfa


una subclase, fuera del paquete de la superclase, acceda a
Temario de curso JavaSE

miembros protected de la superclase


b) Un miembro protected heredado por una subclase (de un paquete distinto de su superclase) no es tampoco
accessible desde cualquier otra clase del paquete de la subclase (salvo que esta clase sea parte del arbol
de herencia de la superclase)
En el siguiente ejemplo, en la clase Padre, se declara una variable x como protected. De modo que es accessible por
todas las clases de su propio paquete (alfa) o por herencia, por otros paquetes. Creamos otra clase, Hija, que hereda
de Padre, pero que está en otro paquete. A través de herencia, puedo acceder al miembro protected. Pero si declaro
una instancia de la clase Padre, en cualquier parte del paquete beta, no puedo acceder al atributo protected (pues esta
en otro paquete).
package beta; // Paquete diferente
import alfa.Padre;
package alfa;
class Hija extends Padre {
public void testIt() {
public class Padre {
System.out.println("x is " + x); // Sin problema, Hija hereda x
// cuidado, acceso protected
Padre p = new Padre(); // ¿Puedo accede a p.x ?
protected int x = 9;
System.out.println("X in parent is " + p.x); // ERROR !!
}
}
}
9.- Modificadores y genéricos Algunas zonas de trampas 23

Bloques de inicializacion : excepciones


Cuidado con los errores en un bloque de inicialización, pues lanzan una excepción java.lang.ExceptionInInitializerError, que no es la
que se puede esperar encontrar:

class InitError {
Temas avanzados

static int [] xxx;


static {
Iñaki Martín

xxx [2] = 5; // ERROR


}
public static void main(String [] args) { … }
}

En este ejemplo la variable xxx aun no esta dimensionada, y da error, pero no lanza un NullPointerException, sino una
©

ExceptionInitializerError
Temario de curso JavaSE

Protected y herencia.

Cuidado: Las subclases acceden a los miembros protegidos a través de la herencia, no pudiendo utilizar una referencia a un
objeto de la superclase para acceder al miembro protegido.
package claseMobiliario;
public class Mobiliario {
protected int precio;
Temas avanzados

protected int getPrecio () {


import claseMobiliario.Mobiliario; return precio;
}
class Mesa extends Mobiliario { }
public void mostrar () {
Mesa me = new Mesa ();
System.out.println ("Precio: " + me.getPrecio ()); // SI, por herencia

Mobiliario mo = new Mobiliario ();


System.out.println ("Precio: " + mo.getPrecio ()); //¡Error!
}
}

También podría gustarte