# Konkreten Typ eines generischen Parameters einer Klasse ermitteln?



## DarthShader (8. Juli 2010)

Hallo,

ich würde gerne den festgelegten generischen Typ einer Klasse zur Laufzeit ermitteln. Ich weiß natürlich um das TypeErsarure bei Java Generics, aber wenn ich das hier habe:


```
public abstract class AbstractConverter< S > {
  // ...
}


public class StringConverter< String > {
  // ...
}
```

ist es dann möglich zur Laufzeit zu ermitteln, dass StringConverter als generischen Typen "String" hat? Die Information steht ja konkret dort und ist kein Platzhalter mehr, das müsste doch irgendwie möglich sein.


Über Eure Hilfe würde ich mich sehr freuen


Vielen Dank!


----------



## Steiner_B (8. Juli 2010)

Eine Variante ist sicherlich in der abstrakten Klasse eine abstrakte Methode GetConverterType vorzuschreiben, welche dann in jeder Ableitung implementiert wird und dir den jeweiligen Typ zurückgibt.


----------



## Artorius (8. Juli 2010)

Moin!
Oder so:

```
TypeVariable[] vars = StringConverter.class.getTypeParameters();
for(TypeVariable t: vars){
    System.out.println(t);
}
```

Ausgabe

```
String
```

*grüssle*


----------



## DarthShader (8. Juli 2010)

Nanu, bei mir ist


```
StringConverter.class.getTypeParameters().length == 0
```

mache ich irgendwas offensichtliches falsch?


----------



## zerix (8. Juli 2010)

Hallo,

du musst die Abfrage bei der Elternklasse machen. Also bei der Klasse, die diesen Paramter definiert. 

Gruß

Sascha

//EDIT: Da hab ich mich offensichtlich geirrt. ;-)


----------



## Artorius (8. Juli 2010)

Mal als komplettes Beispiel:

```
package javaapplication5;

import java.lang.reflect.TypeVariable;
import javaapplication5.AbstractConverter.StringConverter;

public class Main {

    public static void main(String args[]) {
       try {

           TypeVariable[] vars = StringConverter.class.getTypeParameters();
           for(TypeVariable t: vars){
               System.out.println(t.getName());

           }

        }
        catch(Exception e){
           e.printStackTrace();
       }
    }

}
```


```
package javaapplication5;

import java.lang.reflect.TypeVariable;

public abstract class AbstractConverter<T> {

    public static class StringConverter< String > extends AbstractConverter{


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

           TypeVariable[] vars = StringConverter.class.getTypeParameters();
           for(TypeVariable t: vars){
               System.out.println(t.getName());

           }

        }
        catch(Exception e){
           e.printStackTrace();
       }
    }
}
```


----------



## DarthShader (8. Juli 2010)

Hallo,

hm ich denke ich erkenne nun einen Teil des Problems - wahrscheinlich ist, dass ich mein Beispiel ganz oben falsch formuliert habe. Eigentlich habe ich soetwas:


```
public abstract class AbstractConverter< S > {
  // ...
}
 
public class StringConverter extends AbstractConverter< String > {
  // ...
}
```

Der StringConverter ist damit ein konkreter typ, indem die Basis-Klasse konkret parametrisiert wird - sorry, das war mein Fehler.

Jetzt bezogen auf die korrigierte Variante, wie bekomme ich nun raus, dass StringConverter quasi den Typ String als konkreten Typ für die Basisklasse hat? Ist das möglich?

Zusatz: ich möchte als Ergebnis nicht "String" haben - also nicht den Namen des generischen Typs, sondern das Ergebnis muss "java.lang.String" sein, also der wirkliche Typ/die Klasse.


----------



## zerix (8. Juli 2010)

Schau mal hier

```
public static void main(String[] args) {
		Type genericSuperclass = StringConverter.class.getGenericSuperclass();
		ParameterizedType genericSuperclass2 = (ParameterizedType) genericSuperclass;
		System.out.println(genericSuperclass2.getActualTypeArguments()[0]);;

	}
```


----------



## Artorius (8. Juli 2010)

```
package javaapplication5;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public abstract class AbstractConverter<T> {

    public static class StringConverter extends AbstractConverter< String > {


    }
    public static void main(String args[]) {
       try {
         Type type = (Class)((ParameterizedType)StringConverter.class.getGenericSuperclass()).getActualTypeArguments()[0];
         System.out.println(type);

        }
        catch(Exception e){
           e.printStackTrace();
       }
    }
}
```


Edit: Mist zu langsam


----------



## Thomas Darimont (8. Juli 2010)

Hallo,

hast du das nicht schonmal gefragt?
http://www.tutorials.de/forum/java/310803-java-generics-generischen-typ-ermitteln.html
http://www.tutorials.de/forum/java/...ichen-typen-eines-generic-typs-ermitteln.html
http://www.tutorials.de/forum/java/299359-frage-zu-generischer-methode.html

Gruß Tom


----------



## DarthShader (8. Juli 2010)

Hallo,

danke an Sascha und Artorius - ja das ist genau das, was ich gesucht habe. Ich hatte es vorher immer via "getSuperclass()" probiert, auf "getGenericSuperclass()" bin ich nicht gekommen - danke!

Wegen der anderen Threads - ich denke die sind thematisch ähnlich, aber nicht gleich, was man ja an der Lösung sieht.


----------



## Thomas Darimont (8. Juli 2010)

Hallo,



> wegen der anderen Threads - ich denke die sind thematisch ähnlich, aber  nicht gleich, was man ja an der Lösung sieht.


Na ja, nicht ganz ;-) Ich glaub der "Trick" mit getGenericSuperClass() ist in jedem Thread drinnen:

Beispielsweise:
http://www.tutorials.de/forum/1602981-post9.html

Gruß Tom


----------



## DarthShader (11. Juli 2010)

Hallo,

wenn ich nochmal eine Frage hinterher stellen dürfte, ich habe nun folgendes Konstrukt:


```
public abstract class AbstractConverter< S, T > implements Converter< S, T >
{
	private Class< S >	sourceType;
	private Class< T >	targetType;
	
	@SuppressWarnings( "unchecked" )
	public AbstractConverter()
	{
		ParameterizedType genericSuperclass = 
			(ParameterizedType)getClass().getGenericSuperclass();

		sourceType = (Class< S >)genericSuperclass.getActualTypeArguments()[ 0 ];
		targetType = (Class< T >)genericSuperclass.getActualTypeArguments()[ 1 ];
	}

    // [...]
}
```

Wenn ich nun eine konkrete Klasse daraus mache:


```
public class StringToIntegerConverter extends AbstractConverter< String, Integer >
{
	// [...]
}
```

dann sind "sourceType" und "targetType" entsprechend "class java.lang.String" und "class java.lang.Integer", sie wurden also korrekt ermittelt.

Wenn ich nun aber folgende konkrete Klasse mache:


```
public class StringToClassConverter extends AbstractConverter< String, Class< ? > >
{
	// [...]
}
```

dann schlägt die Zeile


```
targetType = (Class< T >)genericSuperclass.getActualTypeArguments()[ 1 ];
```

von oben (erstes Listing) fehl mit der Exception:


```
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl cannot be cast to java.lang.Class
	at de.[...].converter.AbstractConverter.<init>(AbstractConverter.java:36)
```


Diese Exception verstehe ich leider nicht, bzw. ich kann nicht ganz den Unterschied zwischen dem funktionierenden und dem nicht funktionierenden Beispiel sehen.


Liegt es an dem Wildcard "?" in "Class< ? >"?
Wie könnte ich dies lösen, sodass "Class< ? >" als richtiger "targetType" (siehe erstes Listing) erkannt wird?

Über Eure erneute Hilfe würde ich mich sehr freuen!


----------



## Thomas Darimont (11. Juli 2010)

Hallo,

für einen generischen Typ Konverter der mit ParameterizedTypes umgehen kann gibts mehrere Implementierungsmöglichkeiten. Willst du als Konvertierungsziel Parameterisierte Typen verwenden so kannst du den oben angegeben TypVariable Trick verwenden. Eine andere Möglichkeit wäre die Verwendung von Raw Types also Class anstatt Class<?> (im Falle von <?> ist das IMHO sogar legitim....)

Siehe auch:
http://www.tutorials.de/forum/java/340418-generische-type-converter.html

Hier mal ein Beispiel für einen Generischen Converter der mit ParameterizedTypes  und RawTypes umgehen kann:
(Siehe StringToClassConversion bzw. StringToGenericClassConversion)

```
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.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

public class ConverterExample {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		IConverter converter = new Converter();

		converter.register(new StringToIntegerConversion());
		converter.register(new StringToCollectionStringConversion());
		converter.register(new StringToCollectionIntegerConversion());
		converter.register(new StringToClassConversion());
		converter.register(new StringToGenericClassConversion());

		int value = converter.convert("4711", Integer.class);
		System.out.println(value);

		Collection<String> values = converter.convert("1,2,3,4",new TypeVariable<Collection<String>>() {});
		System.out.println(values + " " + values.iterator().next().getClass());

		Collection<Integer> numbers = converter.convert("1,2,3,4",new TypeVariable<Collection<Integer>>() {});
		System.out.println(numbers + " " + numbers.iterator().next().getClass());
		
		
		Class<?> clazz = converter.convert("java.lang.String", new TypeVariable<Class<?>>(){});
		System.out.println(clazz);
		
		Class<?> clazz1 = converter.convert("java.lang.String", Class.class);
		System.out.println(clazz1);
		

	}
	
	static class StringToClassConversion implements IConversion<String,Class>{
		public Class convert(String input) {
			try {
				return Class.forName(input);
			} catch (ClassNotFoundException e) {
				throw new RuntimeException(e);
			}
		}
	}
	
	static class StringToGenericClassConversion implements IConversion<String,Class<?>>{
		public Class<?> convert(String input) {
			try {
				return Class.forName(input);
			} catch (ClassNotFoundException e) {
				throw new RuntimeException(e);
			}
		}
	}
	

	static class StringToIntegerConversion implements
			IConversion<String, Integer> {
		public Integer convert(String input) {
			return Integer.valueOf(input);
		}
	}

	static class StringToCollectionStringConversion implements
			IConversion<String, Collection<String>> {
		public Collection<String> convert(String input) {
			return Arrays.asList(input.split(","));
		}
	}

	static class StringToCollectionIntegerConversion implements
			IConversion<String, Collection<Integer>> {
		public Collection<Integer> convert(String input) {
			Collection<Integer> numbers = new ArrayList<Integer>();
			for (String item : input.split(",")) {
				numbers.add(Integer.valueOf(item));
			}
			return numbers;
		}
	}

	static interface IConversion<TInput extends Object, TOutput extends Object> {
		TOutput convert(TInput input);
	}

	static interface IConverter {

		void register(IConversion<? extends Object, ? extends Object> conversion);

		<TOutput> TOutput convert(Object o, Type outputType);

		<TOutput> TOutput convert(Object o, TypeVariable<?> typeVariable);
	}

	static class Converter implements IConverter {

		final static String CONVERSION_METHOD_NAME = "convert";
		Map<Type, Map<Type, IConversion<Object, Object>>> inputToOutputConverters;

		public Converter() {
			inputToOutputConverters = new LinkedHashMap<Type, Map<Type, IConversion<Object, Object>>>();
		}

		@Override
		@SuppressWarnings("unchecked")
		public void register(
				IConversion<? extends Object, ? extends Object> conversion) {
			ConversionInfo conversionInfo = analyze(conversion);

			if (conversionInfo != null) {
				Map<Type, IConversion<Object, Object>> outputToConverter = inputToOutputConverters
						.get(conversionInfo.getInputType());

				if (outputToConverter == null) {
					outputToConverter = new LinkedHashMap<Type, IConversion<Object, Object>>();
					inputToOutputConverters.put(conversionInfo.getInputType(),
							outputToConverter);
				}

				outputToConverter.put(conversionInfo.getOutputType(),
						(IConversion<Object, Object>) conversion);
			}
		}

		@Override
		@SuppressWarnings("unchecked")
		public <TOutput> TOutput convert(Object o, TypeVariable<?> typeVariable) {
			return (TOutput) convert(o, typeVariable.getType());
		}

		@Override
		@SuppressWarnings("unchecked")
		public <TOutput> TOutput convert(Object o, Type outputType) {
			Type compatibleInputType = findCompatibleInputType(o.getClass());
			Type compatibleOutputType = findCompatiblOutputType(
					compatibleInputType, outputType);

			TOutput output = null;

			if (compatibleInputType != null && compatibleOutputType != null) {
				IConversion<Object, Object> conversion = inputToOutputConverters
						.get(compatibleInputType).get(compatibleOutputType);
				System.out.println(String.format(
						"Using conversion: %s to convert from: %s to: %s",
						conversion, compatibleInputType, compatibleOutputType));
				output = (TOutput) conversion.convert(o);

			}

			return output;
		}

		private Type findCompatibleInputType(Type inputType) {
			Type result = null;

			if (inputType != null) {
				if (inputToOutputConverters.containsKey(inputType)) {
					result = inputType;
				} else {
					for (Type candidate : inputToOutputConverters.keySet()) {
						if (typesAreCompatible(candidate, inputType)) {
							result = candidate;
							break;
						}
					}
				}
			}

			return result;
		}

		private Type findCompatiblOutputType(Type inputType, Type outputType) {
			Type result = null;

			if (inputType != null && outputType != null) {
				if (inputToOutputConverters.get(inputType).containsKey(
						outputType)) {
					result = outputType;
				} else {
					for (Type candidate : inputToOutputConverters
							.get(inputType).keySet()) {
						if (typesAreCompatible(candidate, outputType)) {
							result = candidate;
							break;
						}
					}
				}
			}

			return result;
		}

		@SuppressWarnings("unchecked")
		private boolean rawTypesAreCompatible(Type candidate, Type inputType) {
			return ((Class) candidate).isAssignableFrom((Class) inputType);
		}

		private boolean typesAreCompatible(Type candidate, Type type) {
			boolean result = false;

			if (type instanceof Class && candidate instanceof Class
					&& rawTypesAreCompatible(candidate, type)) {
				result = true;
			}

			if (!result && type instanceof ParameterizedType
					&& candidate instanceof ParameterizedType) {
				ParameterizedType pType = (ParameterizedType) type;
				ParameterizedType pCandidate = (ParameterizedType) candidate;

				if (rawTypesAreCompatible(pCandidate.getRawType(), pType
						.getRawType())
						&& Arrays.equals(pCandidate.getActualTypeArguments(),
								pType.getActualTypeArguments())) {
					result = true;
				}
			}

			return result;
		}

		private ConversionInfo analyze(
				IConversion<? extends Object, ? extends Object> converter) {
			ConversionInfo conversionInfo = null;
			for (Method conversionMethodCandidate : converter.getClass()
					.getDeclaredMethods()) {
				if (isConversionMethod(conversionMethodCandidate)) {

					Type returnType = conversionMethodCandidate.getReturnType();
					Type parameterType = conversionMethodCandidate
							.getParameterTypes()[0];

					if (conversionMethodCandidate.getGenericReturnType() instanceof ParameterizedType) {
						returnType = (ParameterizedType) conversionMethodCandidate
								.getGenericReturnType();
						if (conversionMethodCandidate
								.getGenericParameterTypes()[0] instanceof ParameterizedType) {
							parameterType = (ParameterizedType) conversionMethodCandidate
									.getGenericParameterTypes()[0];
						}
					}

					conversionInfo = new ConversionInfo(parameterType,
							returnType);

					break;
				}
			}

			return conversionInfo;
		}

		private boolean isConversionMethod(Method conversionMethodCandidate) {
			return CONVERSION_METHOD_NAME.equals(conversionMethodCandidate
					.getName())
					&& conversionMethodCandidate.getParameterTypes().length == 1
					&& !Void.class.equals(conversionMethodCandidate
							.getReturnType())
					&& !conversionMethodCandidate.isBridge();
		}

	}

	static class ConversionInfo {
		Type inputType;
		Type outputType;

		public ConversionInfo(Type inputType, Type outputType) {
			this.inputType = inputType;
			this.outputType = outputType;
		}

		public Type getInputType() {
			return inputType;
		}

		public Type getOutputType() {
			return outputType;
		}
	}

	static class TypeVariable<TType> {
		Type type;

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

		public Type getType() {
			return type;
		}
	}

}
```

Ausgabe:

```
Using conversion: de.tutorials.ConverterExample$StringToIntegerConversion@d8a7efd to convert from: class java.lang.String to: class java.lang.Integer
4711
Using conversion: de.tutorials.ConverterExample$StringToCollectionStringConversion@196cd7d5 to convert from: class java.lang.String to: java.util.Collection<java.lang.String>
[1, 2, 3, 4] class java.lang.String
Using conversion: de.tutorials.ConverterExample$StringToCollectionIntegerConversion@6d632c2d to convert from: class java.lang.String to: java.util.Collection<java.lang.Integer>
[1, 2, 3, 4] class java.lang.Integer
Using conversion: de.tutorials.ConverterExample$StringToGenericClassConversion@9e97676 to convert from: class java.lang.String to: java.lang.Class<?>
class java.lang.String
Using conversion: de.tutorials.ConverterExample$StringToClassConversion@3e60420f to convert from: class java.lang.String to: class java.lang.Class
class java.lang.String
```

Gruß Tom


----------



## DarthShader (11. Juli 2010)

Hallo Thomas,

vielen Dank für das ausführliche Beispiel - ich hätte nicht gedacht, dass das jetzt noch viel komplizierter wird, denn im Prinzip funktioniert meine Konvertierung, wie oben in den Listings, ja bereits. Nur eben dieser eine Cast 


```
targetType = (Class< T >)genericSuperclass.getActualTypeArguments()[ 1 ];
```

funktioniert nicht, und ich verstehe nicht genau warum.

Falls die Antwort in deinem größeren Beispiel steckt, magst Du mir einen Hinweis geben, wo?


----------



## Thomas Darimont (11. Juli 2010)

Hallo,

ParameterizedType impls lassen sich nicht direkt zu Class casten.Das heißt in deinem Fall: Wenn der ParameterizedType nur ein TypeArgument hat und dieses ein Wildcard ist, so kannst du den RawType das ParameterizedTypes verwenden (getRawType()). In anderen Fällen muss du bei deinem Converter noch ein wenig Zusatzlogik hinterlegen. Wenn du generische Source- und Target-Typen unterstützen möchtest, so kommst du nicht drum herum dir auch die Fälle zu implementieren bei denen Source bzw. Target selbst auch wieder ParameterizedTypes sind. Diese musst du dann analysieren und in ein Format überführen bei dem du die Informationen zum RawType bzw. den ActualTypeArguments bequem auswerten und zugreifen kannst um dann einen passenden Lookup durchführen zu können.

Beispielsweise könnte man sich vorstellen, dass man einen Converter für List<String> nach List<Integer> oder ein List<String> nach List<? extends Number> etc.
Das hab ich so in meinem Beispiel schon ansatzweise drinnen.

Gruß Tom


----------



## DarthShader (11. Juli 2010)

Hallo Thomas,

hm ja, ich verstehe, zumindest Ansatzweise   Ich verstehe auf jeden Fall, warum wir hier eine Fallunterscheidung machen müssen. Dein Beispiel ist etwas komplexer, das muss ich für mich noch länger durcharbeiten denke ich.

Allerdings hast Du in Deinem Beispiel, wenn ich das richtig sehe, gegenüber meinem Vorhaben auch noch eine "Flexibilitätsstufe" mehr. Bei mir geht es ja, zusammengefasst, nur darum, dass der Benutzer der API die Methode "getSourceType" und "getTargetType" nicht selbst implementieren muss. Nehmen wir nochmal an, es soll einen Converter geben, der von String nach List<String> konvertiert. Wie müsste dann meine "getTargetType" Methode aussehen? Der Zieltyp ist ja "List<String>", aber wie sähe das denn manuell-implementiert aus? Sowas in der Art:


```
@Override
public Class< List< String > > getTargetType()
{
	return List< String >.class;
}
```

Das Beispiel ist natürlich quaak, es kompiliert ja nichtmal. Aber vielleicht macht es deutlich, was ich in der Methode erreichen will.


----------

