Java generics: generischen Typ ermitteln?

DarthShader

Erfahrenes Mitglied
Hallo,

ist es möglich, den Typ einer generischen Variable zu ermitteln? Nehmen wir mal folgende (sinnfreie) Methode an:

Java:
public < T > T genericMethod() {

  System.out.println( "Typ von T ist " +  );
}

Die stelle von "" möchte ich nun durch sowas wie "T.getClass()" ersetzen (T.getClass() funktioniert natürlich nicht).

Nun weiß ich um das "Object erasure" in Java Generics, zur Laufzeit ist von den Generics also nichts mehr bekannt, da der Compiler die generischen Typen konkretisiert. Aber müsste es dann nicht auch irgendwie möglich sein, an den Typ von "T" im Code ranzukommen?


Danke für Eure Hilfe!
 
Hallo,

so wie du die Methode deklariert hast, musst du ein Class-Object übergeben, damit der generische Typ festgesetzt wird.

Code:
public < T > T genericMethod(Class<T> clazz) 
{
   System.out.println( "Typ von T ist " +  );
}

In diesem Fall hast du natürlich alles was du brauchst.

MFG

Sascha
 
Hallo,

mein Ziel ist es, genau das zu verhindern. Es geht mir eigentlich (vereinfacht gesagt) darum, eine andere Methode zu kapseln, die genau das braucht: als Parameter einen Typ, also ein Class Objekt.

Mit einen unsicheren Cast ist sowas ja auch möglich:

Java:
public < T > T myMethod() {
  return (T)getObject();
}

Sodass sowas geht:
Integer i = myMethod();
String s = myMethod();

(Solange getObject nach Integer und String konvertiert werden kann - ist natürlich hier ein doofes Beispiel, aber das Prinzip sollte klar werden).

Nun habe ich aber statt "getObject()" ein "getObject( Class< ? > clazz )" da drin, und ich möchte diesen Parameter "loswerden". Ich dachte halt, hey, den Typ T hast Du ja, kannst Du den Typ dann nicht einfach "getObject" übergeben?
Aber ich bezweifle selbst, dass das möglich ist...
 
Das kann so nicht funktionieren. Weil die Methode so gar nicht weiß, von welcher Klasse T ist. Du musst ja der Methode sagen, von welchem Typ T ist und das funktioniert nur so.

MFG

Sascha
 
Geht nicht, ist nicht ganz richtig. Geht schon, ist aber in den meisten Fällen unbrauchbar. Wenn man von dem Typ zur Laufzeit eine anonyme Subklasse erstellt, kann man tatsächlich auf den generischen Typ zugreifen (Tom hatte hier irgendwo mal ein Beispiel dazu).

Ansonsten kommt man nicht drumrum, ein Class<T> mitzugeben.

Gruß
Ollie
 
@Olli
Bist du sicher, dass es sowas war?

Wenn ich mich richtig erinnere, handelt das Beispiel von Tom von einer generischen Klasse, die so instanziert wird.

Code:
List<Integer> list = new ArrayList<Integer>()

Bei dem Beispiel von DarthShader passiert sowas ja nirgends. Das heißt der Typ T wurde nie irgendwo definiert. Ich glaube, das geht auch mit Toms Beispiel nicht.

MFG

Sascha
 
Nein, nicht ganz... anonymous Subtype:

List<Integer> list = new ArrayList<Integer>() {}

Dafür erzeugt die VM Zur Laufzeit eine Subklasse über für die man den generischen Typ bekommt. Ich glaub mit getClass().getGenericInterfaces() o.ä.

Frag Tom ;)

REINHAUN!
 
Das ist hier aber im moment was ganz anderes. :-)
Das worauf es mir in dem Beispiel mit der Liste ankam war das <Integer>, das gibt es aber bei der Methode von DarkShader nicht.

MFG

Sascha
 
Hallo,

schau mal hier:
Java:
package de.tutorials;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 
 */

/**
 * @author Thomas.Darimont
 * 
 */
public class GenericTypeDetectionExample {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>() {
		};

		System.out.println(((ParameterizedType) list.getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0]);

		List<String> listA = createList();
		
		System.out.println(((ParameterizedType) listA.getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0]);

		String string = create();
		System.out.println(System.identityHashCode(string));

		System.out.println(new TypeVariable<String>() {
		}.getType());

		System.out.println(getParameterTypesFromInterface(new Service(),
				IService.class));
	}

	private static Map<String, Class<?>> getParameterTypesFromInterface(
			Object target, Class<?> iface) {
		Map<String, Class<?>> parameterNameToTypeMap = new HashMap<String, Class<?>>();

		Class<?> targetClass = target.getClass();
		for (Method ifaceMethod : iface.getMethods()) {
			Type ifaceMethodGenericReturnType = ifaceMethod
					.getGenericReturnType();
			Type[] ifaceMethodGenericParameterTypes = ifaceMethod
					.getGenericParameterTypes();

			Method targetMethod = findMostSpecificDeclaredMethodBy(ifaceMethod,
					targetClass);

			Type targetMethodGenericReturnType = targetMethod
					.getGenericReturnType();
			Type[] targetMethodGenericParameterTypes = targetMethod
					.getGenericParameterTypes();

			System.out.println(ifaceMethod);

			String parameterName = null;

			if (null != targetMethodGenericReturnType) {
				parameterName = ifaceMethodGenericReturnType.toString();
				parameterNameToTypeMap.put(parameterName,
						(Class<?>) targetMethodGenericReturnType);
			}

			for (int i = 0; i < ifaceMethodGenericParameterTypes.length; i++) {
				parameterName = ifaceMethodGenericParameterTypes[i].toString();
				Type genericParameterType = targetMethodGenericParameterTypes[i];
				if (genericParameterType instanceof ParameterizedType) {
					ParameterizedType parameterizedType = (ParameterizedType) genericParameterType;
					System.out.println(parameterizedType
							.getActualTypeArguments());
				}
				parameterNameToTypeMap.put(parameterName,
						(Class<?>) genericParameterType);
			}
		}

		return parameterNameToTypeMap;
	}

	private static Method findMostSpecificDeclaredMethodBy(Method ifaceMethod,
			Class<?> targetClass) {

		Method targetMethod = null;

		Class<?>[] ifaceMethodParameterTypes = ifaceMethod.getParameterTypes();

		for (Method method : targetClass.getDeclaredMethods()) {

			Class<?>[] methodParameterTypes = method.getParameterTypes();

			boolean match = true;

			if (method.getName().equals(ifaceMethod.getName())
					&& ifaceMethod.getReturnType().isAssignableFrom(
							method.getReturnType())
					&& methodParameterTypes.length == ifaceMethodParameterTypes.length) {

				for (int i = 0; i < methodParameterTypes.length; i++) {
					if (!ifaceMethodParameterTypes[i]
							.isAssignableFrom(methodParameterTypes[i])) {
						match = false;
					}
				}

				if (!match) {
					continue;
				}

				if (!method.isSynthetic()) {
					targetMethod = method;
					break;
				}
			}
		}

		return targetMethod;
	}

	private static <T> List<T> createList() {
		return new ArrayList<T>() {
		};
	}

	private static <T> T create(T... t) {
		try {
			return (T) t.getClass().getComponentType().newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return null;
	}

	static class TypeVariable<T> {
		Class<T> type;

		public TypeVariable() {
			this.type = (Class<T>) ((ParameterizedType) getClass()
					.getGenericSuperclass()).getActualTypeArguments()[0];
		}

		public Class<T> getType() {
			return type;
		}
	}

	static interface IService<TResponse, TRequest> {
		TResponse process(TRequest request);
	}

	static class Service implements IService<Integer, String> {
		@Override
		public Integer process(String request) {
			System.out.println("Processing: " + request);
			return request.length();
		}
	}
}

Ausgabe:
Code:
class java.lang.String
T
14718739
class java.lang.String
public java.lang.Integer de.tutorials.GenericTypeDetectionExample$Service.process(java.lang.String)
{TResponse=class java.lang.Integer, TRequest=class java.lang.String}

http://www.tutorials.de/forum/java/299359-frage-zu-generischer-methode.html
http://www.tutorials.de/forum/java/290985-class-mithilfe-von-type-bekommen.html
http://www.tutorials.de/forum/java/...ichen-typen-eines-generic-typs-ermitteln.html

Gruß Tom
 
Vielen Dank für Eure Antworten.

Ich finde das ein spannendes Thema, denn momentan halte ich die Sache eher für eine Einschränkung der Java Generics als ein echtes "geht nicht", weil vielleicht die Anforderung "unsinnig" ist, denn der Typ sollte ja bekannt sein.

Aber bevor ich noch mehr schreibe obwohl ich mich bei dem Thema noch etwas unsicher finde, werde ich mal die Beispiele studieren :)

Danke!
 
Zurück