Ola, somos Arume

Desenvolvemos páxinas web, aplicacións para móbiles, capas de realidade aumentada e aplicacións para Facebook. Apaixónanos a informática e somos uns perfeccionistas incurables; por eso nos nosos proxectos utilizamos estándares.

tel. 625 519 694

Mendaña de Neyra, 34, 3º B, 15008, A Coruña

Autenticarse

Rexistrarse. Esqueceches o teu contrasinal?

Etiquetas

Saltar as etiquetas

Subscríbete ás RSS

Estás en:

Java Reflection (parte 3)

17 Xuñ 2011 por Santi

Comentarios: 11

Java

Nas anteriores entradas Java Reflection parte 1 e Java Reflection parte 2) comentamos conceptos básicos sobre como obter en tempo de execución os tipos, atributos e métodos dun obxecto, e as manipulacións básicas sobre os mesmos (acceder e cambiar os seus valores). Tamén explicamos algunhas manipulacións máis complexas (obtención de instancias a partir de construtores específicos, acceder e modificar elementos privados, ...).

Nesta terceira parte dedicarémonos a explicar como traballar con reflexión con dous elementos un pouco máis avanzados de Java: arrays e tipos xenéricos (Java Generics). Aínda que quizais poidan ser considerados elementos básicos da linguaxe de programación (os arrays deben ser o primeiro tipo complexo que se ensina nas clases de programación), a manipulación vía Java Reflection dos mesmos é un pouco máis complexa que os elementos vistos ata o momento, aínda que descubriremos que unha vez aprendidos os conceptos básicos tal complexidade é só aparente.

Arrays

A manipulación de arrays mediante Java Reflection efectúase usando a clase java.lang.reflect.Array. Para crear un array debemos usar o método Array.newInstance(Class arrayClass, int size), sendo o primeiro parámetro a clase dos elementos do array e o segundo o tamaño do array. Desta forma, para crear un array de 3 cadeas:

String[] names = (String[]) Array.newInstance(String.class, 3);

Unha vez temos o noso array creado, poderemos acceder e asignar os seus elementos mediante os respectivos métodos Array.get(Object array, int index) e Array.set(Object array, int index, Object value), onde os parámetros son: array = o array en cuestión, index = o elemento dentro do array (notación estándar de arrays, o primeiro elemento terá sempre o índice 0), value = o valor a asignar.

Ademais destes dous métodos xenéricos, a clase Array contén uns métodos get e set específicos para os tipos primitivos de Java. Desta forma temos Array.getBoolean() e Array.setBoolean() para arrays de tipo boolean, Array.getLong() e Array.setLong() para arrays de tipo long, ... Os parámetros para estes métodos son os mesmos que para os métodos xenéricos, ca única excepción de que o último parámetro do set() (value, o que contén o valor a asignar) será do tipo primitivo en cuestión no canto de tipo xenérico Object.

Como exemplo, supoñamos que temos unha cadea almacenada nunha variable storedName, e queremos gardala no segundo elemento (índice 1) do noso array anterior e almacenar nela o seguinte valor do array:

String storedName;

/* ... */

Array.set(names, 1, storedName);
storedName = (String) Array.get(names, 2);

Obter a clase dun array é lixeiramente máis complexo que co resto de obxectos, posto que hai que utilizar unha notación un tanto especial. Para obter a clase dun array mediante o coñecido método Class.forName(String className), debemos saber que para indicar un array debemos iniciar a cadea do parámetro className co carácter '[' seguido do tipo de clase dos elementos do array. Para tipos primitivos usarase unha letra da seguinte lista:

  • boolean: Z
  • byte: B
  • short: S
  • int: I
  • long: J
  • char: C
  • float: F
  • double: D

En cambio, para elementos de tipo "obxectual" débese usar a letra L, seguida do nome calificado completo da clase e finalizado co carácter ';'.

Así, se queremos obter a clase dun array de enteiros e un array de cadeas, usaremos:

Class intArrayClass = Class.forName("[I");
Class stringArrayClass = Class.forName("[Ljava.lang.String;");

Unha vez temos a clase do array, podemos obter o tipo do compoñente (é dicir, o tipo dos elementos do array) mediante o método Class.getComponentType(). Por exemplo, para o noso array anterior:

Class namesClass = names.getClass();
Class componentClass = namesClass.getComponentType();

Neste exemplo, a variable componentClass conterá a clase java.lang.String .

Generics

Mediante reflexión tamén seremos capaces de obter en tempo de execución información sobre tipos xenéricos, aínda que o sistema é un pouco máis complexo e non tan inmediato como cando tratabamos con tipos "normais".

Crearemos a seguinte clase para os nosos exemplos, extensión da clase User que definimos no noso artigo Java Reflection parte 2:

package com.test.model;

public class SocialUser extends User
{
	private List<User> friends;

	public List<User> getFriends()
	{
		return friends;
	}

	public void setFriends(List<User> friends)
	{
		this.friends = friends;
	}
}

Como podemos ver, no noso exemplo estamos usando Java Generics para un caso típico: parametrizar os elementos dunha lista Java.util.List (tanto na definición do atributo como os métodos que manipulan o mesmo).

Primeiramente imos ver como podemos obter o tipo xenérico do atributo friends mediante o método Field.getGenericType(). Quedará mellor explicado véndoo directamente cun exemplo:

Field friendsField = SocialUser.class.getDeclaredField("friends");

Type friendsGenericType = friendsField.getGenericType();
ParameterizedType friendsParameterizedType = (ParameterizedType) friendsGenericType;
Type[] friendsType = friendsParameterizedType.getActualTypeArguments();
Class userClass = (Class) friendsType[0];

O primeiro paso é obter o obxecto Field asociado ó atributo como aprendemos no primeiro artigo sobre Java Reflection. Posteriormente obtemos o tipo xenérico na variable friendsGenericType, que como vemos é do tipo Type (un interfaz xenérico para todos os tipos de Java). Pero o tipo contido aí é o xenérico (java.util.List), mentres que nós queremos saber o tipo específico dos elementos da lista (User). Para poder chegar ata el, necesitamos facer un cast do noso obxecto Type a ParameterizedType (un subinterfaz específico dos tipos parametrizados en Java, recordemos que os tipos xenéricos chámanse tamén tipos parametrizados). Finalmente obtemos o array dos tipos específicos mediante o método getActualTypeArguments() (recordemos que nunha declaración de xenéricos, pódense especificar varios tipos específicos). Posto que isto é un exemplo, sabemos que só ten un tipo específico e está no índice 0 do array resultante (friendsType). Se queremos utilizalo como un obxecto Class, só temos que asignalo a unha variable cun simple cast de Type a Class.

No noso exemplo simplificouse o código para facilitar a comprensión (e porque sabiamos desde o principio cales ían ser os tipos devoltos), pero en condicións normais é unha boa práctica asegurarse da corrección dos tipos devoltos (por exemplo facendo un if (friendsGenericType instanceof ParameterizedType) antes do cast de Type a ParameterizedType e non accedendo a ningún elemento do array friendsType sen comprobar que ese índice existe).

Agora que sabemos como obter e manipular tipos xenéricos (ou parametrizados), obter o tipo xenérico de retorno dunha función é tan simple como usar o método Method.getGenericReturnType():

Method getFriendsMethod = SocialUser.class.getMethod("getFriends", null);
Type returnType = getFriendsMethod.getGenericReturnType();

Con este código de exemplo, almacenamos na variable returnType o obxecto que contén o tipo xenérico de retorno da función getFriends(), e cuxo procesamiento é totalmente idéntico ó exemplo anterior.

Finalmente, podemos obter tamén en tempo de execución os tipos xenéricos dos parámetros dunha función, usando Method.getGenericParameterTypes():

Method setFriendsMethod = SocialUser.class.getMethod("setFriends", List.class);
Type[] parameterTypes = setFriendsMethod.getGenericParameterTypes();

A única diferenza cos dous exemplos vistos anteriormente é que a variable parameterTypes agora trátase dun array de obxectos Type, onde cada elemento do devandito array correspóndese co tipo xenérico do parámetro correspondente na función setFriends() (no noso exemplo un único elemento, pois a nosa función só ten un parámetro).

Comentarios

11 comentarios. Comentar.

1. Carlos Torres o 26 Xul 2011 ás 07:28:18

Muy interesante articulo, me ha servido mucho para el proyecto que estoy realizando muchas gracias.

2. vcs o 11 Ago 2011 ás 00:18:04

Esta bien bueno su blog!, aparte todo muy claro, la separacion de la explicacion y los codigos es totalmente entendible ;)

3. White_King o 20 Feb 2012 ás 05:21:17

excelente post y muy bien explicado, habrá una continuación supongo?

me gustaría registrarme, Saludos

http://javahelp.redsaltillo.net

4. Anónimo o 06 Mar 2012 ás 19:43:25

Excelente artículo!!, muy bien explicado... los felicito.

Daniel

5. Ramonchu2k o 18 Xuñ 2012 ás 12:53:41

Enhorabuena, gran articulo, muy bien explicado y que me ha quitado ciertas dudas que tenía.

Muchisimas gracias.

6. Rolando o 07 Ago 2012 ás 17:07:36

Excelente

7. Cristhian o 17 Xan 2013 ás 16:13:53

excelente muy bueno muchas gracias

8. anonimo o 04 Out 2013 ás 06:46:06

como crear un menu en java swing en tiempo de ejecucion

9. Emejia o 23 Out 2013 ás 08:54:56

Muy buen trabajo se tomaron el tiempo y se nota los frutos del esfuerzo muchas gracias por el aporte

10. Anónimo o 30 Nov 2013 ás 22:10:29

Muy bueno. Me ha servido muchísimo

11. Anónimo o 13 Dec 2013 ás 19:24:18

Me fue muy util la explicación que se da en este blog. Muchas gracias

Comentar

Comentar de forma anónima

Podes comentar poñendo calquera nome ou alcume, exceptuando os nomes de usuarios rexistrados. Máximo de 50 caracteres.

Comentar como usuario rexistrado

Rexistrarse. Esqueceches o teu contrasinal?