Sorting with Java 8 by Field given as Input - java

I have a REST endpoint and I want the UI to pass the field name that they want to sort their result by "id", "name", etc. I came up with below, but was really trying to use Reflection / Generics so this could be expanded to encompass every object in my project.
I feel like this solution isn't easily maintainable if I want to have the same functionality for 100 different classes.
public static void sort(List<MovieDTO> collection, String field) {
if(collection == null || collection.size() < 1 || field == null || field.isEmpty()) {
return;
}
switch(field.trim().toLowerCase()) {
case "id":
collection.sort(Comparator.comparing(MovieDTO::getId));
break;
case "name":
collection.sort(Comparator.comparing(MovieDTO::getName));
break;
case "year":
collection.sort(Comparator.comparing(MovieDTO::getYear));
break;
case "rating":
collection.sort(Comparator.comparing(MovieDTO::getRating));
break;
default:
collection.sort(Comparator.comparing(MovieDTO::getId));
break;
}
}
Any ideas on how I could implement this better so that it can be expanded to work for an enterprise application with little maintenance?

Original post
I won't repeat everything said in the comments. There are good thoughts there. I hope you understand that reflection is not an optimal choice here.
I would suggest keeping a Map<String, Function<MovieDTO, String>>, where the key is a field name, the value is a mapper movie -> field:
Map<String, Function<MovieDTO, String>> extractors = ImmutableMap.of(
"id", MovieDTO::getId,
"name", MovieDTO::getName
);
Then, the collection can be sorted like:
Function<MovieDTO, String> extractor = extractors.getOrDefault(
field.trim().toLowerCase(),
MovieDTO::getId
);
collection.sort(Comparator.comparing(extractor));
Playing with reflection
As I promised, I am adding my vision of annotation processing to help you out. Note, it's not a version you have to stick firmly. It's rather a good point to start with.
I declared 2 annotations.
To clarify a getter name ( if not specified, <get + FieldName> is the pattern):
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD})
#interface FieldExtractor {
String getterName();
}
To define all possible sorting keys for a class:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE})
#interface SortingFields {
String[] fields();
}
The class MovieDTO has been given the following look:
#SortingFields(fields = {"id", "name"})
class MovieDTO implements Comparable<MovieDTO> {
#FieldExtractor(getterName = "getIdentifier")
private Long id;
private String name;
public Long getIdentifier() {
return id;
}
public String getName() {
return name;
}
...
}
I didn't change the sort method signature (though, it would simplify the task):
public static <T> void sort(List<T> collection, String field) throws NoSuchMethodException, NoSuchFieldException {
if (collection == null || collection.isEmpty() || field == null || field.isEmpty()) {
return;
}
// get a generic type of the collection
Class<?> genericType = ActualGenericTypeExtractor.extractFromType(collection.getClass().getGenericSuperclass());
// get a key-extractor function
Function<T, Comparable<? super Object>> extractor = SortingKeyExtractor.extractFromClassByFieldName(genericType, field);
// sort
collection.sort(Comparator.comparing(extractor));
}
As you may see, I needed to introduce 2 classes to accomplish:
class ActualGenericTypeExtractor {
public static Class<?> extractFromType(Type type) {
// check if it is a waw type
if (!(type instanceof ParameterizedType)) {
throw new IllegalArgumentException("Raw type has been found! Specify a generic type for further scanning.");
}
// return the first generic type
return (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0];
}
}
class SortingKeyExtractor {
#SuppressWarnings("unchecked")
public static <T> Function<T, Comparable<? super Object>> extractFromClassByFieldName(Class<?> type, String fieldName) throws NoSuchFieldException, NoSuchMethodException {
// check if the fieldName is in allowed fields
validateFieldName(type, fieldName);
// fetch a key-extractor method
Method method = findExtractorForField(type, type.getDeclaredField(fieldName));
// form a Function with a method invocation inside
return (T instance) -> {
try {
return (Comparable<? super Object>) method.invoke(instance);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
};
}
private static Method findExtractorForField(Class<?> type, Field field) throws NoSuchMethodException {
// generate the default name for a getter
String fieldName = "get" + StringUtil.capitalize(field.getName());
// override it if specified by the annotation
if (field.isAnnotationPresent(FieldExtractor.class)) {
fieldName = field.getAnnotation(FieldExtractor.class).getterName();
}
System.out.println("> Fetching a method with the name [" + fieldName + "]...");
return type.getDeclaredMethod(fieldName);
}
private static void validateFieldName(Class<?> type, String fieldName) {
if (!type.isAnnotationPresent(SortingFields.class)) {
throw new IllegalArgumentException("A list of sorting fields hasn't been specified!");
}
SortingFields annotation = type.getAnnotation(SortingFields.class);
for (String field : annotation.fields()) {
if (field.equals(fieldName)) {
System.out.println("> The given field name [" + fieldName + "] is allowed!");
return;
}
}
throw new IllegalArgumentException("The given field is not allowed to be a sorting key!");
}
}
It looks a bit complicated, but it's the price for generalisation. Of course, there is room for improvements, and if you pointed them out, I would be glad to look over.

Well, you could create a Function that would be generic for your types:
private static <T, R> Function<T, R> findFunction(Class<T> clazz, String fieldName, Class<R> fieldType) throws Throwable {
MethodHandles.Lookup caller = MethodHandles.lookup();
MethodType getter = MethodType.methodType(fieldType);
MethodHandle target = caller.findVirtual(clazz, "get" + fieldName, getter);
MethodType func = target.type();
CallSite site = LambdaMetafactory.metafactory(caller,
"apply",
MethodType.methodType(Function.class),
func.erase(),
target,
func);
MethodHandle factory = site.getTarget();
Function<T, R> function = (Function<T, R>) factory.invoke();
return function;
}
The only problem is that you need to know the types, via the last parameter fieldType

I'd use jOOR library and the following snippet:
public static <T, U extends Comparable<U>> void sort(final List<T> collection, final String fieldName) {
collection.sort(comparing(ob -> (U) on(ob).get(fieldName)));
}

Consider using ComparatorChain from apache commons.
Take a look to this answer https://stackoverflow.com/a/20093642/3790546 to see how to use it.

Related

Java calling method based on type

I'm making a Java class where I can apply a filter (searchterms) on a List with DTO's. The filter looks like:
[{ field: "price", value: "10.0" }, { field: "name", value: "%phone%" }]
In my class I have the following method, to apply all the filters to the list:
public List<T> applyFilters(List<T> input, ArrayList<LinkedHashMap<String, String>> searchTerms) {
for (LinkedHashMap<String, String> searchTerm : searchTerms) {
input = input.stream()
.filter(row -> {
try {
return applySingleFilter(row, searchTerm);
} catch (Exception e) {
throw new CustomGraphQLException(400, "The filter field is not a valid field in this type");
}
})
.collect(Collectors.toList());
}
return input;
}
But the applySingleFilter has different implementations based on the type of a field. Like for Strings I create a Regex:
private boolean applySingleStringFilter (T category, LinkedHashMap<String, String> searchTerm) throws Exception {
String patternString = createCompareRegex(searchTerm.get("value"));
String propertyValue = (String) PropertyUtils.getProperty(category, searchTerm.get("field"));
return propertyValue.matches(patternString);
}
But for like a Float I want another comparison, I don't want to apply the Regex to a float. What is the best way to make sure the correct method is called based on the type of the field?
Well, you'd first need to know the type of the field and once you have that information you could maintain a repository of filters that apply to the type (some casting might be required though).
We had a similar system in place which in essence looked like this:
interface Filter<T> {
Class<T> getHandledType();
boolean apply(T element);
}
class DoubleFilter implements Filter<Double> {
public Class<Double> getHandledType() { return Double.class; }
boolean apply(Double element) {
//filter here
}
}
The repository was basically a Map<Class<?>, Filter<?>> and using it was like:
Object fieldValue = //get the field value;
Class<?> fieldType = fieldValue.getClass(); //ofc, check for null first
Filter<?> filter = repo.get(fieldType);
if( filter != null ) {
//nasty cast to a raw type to tell the compiler to allow the call
((Filter)filter).apply(fieldValue);
}

Using a generic type of any nested subclass within its abstract superclass

Suppose you have the following abstract java class:
public abstract class AbstractRequestHandler<I,O> {
I input;
O output;
}
and the following child classes hierarchy:
public abstract class AbstractUserRequestHandler<I extends User,O> extends AbstractRequestHandler<I,O>{...}
public abstract class AbstractUniversityRequestHandler<I extends UniversityUser> extends AbstractUserRequestHandler<I,String>{...}
public class StudentRequestHandler extends AbstractUniversityRequestHandler<Student>{...}
public class TeacherRequestHandler extends AbstractUniversityRequestHandler<Teacher>{...}
Suppose you need to use at a given point on the super class the generic type, for example in order to deserialize on the constructor the request json to the specific request object using gson library as follow:
public AbstractRequestHandler(final String inputJson) {
input = new Gson().fromJson(inputJson,typeOfI);
}
You need the type of generic I within variable "typeOfI"
Is there a global solution that allows to get the generic type specified by a concrete child class that respects the following constraints?
The type is gotten at runtime regardless the child classes hierarchy ( that can be also more complex the one given as example on this question )
The developer just needs to define the generic extending the super class without manually specify the generic type somewhere on concrete child class ( for example on overrided method or constructor )
So that if you want to define a new concrete child that assign a new value to a generic you can just write the following concrete class for example:
public class StudentRequestHandler extends AbstractUniversityRequestHandler<Student>{
public StudentRequestHandler(String inputJson) {
super(inputJson);
}
}
I found the following solutions but they don't respect both the asked solution constraints.
Solution that breaks constraint n°2
A solution could be to define an abstract method on the superclass as follow
protected abstract Type getRequestType();
and then implement it on every concrete child class that defines the generic:
public class StudentRequestHandler extends AbstractUniversityRequestHandler<Student>{
public StudentRequestHandler(String inputJson) {
super(inputJson);
}
#Override
protected Type getRequestType() {
return Student.class;
}
}
Then the getRequestType() method can be used on constructor on the target superclass:
public AbstractRequestHandler(final String inputJson) {
request = new Gson().fromJson(inputJson,getRequestType());
}
But even if it works regardless the child classes hierarchy ( respect constraint n°1 ) the developer should manually implement an abstract method on each concrete child class.
Solution that breaks constraint n°1
If the hierarchy is simple having only a direct child that extend from the target superclass, as for example:
public class TeacherRequestHandler extends AbstractRequestHandler<Teacher,String>{...}
a working solution has been proposed by #naikus ( https://stackoverflow.com/users/306602/naikus ) on the following stackoverflow thread:
Using a generic type of a subclass within it's abstract superclass?
However this doesn't work if the concrete class is not a direct child of the superclass that defines the generics ( as the one proposed as example on this question ).
Edit: after reading your answer and testing many other possible cases I decided to edit your code and re-write it to support all other possible edge cases to include tracking of generics nested deeply inside other generic types.
Sadly to support all cases we need a lot more code than you provided, generics are very tricky, like consider class like this:
private class SomeClass<A, B, C, D, E, F> {}
private class SomeConfusingClass<A> extends SomeClass<List<Void>[], List<? extends A>[], List<? extends A[][][]>[][][], List<? extends String[]>[], Map<List<? extends A[]>, A[][]>[], A> {}
private class TestClass extends SomeConfusingClass<Void> {}
To even start doing this we need to have own implementation of java generic types to later be able to construct types like List<String>[] as there is no way to create such type dynamically with raw java API.
This is pretty popular way of handling generic in libraries like that, you can see similar thing in jackson library and many more.
So we need implementation of GenericArrayType, ParameterizedType and WildcardType:
private static class ResolvedGenericArrayType implements GenericArrayType {
private final Type genericComponentType;
ResolvedGenericArrayType(Type genericComponentType) {
this.genericComponentType = genericComponentType;
}
#Override
public Type getGenericComponentType() {
return genericComponentType;
}
public String toString() {
return getGenericComponentType().toString() + "[]";
}
#Override
public boolean equals(Object o) {
if (o instanceof GenericArrayType) {
GenericArrayType that = (GenericArrayType) o;
return Objects.equals(genericComponentType, that.getGenericComponentType());
} else
return false;
}
#Override
public int hashCode() {
return Objects.hashCode(genericComponentType);
}
}
private static class ResolvedParameterizedType implements ParameterizedType {
private final Type[] actualTypeArguments;
private final Class<?> rawType;
private final Type ownerType;
private ResolvedParameterizedType(Type rawType, Type[] actualTypeArguments, Type ownerType) {
this.actualTypeArguments = actualTypeArguments;
this.rawType = (Class<?>) rawType;
this.ownerType = (ownerType != null) ? ownerType : this.rawType.getDeclaringClass();
}
public Type[] getActualTypeArguments() {
return actualTypeArguments.clone();
}
public Class<?> getRawType() {
return rawType;
}
public Type getOwnerType() {
return ownerType;
}
#Override
public boolean equals(Object o) {
if (!(o instanceof ParameterizedType)) {
return false;
}
ParameterizedType that = (ParameterizedType) o;
if (this == that)
return true;
Type thatOwner = that.getOwnerType();
Type thatRawType = that.getRawType();
return Objects.equals(ownerType, thatOwner) && Objects.equals(rawType, thatRawType) &&
Arrays.equals(actualTypeArguments, that.getActualTypeArguments());
}
#Override
public int hashCode() {
return Arrays.hashCode(actualTypeArguments) ^
Objects.hashCode(ownerType) ^
Objects.hashCode(rawType);
}
public String toString() {
StringBuilder sb = new StringBuilder();
if (ownerType != null) {
sb.append(ownerType.getTypeName());
sb.append("$");
if (ownerType instanceof ResolvedParameterizedType) {
sb.append(rawType.getName().replace(((ResolvedParameterizedType) ownerType).rawType.getName() + "$", ""));
} else
sb.append(rawType.getSimpleName());
} else
sb.append(rawType.getName());
if (actualTypeArguments != null) {
StringJoiner sj = new StringJoiner(", ", "<", ">");
sj.setEmptyValue("");
for (Type t : actualTypeArguments) {
sj.add(t.getTypeName());
}
sb.append(sj.toString());
}
return sb.toString();
}
}
private static class ResolvedWildcardType implements WildcardType {
private final Type[] upperBounds;
private final Type[] lowerBounds;
public ResolvedWildcardType(Type[] upperBounds, Type[] lowerBounds) {
this.upperBounds = upperBounds;
this.lowerBounds = lowerBounds;
}
public Type[] getUpperBounds() {
return upperBounds.clone();
}
public Type[] getLowerBounds() {
return lowerBounds.clone();
}
public String toString() {
Type[] lowerBounds = getLowerBounds();
Type[] bounds = lowerBounds;
StringBuilder sb = new StringBuilder();
if (lowerBounds.length > 0)
sb.append("? super ");
else {
Type[] upperBounds = getUpperBounds();
if (upperBounds.length > 0 && !upperBounds[0].equals(Object.class)) {
bounds = upperBounds;
sb.append("? extends ");
} else
return "?";
}
StringJoiner sj = new StringJoiner(" & ");
for (Type bound : bounds) {
sj.add(bound.getTypeName());
}
sb.append(sj.toString());
return sb.toString();
}
#Override
public boolean equals(Object o) {
if (o instanceof WildcardType) {
WildcardType that = (WildcardType) o;
return Arrays.equals(this.getLowerBounds(), that.getLowerBounds()) && Arrays.equals(this.getUpperBounds(), that.getUpperBounds());
} else
return false;
}
#Override
public int hashCode() {
Type[] lowerBounds = getLowerBounds();
Type[] upperBounds = getUpperBounds();
return Arrays.hashCode(lowerBounds) ^ Arrays.hashCode(upperBounds);
}
}
You can basically copy them from JDK and just do some cleanup.
Next utility we need is a function to validate at the end if we did everything right, like we don't want to return Map<List<? extends X>[]> where X is still not resolved TypeVariable:
private static boolean isDefined(Type type) {
if (type instanceof Class) {
return true;
}
if (type instanceof GenericArrayType) {
return isDefined(((GenericArrayType) type).getGenericComponentType());
}
if (type instanceof WildcardType) {
for (Type lowerBound : ((WildcardType) type).getLowerBounds()) {
if (!isDefined(lowerBound)) {
return false;
}
}
for (Type upperBound : ((WildcardType) type).getUpperBounds()) {
if (!isDefined(upperBound)) {
return false;
}
}
return true;
}
if (!(type instanceof ParameterizedType)) {
return false;
}
for (Type typeArgument : ((ParameterizedType) type).getActualTypeArguments()) {
if (!isDefined(typeArgument)) {
return false;
}
}
return true;
}
Simple recursive function will do this for us. We just check for every possible generic type and check if every member of it is also defined, and unless we will find some hidden TypeVariable we are fine.
Main function can stay the same as in your code, we only will edit that one check at the end to use our new function:
public static Type getParameterizedType(Class<?> klass, Class<?> rootClass, int paramTypeNumber) throws GenericsException {
int targetClassParametersNumber = rootClass.getTypeParameters().length;
if (targetClassParametersNumber == 0) {
throw new GenericsException(String.format("Target class [%s] has no parameters type", rootClass.getName()));
} else if (targetClassParametersNumber - 1 < paramTypeNumber)
throw new GenericsException(String.format("Target class [%s] has parameters type which index start from [0] to [%s]. You requested instead parameter with index [%s]", rootClass, paramTypeNumber - 1, targetClassParametersNumber));
Type type = analyzeParameterizedTypes(klass, klass, rootClass, paramTypeNumber, null);
if (!isDefined(type))
throw new GenericsException(String.format("Parameter [%s] with index [%d] defined on class [%s] has not been valued yet on child class [%s]", type, paramTypeNumber, rootClass.getName(), klass.getName()));
return type;
}
Now lets work on our main
public static Type analyzeParameterizedTypes(final Class<?> klass, final Class<?> targetClass, final Class<?> rootClass, final int paramTypeNumber, Map<Integer, Type> childClassTypes) throws GenericsException {
function, the begging stays the same, we collect all TypeVariable to simple map, keeping already collected information from previous loop on previous class.
Type superclassType = klass.getGenericSuperclass();
Map<TypeVariable<?>, Type> currentClassTypes = new HashMap<>();
int z = 0;
if (childClassTypes != null) {
for (TypeVariable<?> variable : klass.getTypeParameters()) {
currentClassTypes.put(variable, childClassTypes.get(z));
z++;
}
}
Then we have our loop collecting and refining our type arguments:
Map<Integer, Type> superClassesTypes = new HashMap<>();
if (superclassType instanceof ParameterizedType) {
int i = 0;
for (final Type argType : ((ParameterizedType) superclassType).getActualTypeArguments()) {
if (argType instanceof TypeVariable) {
superClassesTypes.put(i, currentClassTypes.containsKey(argType) ? currentClassTypes.get(argType) : argType);
} else {
superClassesTypes.put(i, refineType(klass, argType, currentClassTypesByName));
}
i++;
}
}
There 2 paths for each type argument, if its TypeVariable we just keep tracking it, and if its anything else we try to "refine" it from any possible references to TypeVariable. This is the most complicated process of this code, and this is why we needed all these classes above.
We start from this simple recursive dispatch method that handles all possible types:
private static Type refineType(Type type, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException {
if (type instanceof Class) {
return type;
}
if (type instanceof GenericArrayType) {
return refineArrayType((GenericArrayType) type, typeVariablesMap);
}
if (type instanceof ParameterizedType) {
return refineParameterizedType((ParameterizedType) type, typeVariablesMap);
}
if (type instanceof WildcardType) {
return refineWildcardType((WildcardType) type, typeVariablesMap);
}
if (type instanceof TypeVariable) {
return typeVariablesMap.get(type);
}
throw new GenericsException("Unsolvable generic type: " + type);
}
And small utility method to run it on array of types:
private static Type[] refineTypes(Type[] types, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException {
Type[] refinedTypes = new Type[types.length];
for (int i = 0; i < types.length; i++) {
refinedTypes[i] = refineType(types[i], typeVariablesMap);
}
return refinedTypes;
}
Each type goes to own function, or if its TypeVariable we just fetch resolved one from map. Note that this can return null, and I did not handle it here. This could be improved later. For classes we don't need to do anything so we can just return class itself.
For GenericArrayType we need to first find out how many dimension such array might have (this could be handled by recursion in our refine method too, but then its a bit harder to debug in my opinion):
private static int getArrayDimensions(GenericArrayType genericArrayType) {
int levels = 1;
GenericArrayType currentArrayLevel = genericArrayType;
while (currentArrayLevel.getGenericComponentType() instanceof GenericArrayType) {
currentArrayLevel = (GenericArrayType) currentArrayLevel.getGenericComponentType();
levels += 1;
}
return levels;
}
Then we want to extract that nested component type of array, so for List<A>[][][] we want just List<A>:
private static Type getArrayNestedComponentType(GenericArrayType genericArrayType) {
GenericArrayType currentArrayLevel = genericArrayType;
while (currentArrayLevel.getGenericComponentType() instanceof GenericArrayType) {
currentArrayLevel = (GenericArrayType) currentArrayLevel.getGenericComponentType();
}
return currentArrayLevel.getGenericComponentType();
}
And then we need to refine this type, so our List<A> will change to eg List<String>:
Type arrayComponentType = refineType(getArrayNestedComponentType(genericArrayType), typeVariablesMap);
And rebuild our generic structure using refined type, so our created List<String> will change back to List<String>[][][]:
private static Type buildArrayType(Type componentType, int levels) throws GenericsException {
if (componentType instanceof Class) {
return Array.newInstance(((Class<?>) componentType), new int[levels]).getClass();
} else if (componentType instanceof ParameterizedType) {
GenericArrayType genericArrayType = new ResolvedGenericArrayType(componentType);
for (int i = 1; i < levels; i++) {
genericArrayType = new ResolvedGenericArrayType(genericArrayType);
}
return genericArrayType;
} else {
throw new GenericsException("Array can't be of generic type");
}
}
And whole function looks like this:
private static Type refineArrayType( GenericArrayType genericArrayType, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException {
int levels = getArrayDimensions(genericArrayType);
Type arrayComponentType = refineType(getArrayNestedComponentType(genericArrayType), typeVariablesMap);
return buildArrayType(arrayComponentType, levels);
}
For ParameterizedType its much simpler, we just refine type arguments, and create new ParameterizedType instance with these refined arguments:
private static Type refineParameterizedType(ParameterizedType parameterizedType, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException {
Type[] refinedTypeArguments = refineTypes(parameterizedType.getActualTypeArguments(), typeVariablesMap);
return new ResolvedParameterizedType(parameterizedType.getRawType(), refinedTypeArguments, parameterizedType.getOwnerType());
}
Same for WildcardType:
private static Type refineWildcardType(WildcardType wildcardType, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException {
Type[] refinedUpperBounds = refineTypes(wildcardType.getUpperBounds(), typeVariablesMap);
Type[] refinedLowerBounds = refineTypes(wildcardType.getLowerBounds(), typeVariablesMap);
return new ResolvedWildcardType(refinedUpperBounds, refinedLowerBounds);
}
And this leaves us with whole analyze function looking like this:
public static Type analyzeParameterizedTypes(final Class<?> klass, final Class<?> targetClass, final Class<?> rootClass, final int paramTypeNumber, Map<Integer, Type> childClassTypes) throws GenericsException {
Type superclassType = klass.getGenericSuperclass();
Map<TypeVariable<?>, Type> currentClassTypes = new HashMap<>();
int z = 0;
if (childClassTypes != null) {
for (TypeVariable<?> variable : klass.getTypeParameters()) {
currentClassTypes.put(variable, childClassTypes.get(z));
z++;
}
}
Map<Integer, Type> superClassesTypes = new HashMap<>();
if (superclassType instanceof ParameterizedType) {
int i = 0;
for (final Type argType : ((ParameterizedType) superclassType).getActualTypeArguments()) {
if (argType instanceof TypeVariable) {
superClassesTypes.put(i, currentClassTypes.getOrDefault(argType, argType));
} else {
superClassesTypes.put(i, refineType(argType, currentClassTypes));
}
i++;
}
}
if (klass != rootClass) {
final Class<?> superClass = klass.getSuperclass();
if (superClass == null)
throw new GenericsException(String.format("Class [%s] not found on class parent hierarchy [%s]", rootClass, targetClass));
return analyzeParameterizedTypes(superClass, targetClass, rootClass, paramTypeNumber, superClassesTypes);
}
return childClassTypes.get(paramTypeNumber);
}
Example usage:
private class SomeClass<A, B, C, D, E, F> {}
private class SomeConfusingClass<A> extends SomeClass<List<Void>[], List<? extends A>[], List<? extends A[][][]>[][][], List<? extends String[]>[], Map<List<? extends A[]>, A[][]>[], A> {}
private class TestClass extends SomeConfusingClass<Void> {}
public static void main(String[] args) throws Exception {
System.out.println(GenericsUtils.getParameterizedType(TestClass.class, SomeClass.class, 0));
System.out.println(GenericsUtils.getParameterizedType(TestClass.class, SomeClass.class, 1));
System.out.println(GenericsUtils.getParameterizedType(TestClass.class, SomeClass.class, 2));
System.out.println(GenericsUtils.getParameterizedType(TestClass.class, SomeClass.class, 3));
System.out.println(GenericsUtils.getParameterizedType(TestClass.class, SomeClass.class, 4));
System.out.println(GenericsUtils.getParameterizedType(TestClass.class, SomeClass.class, 5));
}
And results:
java.util.List<java.lang.Void>[]
java.util.List<? extends java.lang.Void>[]
java.util.List<? extends java.lang.Void[][][]>[][][]
java.util.List<? extends java.lang.String[]>[]
java.util.Map<java.util.List<? extends java.lang.Void[]>, java.lang.Void[][]>[]
class java.lang.Void
Whole code with tests can be found here: https://gist.github.com/GotoFinal/33b9e282f270dbfe61907aa830c27587 or here: https://github.com/GotoFinal/generics-utils/tree/edge-cases-1
Based on OP original answer code, but with most of edge cases covered.
The answer is: Java does not support reified generics, see this feature request from 2004 with lots of duplicates. See also:
C# has reified generics, Java does not
Kotlin reified generics example
Java generics, type erasure and again Kotlin's reified generics
So unless you want to switch to Kotlin, there is simply nothing you can do because generic type information in Java is available to the compiler only, not during runtime (reified generics).
I am sorry if you do not like the answer, but still it is correct as of Java 13 in early 2020.
I think a working solution would be to extend the one proposed by #naikus. It only needs to go up in the hierarchy on the constructor.
import java.lang.reflect.ParameterizedType;
public abstract class AbstractRequestHandler<I,O> {
protected I input;
protected O output;
protected Class<I> inputClass;
protected Class<O> outputClass;
protected AbstractRequestHandler() {
Class<?> clazz = getClass();
while (!clazz.getSuperclass().equals(AbstractRequestHandler.class)) {
clazz = clazz.getSuperclass();
}
ParameterizedType genericSuperclass = (ParameterizedType) clazz.getGenericSuperclass();
this.inputClass = (Class<I>) genericSuperclass.getActualTypeArguments()[0];
this.outputClass = (Class<O>) genericSuperclass.getActualTypeArguments()[1];
}
}
I've worked to an utility library that offers a method that generally solve the question analyzing recursively all parent classes hierarchy to get a specific generic type.
It is available on my GitHub project: https://github.com/gregorycallea/generics-utils
UPDATE: Thanks to #GoToFinal user that with his great effort improved the project covering also several differents complex
generics case ( such as GenericArrayType, ParameterizedType and
WildcardType).
For all details about these improvements see his answer on this question.
This is the summarized scenario the method works on:
Suppose you have a parameterized root class with an undefined number of generics defined.
Example: Let's consider as root class the following "Base" class that defines 3 generics:
private class Base<I, E, F> {
I var1;
E var2;
F var3;
}
NOTE: To each generic is assigned an index starting from 0. So index mapping for this class is:
I = 0
E = 1
F = 2
Suppose this root class have a complex and multi-leveled hierarchy of child classes.
Example:
// Base<I,E,F>
// BaseA<G,H> extends Base<H,Boolean,G>
// BaseB<T> extends BaseA<T,String>
// BaseC<H> extends BaseB<H>
// BaseD extends BaseC<Integer>
// BaseE extends BaseD
// BaseF extends BaseE
// BaseG extends BaseF
// BaseH<H> extends BaseG<H,Double>
// BaseI<T> extends BaseF<T>
// BaseL<J> extends BaseI<J>
// BaseM extends BaseL<Float>
// BaseN extends BaseM
NOTE: Notice that walking the child hierarchy new parameterized classes are defined and also some classes are not parameterized at all
Then suppose you want to choose whatever class on root class child hierarchy and then get the exactly type of a specific generic defined on root class starting from this.
Example:
You want to know the type of E generic ( with index = 1 ) defined on Base class starting from child class BaseN.
To do this you can simply execute the GenericsUtils.getParameterizedType method as follow:
Type targetType = GenericsUtils.getParameterizedType(GenericChildClass.class, RootClass.class, genericRootClassIndex);
Example:
Type EType = GenericsUtils.getParameterizedType(BaseN.class, Base.class, 1);
I evaluated several cases for this example scenario with unit tests.
Take a look at:
https://github.com/gregorycallea/generics-utils/blob/master/src/test/java/com/github/gregorycallea/generics/GenericsUtilsTest.java
About the initial scenario exposed on my question instead we can use this method on AbstractRequestHandler constructor as follow:
public abstract class AbstractRequestHandler<I,O> {
I input;
O output;
public AbstractRequestHandler(String inputJson) throws GenericsException {
this.input = new Gson().fromJson(inputJson,GenericsUtils.getParameterizedType(getClass(), AbstractRequestHandler.class, 0));
}
}

How does generic type in declaration really affects the method if it's never used?

I'm looking through HTMLUnit sources and can't quite figure out how is generic type supposed to work here. It's never used inside the method, what's the point of making the method generic?
public <T> List<T> getByXPath(final String xpathExpr) {
PrefixResolver prefixResolver = null;
if (hasFeature(XPATH_SELECTION_NAMESPACES)) {
/*
* See if the document has the SelectionNamespaces property defined. If so, then
* create a PrefixResolver that resolves the defined namespaces.
*/
final Document doc = getOwnerDocument();
if (doc instanceof XmlPage) {
final ScriptableObject scriptable = ((XmlPage) doc).getScriptableObject();
if (ScriptableObject.hasProperty(scriptable, "getProperty")) {
final Object selectionNS =
ScriptableObject.callMethod(scriptable, "getProperty", new Object[]{"SelectionNamespaces"});
if (selectionNS != null && !selectionNS.toString().isEmpty()) {
final Map<String, String> namespaces = parseSelectionNamespaces(selectionNS.toString());
if (namespaces != null) {
prefixResolver = new PrefixResolver() {
#Override
public String getBaseIdentifier() {
return namespaces.get("");
}
#Override
public String getNamespaceForPrefix(final String prefix) {
return namespaces.get(prefix);
}
#Override
public String getNamespaceForPrefix(final String prefix, final Node node) {
throw new UnsupportedOperationException();
}
#Override
public boolean handlesNullPrefixes() {
return false;
}
};
}
}
}
}
}
return XPathUtils.getByXPath(this, xpathExpr, prefixResolver);
}
The generics here are used to infer the type T into the XPathUtils.getByXPath method, which signature is
public static <T> List<T> getByXPath(DomNode node, String xpathExpr, PrefixResolver resolver)
When compiler looks at this statement
return XPathUtils.getByXPath(this, xpathExpr, prefixResolver);
it infers the T type from the outer getByXPath method, which in turn infers T from its invocation.
So if you call List<HtmlDivision> list = page.getByXPath(expr); then HtmlDivision will be inferred into getByXPath and then into XPathUtils.getByXPath
It's used for the return type (List<T>), so the returned List will have that specific generic type. This also means that XPathUtils.getByXPath(…) returns a List<T>, since that's what it's returning at the bottom of the method, so it's used inside the method there.

Java Map with variable generics as values

So here's a slightly tricky question (for me).
I have a generic object. Call it MyObject. This object has a method which returns something of the type T:
public class MyObject<T>
{
private T _t;
public MyObject(T t)
{
_t = t;
}
//...
public T get()
{
return _t;
}
}
(Obviously my "MyObject" does a bit more but that's the gist).
Now, I want to have a map of this type:
Map<String, MyObject<?>> m = new HashMap<>();
I want to be able to fetch maps using some predefined string name, and these maps can be of any MyObject. For example, I could call:
m.put("map_1", new MyObject<String>("String"));
m.put("map_2", new MyObject<Integer>(new Integer(3));
m.put("map_3", new MyObject<Long>(new Long(5));
etc.
But - and here's the tricky part - I want the map to "remember" the parameterized type of MyObject when I fetch some value from the map. Using
m.get("map_1");
would return a
MyObject<Object>
type, since the map was defined as containing
MyObject<?>
values. Thus:
m.get("map_1").get() // <-- This is an Object, not a String!
What modification (if any) is possible, in order to be able to get the correct - full - information regarding the MyObject fetched object, such that invoking the last line (m.get("map_1")) would return a
MyObject<String>
Thanks :)
Amir.
Typesafe Heterogeneous Containers from Joshua Bloch's Effective Java might work here. Basically you add a Class object to represent the type.
public class MyObject<T>
{
private T _t;
private Class<T> type;
public MyObject( Class<T> type, T t)
{
_t = t;
this.type = type;
}
//...
public T get()
{
return _t;
}
public Class<T> getType() { return type; }
}
Then you could do something like this:
public <T> T get( Map<String, MyObject<?>> map, String key, Class<T> type ) {
return type.cast( m.get( key ).get() );
}
Which is safe and will compile, but will throw a runtime error if you get the type wrong.
(Note I didn't actually compile that, so I might have syntax errors floating around. But most folks don't know how to use Class to cast objects.)
You can get the class.
Class c = m.get("map_1").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
Here is a full test.
public class GenericsTest {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Map<String, MyObject<?>> map = new HashMap<>();
MyObject<String> obj = new MyObject<>("hello");
map.put("greeting", obj);
Class c = map.get("greeting").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
}
static class MyObject<T> {
T t;
public MyObject(T t) {
this.t = t;
}
T get() {
return t;
}
}
}
The type system only knows about types, not objects, and therefore can not distinguish "key1" from "key2", because both are of type String.
If keys have different types, the easiest way is to encapsulate a weakly typed map, and use reflective casts to prove to the compiler the types are correct:
class Favorites {
private Map<Class<?>,?> map = new HashMap<>();
<V> V get(Class<V> clazz) {
return clazz.cast(map.get(clazz));
}
<V> void put(Class<V> clazz, V value) {
map.put(clazz, value);
}
}
Favorites favs = new Favorites();
favs.put(String.class, "hello");
favs.put(Integer.class, 42);
favs.get(String.class).charAt(1);

Can I make this java pluck() method more safe?

I wrote this utility function:
public static <T> List<T> pluck(String fieldName, List list)
throws NoSuchFieldException, IllegalAccessException {
if (list.isEmpty()) {
return new ArrayList<T>();
}
Class c = list.get(0).getClass();
Field f = c.getField(fieldName);
ArrayList<T> result = Lists.newArrayList();
for (Object object : list) {
result.add((T) f.get(object));
}
return result;
}
I copied the idea from underscore.js. The use case is:
ArrayList<Person> people = new ArrayList<Person>;
people.add(new Person("Alice", "Applebee"));
people.add(new Person("Bob", "Bedmington"));
people.add(new Person("Charlie", "Chang"));
List<String> firstNames = pluck("firstName", people);
My problem is that if the caller gets the type wrong, no exception is thrown until the caller tried to get an object from the list. Ideally, I'd like to throw a ClassCastException from the pluck method itself. However, I don't see a way to access the type of the list on run time.
Is there some trick I can use to make sure the caller doesn't end up with an invalid list?
Edit: So using the feedback I got, a safe implementation would be:
public static <T,F> List<F> pluck(String fieldName, Class<F> fieldType,
List<T> list, Class<T> listType)
throws NoSuchFieldException, IllegalAccessException {
Field f = listType.getField(fieldName);
ArrayList<F> result = new ArrayList<F>();
for (T element : list) {
result.add(fieldType.cast(f.get(element)));
}
return result;
}
But actually lambdaj seems to do what I wanted, so I guess I'll use that. Thanks mike!
Disclaimer: LambdaJ ( #GoogleCode | #GitHub ) - This project is not maintained anymore since the release of JDK8 (JSR 335, JEP 126).
You can change your signature to as follows:
public static <T, F> List<F> pluck(String fieldName, Class<F> fieldType,
List<T> list, Class<T> listType)
The you have the list type and field type.
Why dont you define the signature like this:
public static <T, U> List<T> pluck(String fieldName, Class<T> fieldType, List<U> list);
This would:
1) Force the client to supply the type of the field he wants to "pluck", so you can do proper type-checking in your method.
2) Force the client to supply a generic list from which to "pluck", so you prevent another error-source (the client supplying a list that contains objects of different types).
I think this is as safe as it can get..
What do you mean by invalid list? If you mean that they try to cast it to something it is not then try changing the declaration to public static <T> List<T> pluck(String fieldName, List<T> list).
I'm confused by the However, I don't see a way to access the type of the list on run time. comment. However, if I understand you correctly then: there is no "type" at runtime because generics in Java are implemented by "erasure". This means that the compiler checks at compile time that it works, and then turns it into regular casts like we had before generics. This was necessary they felt to enable backward and forward compatibility.
You should use generics for the type parameter, and pass in the class object of the return type:
public static <TItem, TResult> List<TResult> pluck(String fieldName, List<TItem> list, Class<TResult> resultType)
throws NoSuchFieldException, IllegalAccessException {
if(list.isEmpty()) return new ArrayList<TResult>();
Class c = list.get(0).getClass();
Field f = c.getField(fieldName);
ArrayList<TResult> result = new ArrayList<TResult>();
for(Object object : list) {
result.add(resultType.cast(f.get(object)));
}
return result;
}
Generally, when you get a warning about an unsafe cast to a type parameter, you should see if you can replace it with a call to Class.cast
you can try the one given by Google collections library instead of maintaining a new one: Collections2.transform like this
Collection<Y> yourCollection...
...
Collection<X> expected = Collections2.transform(yourCollection, new Function<Y, X>() {
public X apply(Y element) {
return element.getX();
}
}
With Google's Guava collections library, you can use Collections2.transform().
Usage
Given an interface/class, e.g. called Entity, your class can implement/extend this.
public abstract class Entity {
private long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
public interface Entity {
long getId();
}
Now you can Retrieve a list of each Entity's IDs.
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
public class Main {
public static void main(String[] args) {
List<Entity> entities = ...
List<Long> ids = pluckIds(entities);
}
public static <E extends Entity> List<Long> pluckIds(List<E> list) {
return new ArrayList<Long>(Collections2.transform(list, new Function<E, Long>() {
public Long apply(E entity) {
return entity.getId();
}
});
}
}
This is the safest you can get. This satisfies proper OOP principles and Java 5-7.
In Java 8, you can achieve the same effect with a stream, map, and lambda
public static <E extends Entity> List<Long> pluckIds(List<E> list) {
return list.stream().map(e -> e.getId()).collect(Collectors.toList());
}
or
public static <T,F> List<F> pluck(String fieldName, Class<F> fieldType,
List<T> list, Class<T> listType) throws NoSuchFieldException,
IllegalAccessException, IllegalArgumentException {
Field f = listType.getDeclaredField(fieldName);
f.setAccessible(true);
return list.stream().map(e -> {
try { return fieldType.cast(f.get(e)); } catch (Exception e1) { return null; }
}).collect(Collectors.toList());
}
Not sure what you are asking, but you could try:
Class c = list.get(0).getClass();
if (!c.equals(Person.class))
throw new ClassCastException();
You can cast the list to a java.lang.reflect.ParameterizedType and checking that the array returned by getActualTypeArguments() contains the class you need. Other than that, you're out of luck.

Categories

Resources