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 2)

19 Mai 2011 por Santi

Comentarios: 9

Java

Na nosa anterior entrada, Java Reflection (parte 1), comentamos como obter o tipo, construtores e instancias dunha clase cando non coñeciamos en tempo de compilación os detalles específicos da mesma.

Neste artigo, comentaremos como obter e manipular os atributos e métodos dunha clase.

Como exemplo que usaremos durante a nosa explicación, definimos a seguinte clase simple:

package com.test.model;

public class User()
{
	private String alias = null;
	public String name;
	public String address;

	public User(String name)
	{
		this.name = name;
	}

	public setAlias(String alias)
	{
		this.alias = alias;
	}

	public getAlias()
	{
		if (alias == null)
		{
			return name;
		}
		else
		{
			return alias;
		}
	}
}

E supoñamos que temos a clase almacenada na variable userClass:

Class userClass = Class.forName("com.test.model.User");

Atributos

Para acceder ós atributos públicos dunha clase temos dúas posibilidades. Se coñecemos o nome do atributo usaremos a seguinte instrución:

Field userField = userClass.getField("name");

Como nos casos anteriores devólvesenos toda a información sobre o atributo mediante unha instancia ó obxecto correspondente, neste caso de tipo Field.

Se a clase non tivese ningún atributo público con ese nome, o método getField() lanzará unha excepción NoSuchFieldException.

Se por calquera motivo non coñecemos os nomes dos atributos, temos unha forma de obter todos os atributos públicos dunha clase da seguinte forma:

Field[] userFields = userClass.getFields();

A instrución getFields() devólvenos un array cun elemento de tipo Field por cada un dos atributos públicos da clase (no noso exemplo, devolvería un array de 2 elementos: "name" e "address").

As dúas instrucións mencionadas ata o momento serven só para acceder ós atributos públicos. Se o que queremos é acceder a calquera atributo (incluídos os privados), necesitaremos usar os métodos Class.getDeclaredField(String name) (para acceder sabendo o nome do atributo) e Class.getDeclaredFields() (que nos devolverá un array con todos os atributos declarados na clase: "alias", "name" e "address").

Debemos ter en conta que estes métodos só nos permiten acceder ós atributos declarados expresamente na clase en cuestión, nunca aqueles declarados en superclases da mesma.

Agora que dispomos dunha instancia Field para un atributo, podemos proceder a manipulalo. Primeiro veremos como obter información sobre o mesmo. Podemos obter o nome do atributo mediante:

String fieldName = userField.getName();

Tamén podemos obter o tipo do atributo mediante:

Object fieldType = userField.getType();

Se o que nos interesa é o valor contido no atributo, deberemos usar:

Object fieldValue = userField.get(userInstance);

Como vemos, o método Field.get() recibe un parámetro que é a instancia do obxecto en cuestión do que queremos pescudar o valor do seu atributo. Se estivésemos intentando acceder a un método estático, debemos chamar ó método con null como parámetro.

Se o que queremos é cambiar o valor do atributo, escribiremos:

userField.set(userInstance, value);

Onde value é o valor que queremos asignarlle (que, evidentemente, debe ser do tipo correspondente a ese atributo) e userInstance é a instancia do obxecto ó que queremos asignarlle o valor. Do mesmo xeito que con get(), en caso de tratarse dun atributo estático, o valor de userInstance debe ser null.

Como parece lóxico, os métodos anteriores (get() e set()) só nos permiten manipular os atributos públicos dun obxecto (aqueles ós que teñamos acceso desde o contexto do noso código). Pero Java Reflection permítenos manipular ata aqueles atributos privados ós que non teriamos acceso de forma normal. Para iso só hai que usar o método Field.setAccessible(true), que deshabilita os chequeos de acceso para ese campo en particular (para Java Reflections só). Desta forma, se no noso exemplo queremos modificar o valor do atributo privado "alias", só teremos que facer:

Object userInstance = userClass.getConstructor(new Class[] {String.class}).newInstance(new Object[] {"Xosé González"});
Field aliasField = userClass.getDeclaredField("alias");
aliasField.setAccessible(true);
aliasField.set(userInstance, "Pepe");

Métodos

De forma totalmente análoga a como accedemos ós atributos dunha clase, podemos acceder a todos os métodos públicos dunha clase mediante:

Method[] userMethods = userClass.getMethods();

Como podemos adiviñar do exemplo, os métodos dunha clase almacénanse nun obxecto de tipo Method.

Para acceder a un método específico non necesitamos saber só o seu nome, se non que necesitamos saber o tipo e orde dos parámetros necesarios para a súa invocación (posto que recordemos que a sobrecarga de operadores permítenos definir varios métodos co mesmo nome e distintos parámetros). Así, para obter o método setAlias() da nosa clase de exemplo, escribiriamos:

Method userMethod = userClass.getMethod("setAlias", new Class[] {String.class});

Da mesma forma que ocorría cos atributos, estas instrucións só nos devolven os métodos públicos dunha clase. Para poder acceder ós métodos privados deberemos usar respectivamente Class.getDeclaredMethod(String name) e Class.getDeclaredMethods(). Tamén dispomos dun método Method.setAccessible(true) para poder manipular métodos privados dunha clase.

Unha vez temos o método desexado no noso obxecto de tipo Method, procedemos a manipulalo. Para obter o nome e tipo de retorno usaremos métodos análogos ós que usamos para o caso dos atributos:

String methodName = userMethod.getName();
Object methodType = userMethod.getReturnType();

Finalmente, podemos invocar o método desexado mediante a orde Method.invoke():

Object userInstance = userClass.getConstructor(new Class[] {String.class}).newInstance(new Object[] {"Xosé González"});
Method setAliasMethod = userClass.getMethod("setAlias", String.class);
Method getAliasMethod = userClass.getMethod("getAlias", null);
setAliasMethod.invoke(userInstance, "Pepe");
String newAlias = getAliasMethod.invoke(userInstance, null);

O método Method.invoke() debe recibir como primeiro parámetro a instancia en particular da que queremos invocar o método (null se é un método estático) e os parámetros do método que queremos invocar (null ou array baleiro se non dispón de parámetros).

Podes ler máis sobre este tema no noso artigo Java Reflection (parte 3).

Comentarios

9 comentarios. Comentar.

1. Molinero o 23 Mai 2011 ás 11:14:44

Enhorabuena por el blog completo! Me alegro mucho de ver una empresa como la vuestra por estos lares y me apunto vuestro nombre.

2. Anónimo o 05 Set 2013 ás 17:31:30

Increíble. Muy bien explicado, sencillo y útil.

Enhorabuena al autor.

3. Gabriel o 24 Xan 2014 ás 22:07:42

Muchas Gracias! Muy útil y muy bien explicado!

4. Angel Gabriel o 05 Feb 2014 ás 15:00:03

Muchas Gracias! Muy útil y muy bien explicado!

5. LBVP o 21 Feb 2014 ás 17:12:07

Excelente explicación, muchas gracias

6. Anónimo o 11 Set 2014 ás 13:37:42

Creo que en vez de

Object userInstance = userClass.getConstructor(new Class[] {String.class}).newInstance(new Object[] {"José González"});

Method setAliasMethod = userClass.getMethod("setAlias", String.class);

Method getAliasMethod = userClass.getMethod("getAlias", null);

userClass.invoke(userInstance, "Pepe");

String newAlias = userClass.invoke(userInstance, null);

debería ser:

Object userInstance = userClass.getConstructor(new Class[] {String.class}).newInstance(new Object[] {"José González"});

Method setAliasMethod = userClass.getMethod("setAlias", String.class);

Method getAliasMethod = userClass.getMethod("getAlias", null);

setAliasMethod.invoke(userInstance, "Pepe");

String newAlias = getAliasMethod.invoke(userInstance, null);

7. Luis o 16 Feb 2015 ás 12:59:17

Hola Anónimo.

Tienes razón. Había un error en el artículo. Ya lo hemos corregido tal como has puesto.

Gracias.

8. Erick o 10 Xuñ 2015 ás 16:54:13

Muy buen articulo, me sirvió, gracias:)

9. Luis Fernando Corredor Mora. o 08 Abr 2016 ás 16:51:53

Muy bueno el articulo. Me sirvió bastante. 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?