Thomas Darimont
Erfahrenes Mitglied
Hallo,
hier mal ein (prototypisches) Beispiel für einen generischen Type converter der beliebige Objekt in andere Objekte konvertieren und dabei auch noch mit Generics umgehen kann.
Ausgabe:
Gruß Tom
hier mal ein (prototypisches) Beispiel für einen generischen Type converter der beliebige Objekt in andere Objekte konvertieren und dabei auch noch mit Generics umgehen kann.
Java:
package de.tutorials.conversion;
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());
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());
}
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:
Code:
Using conversion: de.tutorials.conversion.ConverterExample$StringToIntegerConversion@6abf2d5e to convert from: class java.lang.String to: class java.lang.Integer
4711
Using conversion: de.tutorials.conversion.ConverterExample$StringToCollectionStringConversion@38540408 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.conversion.ConverterExample$StringToCollectionIntegerConversion@11975b59 to convert from: class java.lang.String to: java.util.Collection<java.lang.Integer>
[1, 2, 3, 4] class java.lang.Integer
Gruß Tom