Given 2 Class objects how can I get the Class object of a Map?
For example, assume I have:
Class keyClass = Long.class;
Class valueClass = String.class;
How can I get the Class object ofMap<Long,String>?
There is no such class of Map<Long, String>. What you want is Map.class. (or HashMap.class, etc)
Map<String, Integer> map1 = new HashMap<>();
Map<Long, String> map2 = new HashMap<>();
System.out.println(map1.getClass().equals(map2.getClass()));
The result is true.
Map<Long, String> is not a class, but it is a type, ParameterizedType to be exact, sadly java code for constructing them is private, but they are 2 ways to get it, more dynamic way is to implement that interface:
final class ParameterizedTypeImpl implements ParameterizedType {
private final Type[] actualTypeArguments;
private final Class rawType;
#Nullable private final Type ownerType;
ParameterizedTypeImpl(Class rawType, Type[] actualTypeArguments, #Nullable Type ownerType) {
this.actualTypeArguments = actualTypeArguments.clone();
this.rawType = rawType;
if ((ownerType != null) || (rawType.getDeclaringClass() == null)) {
this.ownerType = ownerType;
}
else {
Class declaringClass = rawType.getDeclaringClass();
if (Modifier.isStatic(rawType.getModifiers())) {
this.ownerType = declaringClass;
}
else {
TypeVariable[] typeParameters = declaringClass.getTypeParameters();
if (typeParameters.length == 0) {
this.ownerType = declaringClass;
}
else {
this.ownerType = new ParameterizedTypeImpl(declaringClass, typeParameters, null);
}
}
}
}
#Override
public Type[] getActualTypeArguments() { return this.actualTypeArguments.clone(); }
#Override
public Class getRawType() { return this.rawType; }
#Nullable #Override
public Type getOwnerType() { return this.ownerType; }
#Override public boolean equals(Object o) {
if (o instanceof ParameterizedType) {
ParameterizedType that = (ParameterizedType) o;
if (this == that) return true;
Type thatOwner = that.getOwnerType();
Type thatRawType = that.getRawType();
return Objects.equals(this.ownerType, thatOwner) && Objects.equals(this.rawType, thatRawType) && Arrays.equals(this.actualTypeArguments, that.getActualTypeArguments());
}
return false;
}
#Override
public int hashCode() {
return Arrays.hashCode(this.actualTypeArguments) ^ Objects.hashCode(this.ownerType) ^ Objects.hashCode(this.rawType);
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder(256);
if (this.ownerType != null) {
sb.append(this.ownerType.getTypeName());
sb.append("$");
if (this.ownerType instanceof ParameterizedTypeImpl) {
sb.append(this.rawType.getName().replace(((ParameterizedTypeImpl) this.ownerType).rawType.getName() + "$", ""));
}
else {
sb.append(this.rawType.getSimpleName());
}
}
else {
sb.append(this.rawType.getName());
}
StringJoiner joiner = new StringJoiner(", ", "<", ">");
joiner.setEmptyValue("");
for (Type type : this.actualTypeArguments) {
joiner.add(type.getTypeName());
}
sb.append(joiner.toString());
return sb.toString();
}
}
And then you can just do new ParameterizedTypeImpl(Map.class, new Type[]{String.class, Long.class}, null) note that it would be a good practice to make this class not visible to others and just create some factory methods.
Other less dynamic way is to use type tokens like in gson:
public class TypeToken<T> {
final Type type;
protected TypeToken() {
this.type = this.getClass().getGenericSuperclass();
}
public final Type getType() { return this.type; }
#Override public final int hashCode() { return this.type.hashCode(); }
#Override public final boolean equals(Object o) { return (o instanceof TypeToken<?>) && this.type.equals(((TypeToken<?>) o).type); }
#Override public final String toString() { return this.type.toString(); }
}
and then new TypeToken<Map<String, Long>>{}.getType(); - types needs to be provided at compile time.
There should be some libraries that provide both this methods, but I don't know any right now as I needed to make my own to also support parsing from string.
Related
I'm pretty experienced with Java, however a novice to using Reflection and Annotation classes, which I'm trying to learn for fun. To get some practice, I made an Identifiable class which is designed to add several helpful methods to any class it inherits.
Here is the full class:
abstract class Identifiable<T, K extends Comparable<K>> implements Comparable<Identifiable<T, K>> {
#Retention(RetentionPolicy.RUNTIME)
public #interface Identifier { }
private static Method getMethodAnnotatedWith(final Class<?> type) {
return Arrays.stream(type.getDeclaredMethods())
.filter(m -> m.isAnnotationPresent(Identifier.class))
.findFirst()
.orElse(null);
}
private K id;
#SuppressWarnings("unchecked")
public Identifiable(Class<T> clazz) {
var m = getMethodAnnotatedWith(clazz);
if (m == null) throw new IllegalArgumentException(
clazz.toString() + " does not have a method annotated by #Identifier"
);
try {
id = (K) m.invoke(this);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public int compareTo(#NotNull Identifiable<T, K> i) {
return id.compareTo(i.id);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Identifiable<?, ?> that = (Identifiable<?, ?>) o;
return id == that.id;
}
#Override
public int hashCode() {
return Objects.hash(id);
}
}
And here is how I am trying to design it to work:
class Foo extends Identifiable<Foo, Integer> {
private final int i;
Foo(int i) {
super(Foo.class);
this.i = i;
}
#Identifier
int getI() {
return i;
}
}
However, id is always 0 for some reason, so I'm not sure if it's a problem with my Identifier annotation class or the way I'm using reflection. I'm pretty sure it's the latter since while debugging, I found that it is able to access the method with the annotation. Any help would be appreciated, thanks!
Don't call the annotated method during construction.
If the identifier value is immutable (final), just pass the value to the super constructor.
public Identifiable(K id) {
this.id = id;
}
Foo(int i) {
super(i);
this.i = i;
}
If the identifier value is mutable, you need to change the logic to invoke the method when you need the value, not cache the value during construction.
abstract class Identifiable<T, K extends Comparable<K>> implements Comparable<Identifiable<T, K>> {
#Retention(RetentionPolicy.RUNTIME)
public #interface Identifier {/**/}
private Method idGetter;
protected Identifiable(Class<T> type) {
this.idGetter = Arrays.stream(type.getDeclaredMethods())
.filter(m -> m.isAnnotationPresent(Identifier.class))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(type.getName() + " does not have a method annotated by #Identifier"));
}
#SuppressWarnings("unchecked")
private final K getIdentifiableKey() {
try {
return (K) this.idGetter.invoke(this);
} catch (IllegalAccessException e) {
throw new IllegalAccessError(e.getMessage());
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
#Override
public int compareTo(Identifiable<T, K> that) {
return this.getIdentifiableKey().compareTo(that.getIdentifiableKey());
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Identifiable<?, ?> that = (Identifiable<?, ?>) o;
return this.getIdentifiableKey().equals(that.getIdentifiableKey()); // Call equals(), don't use ==
}
#Override
public int hashCode() {
return Objects.hash(this.getIdentifiableKey());
}
}
Alternatively, use a functional interface and supply it with a method reference.
abstract class Identifiable<T extends Identifiable<T, K>, K extends Comparable<K>> implements Comparable<Identifiable<T, K>> {
private Function<T, K> idGetter;
protected Identifiable(Function<T, K> idGetter) {
this.idGetter = Objects.requireNonNull(idGetter);
}
#Override
#SuppressWarnings("unchecked")
public int compareTo(Identifiable<T, K> that) {
return this.idGetter.apply((T) this).compareTo(that.idGetter.apply((T) that));
}
#Override
#SuppressWarnings("unchecked")
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Identifiable<T, K> that = (Identifiable<T, K>) o;
return this.idGetter.apply((T) this).equals(that.idGetter.apply((T) that));
}
#Override
#SuppressWarnings("unchecked")
public int hashCode() {
return Objects.hash(this.idGetter.apply((T) this));
}
}
class Foo extends Identifiable<Foo, Integer> {
private final int i;
Foo(int i) {
super(Foo::getI);
this.i = i;
}
int getI() {
return i;
}
}
I am trying to use spell to get the type of an expression not the actual instance value. I don't think it's possible, but I wanted to throw it out there.
Consider:
public interface Foo {
Integer getAge();
String getFamilyName();
String getGivenName();
Set<Integer> getSomeData();
Set<Baz> getBazez();
Baz getAnyBaz();
}
public interface Baz {
Integer getBat();
Integer getWhatever();
}
Some Tests
#Test
public void testNestedMemberDataType() throws Exception {
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext();
Expression exp = parser.parseExpression("anyBaz.bat");
Class<?> valueType = exp.getValueType(context);
assertEquals(Integer.class,valueType);
}
#Test
public void testNestedMemberDataTypeSet() throws Exception {
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext();
// this is not valid spell syntax but I would like to do this
// to indicate the type inside the collection
Expression exp = parser.parseExpression("bazez[*].bat");
Class<?> valueType = exp.getValueType(context);
assertEquals(Integer.class,valueType);
}
I ended up actually implementing it as such:
#Override
public Class<?> getDeclaredTypeFor(String attribute) {
Class<?> finalType = entityType;
List<String> nestings = Arrays.asList(attribute.split("\\."));
PropertyDescriptor d = null;
Iterator<String> iter = nestings.iterator();
while (iter.hasNext()) {
String nesting = iter.next();
d = BeanUtils.getPropertyDescriptor(finalType, nesting);
if (d == null) {
Set<Class> allTypes = allTypesFor(finalType, new LinkedHashSet<>());
d = allTypes.stream().map(t -> BeanUtils.getPropertyDescriptor(t, nesting)).filter(p -> p != null).findFirst().orElse(null);
if (d == null)
break;
}
if (iter.hasNext() && isTypeACollection(d.getReadMethod().getGenericReturnType())) {
finalType = getGenericType(d.getReadMethod().getGenericReturnType(), true);
} else {
finalType = d.getPropertyType();
}
}
return d == null ? null : finalType;
}
private Class getGenericType(Type t, boolean isCollection) {
Class clazz;
if (isCollection) {
Type type = ((ParameterizedType) t).getActualTypeArguments()[0];
if (type instanceof WildcardType) {
type = ((WildcardType) type).getUpperBounds()[0];
} else if(type instanceof TypeVariable) {
type = ((TypeVariable) type).getBounds()[0];
}
clazz = (Class) type;
} else if (t instanceof Class) {
clazz = (Class) t;
} else {
throw new RuntimeException("Don't know how to handle " + t.getClass());
}
return clazz;
}
private boolean isTypeACollection(Type t) {
boolean isPossible = t instanceof ParameterizedType;
if (isPossible)
isPossible = Collection.class.isAssignableFrom((Class) ((ParameterizedType) t).getRawType());
return isPossible;
}
private static Set<Class> allTypesFor(Class c, Set<Class> types) {
types.add(c);
Class superclass = c.getSuperclass();
if (superclass != null)
allTypesFor(superclass, types);
for (Class iFace : c.getInterfaces()) {
allTypesFor(iFace, types);
}
return types;
}
If a Person without an Address is persisted and loaded later on, Person contains an Address with all fields set to null.
This (modified) example was taken from coderanch, where a similar problem was reported.
#Embeddable
public class Address {
private String street;
private String postalCode;
}
#Entity
public class Person {
private String name;
#Embedded
private Address home;
}
How can I circumvent this problem? Is is possible to instruct Hibernate not to instantiate an #Embedded object, if all fields are null?
Changing the getters for every #Embedded field seems cumbersome and error prone. Another cumbersome alternative would be the use of #PostLoad, but this is called just for #Entitys, not for #Embeddables.
well, I wrote something which helped in my case. I added it with #PostLoad to #Entitys with #Embeddables:
public class NullableEmbeddedCleanerImpl implements NullableEmbeddedCleaner {
private final static Logger LOGGER = LoggerFactory.getLogger(NullableEmbeddedCleaner.class);
Map<Class<?>, Predicate<Object>> classFilterForNullables = new HashMap<>();
Map<Class<?>, Predicate<Object>> collectionMap = new HashMap<>();
Set<Class<?>> primitiveArrayClasses = new HashSet<Class<?>>();
public NullableEmbeddedCleanerImpl() {
fillPredicates();
fillCollectionMap();
fillPrimitiveArrayClasses();
}
/**
* B C D F I J S Z
*/
private void fillPrimitiveArrayClasses() {
try {
primitiveArrayClasses.addAll(Arrays.asList(Class.forName("[B"), Class.forName("[B"), Class.forName("[C"),
Class.forName("[D"), Class.forName("[F"), Class.forName("[I"), Class.forName("[J"), Class.forName("[S"),
Class.forName("[Z")));
} catch (ClassNotFoundException e) {
LOGGER.error("Class not found", e);
}
}
#SuppressWarnings("unchecked")
private void fillCollectionMap() { // misses Lists, Maps, ...
collection(Set.class, s -> {
final Set<Object> toRemove = new HashSet<>();
for (Object i : s) {
try {
if (clean(i)) {
toRemove.add(i);
}
} catch (Exception e) {
LOGGER.warn("Error cleaning embeddable {} : {}", e, i);
}
}
s.removeAll(toRemove);
return s.isEmpty();
});
}
#Override
public final void embeddables(Object... arg) {
for (Object i : arg) {
try {
clean(i);
} catch (IllegalArgumentException | IllegalAccessException e) {
LOGGER.warn("Error cleaning embeddable {} : {}", e, i);
}
}
}
#Override
public final boolean clean(Object arg) throws IllegalArgumentException, IllegalAccessException {
if (arg == null) {
return true;
}
boolean cleanThis = true;
Vector<Field> fields = new Vector<>();
for (Class<?> clazz = arg.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
}
for (Field field : fields) {
if (!fieldLoop(field, arg)) {
cleanThis = false;
}
}
return cleanThis;
}
private boolean fieldLoop(Field field, Object arg) throws IllegalArgumentException, IllegalAccessException {
if (Modifier.isStatic(field.getModifiers())) { // skip static fields
return true;
}
field.setAccessible(true);
Object fieldValue = field.get(arg);
if (fieldValue == null) {
return true;
}
Class<?> fieldsClass = field.getType();
if (fieldsClass.isPrimitive() || fieldsClass.isEnum()) {
return false; // can not clean primitives nor enums
}
if (fieldsClass.isArray()) {
if (primitiveArrayClasses.contains(fieldsClass)) {
return false; // ignore primitive arrays
} else {
throw new UnsupportedOperationException("Do something useful here"); // object
// arrays
}
}
for (Class<?> clazz : collectionMap.keySet()) {
if (clazz.isAssignableFrom(fieldsClass)) {
boolean emptyCollection = collectionMap.get(clazz).test(fieldValue);
if (!emptyCollection) {
return false;
} else {
field.set(arg, null);
}
return true;
}
}
// test primitives. just classes, no interfaces >>
for (Class<?> fieldClass = fieldsClass; fieldClass != null; fieldClass = fieldClass.getSuperclass()) {
if (classFilterForNullables.containsKey(fieldClass)) {
Predicate<Object> handle = classFilterForNullables.get(fieldClass);
if (handle.test(fieldValue)) {
return true;
} else {
return false;
}
}
}
if (clean(field.get(arg))) { // decent to contained objects
field.set(arg, null);
} else {// non clean-able child exists
return false;
}
return true;
}
private void fillPredicates() {
nullableFilters(String.class, Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class,
Float.class, Double.class, Void.class, LocalDateTime.class, LocalDate.class, LocalTime.class,
OffsetDateTime.class, OffsetTime.class);
classFilterForNullables.put(NullableEmbeddedCleanerImpl.class, n -> true); // always
// filter
}
private void nullableFilters(Class<?>... cs) {
for (Class<?> c : cs) {
classFilterForNullables.put(c, o -> false);
}
}
#SuppressWarnings("unchecked")
final private <T> void collection(Class<T> c, Predicate<T> fun) {
collectionMap.put((Class<?>) c, ((Predicate<Object>) fun));
}
}
I have 2 custom Java classes;
private MyCustomClass1 obj1;
private MyCustomClass2 obj2;
Each of them has multiple attributes as below;
MyCustomClass1 {
attr1,
attr2,
commonattrId,
attr3
}
MyCustomClass2 {
attr4,
attr5,
commonattrId,
attr6
}
So as you can see, there is a common attribute in each of them (commonattrId) which just to add is a Long
There is also a composite class defined as below;
MyCompositeClass {
MyCustomClass1 obj1;
MyCustomClass2 obj2;
}
Now one of my query execution returns below list;
List myList1
and there is another query execution which returns me below list;
List myList2
My question is can I combine the above 2 lists given I have a commonattrId ?
slightly long but the idea is to override equals in MyClass1 and MyClass2:
public static void main(String[] args) throws IOException {
List<MyClass1> myClass1s = new ArrayList<MyClass1>();
myClass1s.add(new MyClass1(1, 1));
myClass1s.add(new MyClass1(2, 2));
List<MyClass2> myClass2s = new ArrayList<MyClass2>();
myClass2s.add(new MyClass2(3, 1));
myClass2s.add(new MyClass2(4, 2));
List<MyComposite> allMyClasses = new ArrayList<MyComposite>();
for(MyClass1 m : myClass1s) { // note: you should take the shorte of the two lists
int index = myClass2s.indexOf(m);
if(index != -1) {
allMyClasses.add(new MyComposite(m, myClass2s.get(index)));
}
}
System.out.println(allMyClasses);
}
static class MyClass1 {
int attr1;
long commonAttrId;
public MyClass1(int attr, long commonAttr) {
this.attr1 = attr;
this.commonAttrId = commonAttr;
}
#Override
public int hashCode() {
int hash = 5;
hash = 83 * hash + (int) (this.commonAttrId ^ (this.commonAttrId >>> 32));
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if(obj instanceof MyClass2) {
return this.commonAttrId == ((MyClass2)obj).commonAttrId;
}
if(obj instanceof MyClass1) {
return this.commonAttrId == ((MyClass1)obj).commonAttrId;
}
return false;
}
#Override
public String toString() {
return "attr1=" + attr1 + ", commonAttrId=" + commonAttrId;
}
}
static class MyClass2 {
int attr2;
long commonAttrId;
public MyClass2(int attr, long commonAttr) {
this.attr2 = attr;
this.commonAttrId = commonAttr;
}
#Override
public int hashCode() {
int hash = 5;
hash = 83 * hash + (int) (this.commonAttrId ^ (this.commonAttrId >>> 32));
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if(obj instanceof MyClass1) {
return this.commonAttrId == ((MyClass1)obj).commonAttrId;
}
if(obj instanceof MyClass2) {
return this.commonAttrId == ((MyClass2)obj).commonAttrId;
}
return false;
}
#Override
public String toString() {
return "attr2=" + attr2 + ", commonAttrId=" + commonAttrId;
}
}
static class MyComposite {
MyClass1 myClass1;
MyClass2 myClass2;
public MyComposite(MyClass1 a, MyClass2 b) {
myClass1 = a;
myClass2 = b;
}
#Override
public String toString() {
return "myClass1=" + myClass1 + ", myClass2=" + myClass2;
}
}
I don't know all the parameters of your problem but there are probably better ways to do this. For example: have both MyClass1 and MyClass2 inherit from a common class (i.e. MyBaseClass) and create a collection of that instead of the composite class MyCompositeClass.
Or instead of Lists you could have sets and create a set intersection.
You could create a map from id to the object for one of the lists and then iterate through the other to create the new List using the data from the map.
List<MyCompositeClass> combine(List<MyCustomClass1> myList1, List<MyCustomClass2> myList2) {
// create map
Map<Long, MyCustomClass1> idToObj = new HashMap<>();
for (MyCustomClass1 o : myList1) {
idToObj.put(o.commonattrId, o);
}
// construct result list
List<MyCompositeClass> result = new ArrayList<>();
for (MyCustomClass2 o : myList2) {
MyCustomClass1 o1 = map.get(o.commonattrId);
if (o1 != null) {
MyCompositeClass combined = new MyCompositeClass();
combined.obj1 = o1;
combined.obj2 = o;
result.add(combined);
}
}
return result;
}
This will only add all possible combinations of objects from both lists, if commonattrId values are pairwise distinct in each list, but since the field name has "Id" as suffix, I made an educated guess...
I am creating a class CommonAggregator which can aggregate any object which is Aggregatable.
public interface Aggregatable<T> {
public void addFieldValue(T t, AggregationField<T> field);
}
public class Charges implements Aggregatable<Charges>{
//privat fields
//Getter and Setters
#Override
public void addFieldValue(Charges Charges, AggregationField<Charges> field){
if(ChargeDetailAggregationField.TYPE1 == field){
Type1 += Charges.getType1();
}else if(ChargeDetailAggregationField.TYPE2 == field){
Type2 += Charges.getType2();
}else if(ChargeDetailAggregationField.TYPE3 == field){
Type3 += Charges.getType3();
}
}
}
public class CommonAggregator<T extends Aggregatable<T>> {
private static enum AggregationOperation {
SUM, MAX, MIN, AVG;
}
private AggregationField<T>[] fieldsForSum;
private AggregationField<T>[] fieldsForMax;
private AggregationField<T>[] fieldsForMin;
//private AggregationField groupByField = null;
public CommonAggregator<T> sum(AggregationField<T>... fields){
this.fieldsForSum = fields;
return this;
}
public CommonAggregator<T> max(AggregationField<T>... fields){
this.fieldsForMax = fields;
return this;
}
public CommonAggregator<T> min(AggregationField<T>... fields){
this.fieldsForMin = fields;
return this;
}
private <T> void performOperation(AggregationOperation op,AggregatedResponse<T> aggregatedDetails,List<T> aggregatables,AggregationField<T>... fields){
Aggregatable<T> aggregatedResponse = (Aggregatable<T>) getNewInstance();
T response = null;
for(AggregationField<T> field:fields){
if(op == AggregationOperation.MAX){
response = max(field,aggregatables);//Compilation Err
}else if(op == AggregationOperation.MIN){
response = min(field,aggregatables);//Compilation Err
}else if(op == AggregationOperation.SUM){
response = sum(field,aggregatables);//Compilation Err
}
aggregatedResponse.setFieldValue(response, field);
if(op == AggregationOperation.MAX){
aggregatedDetails.setMax(aggregatedResponse);
}else if(op == AggregationOperation.MIN){
aggregatedDetails.setMin(aggregatedResponse);
}else if(op == AggregationOperation.SUM){
aggregatedDetails.setSum(aggregatedResponse);
}
}
}
private T max(AggregationField<T> field,List<T> aggregatables){
CommonComparator<T> comparator = new CommonComparator<T>(SortOrder.ASCENDING, field);
return Collections.max(aggregatables, comparator);
}
private T min(AggregationField<T> field,List<T> aggregatables){
CommonComparator<T> comparator = new CommonComparator<T>(SortOrder.ASCENDING, field);
return Collections.min(aggregatables, comparator);
}
private T sum(AggregationField<T> field,List<T> listOfAggregatables){
T aggregatable = listOfAggregatables.get(0);
for(T response :listOfAggregatables.subList(1, listOfAggregatables.size())){
aggregatable.addFieldValue(response, field);
}
return aggregatable;
}
public AggregatedResponse<T> aggregate(List<T> aggregatables){
AggregatedResponse<T> aggregatedDetails = new AggregatedResponse<T>();
if(fieldsForMax != null)
performOperation(AggregationOperation.MAX,aggregatedDetails,aggregatables,fieldsForMax);
if(fieldsForMin != null)
performOperation(AggregationOperation.MIN,aggregatedDetails,aggregatables,fieldsForMin);
if(fieldsForSum != null)
performOperation(AggregationOperation.SUM,aggregatedDetails,aggregatables,fieldsForSum);
return aggregatedDetails;
}
public <E> Map<E,List<T>> groupBy(AggregationField<T> fieldName, List<T> listOfAggregatable){
Map<E,List<T>> groupedList = new HashMap<E,List<T>>();
for(T t:listOfAggregatable){
List<T> subList = null;
E fieldValue = (E)t.getFieldValue(fieldName);
if((subList = groupedList.get(fieldValue)) != null){
subList.add(t);
}else{
subList = new ArrayList<T>();
subList.add(t);
groupedList.put(fieldValue,subList);
}
}
return groupedList;
}
public <E> Map<E,AggregatedResponse<T>> groupByWithAggregation(AggregationField<T> fieldName, List<T> listOfAggregatable){
//groupByField = fieldName;
Map<E, List<T>> groupedByList = groupBy(fieldName, listOfAggregatable);
Map<E,AggregatedResponse<T>> mapOfAggregatedDetails = new HashMap<E, AggregatedResponse<T>>();
for(E key : groupedByList.keySet()){
mapOfAggregatedDetails.put(key, aggregate(groupedByList.get(key)));
}
return mapOfAggregatedDetails;
}
:
}
This is not the complete code.
Here, AggregationField tells which field of Aggregatable class has to be aggregated.
Problem:
I have facing followinf error when calling max(), min(), and sum() in performOperation()
The method max(AggregationField< T>, List< T>) in the type CommonAggregator< T> is not applicable for the arguments (AggregationField< T>, List< T>)
Edit: I have modified the original code and question after #Mikhail suggestion.
I am not good in generics. And I guess I am doing something wrong in generics only.
public interface AggregationField <T>
{
// ...
}
public interface Aggregatable <T>
{
public void addFieldValue (T t, AggregationField <T> field);
}
public class Charges implements Aggregatable <Charges>
{
#Override
public void addFieldValue (
Charges Charges, AggregationField <Charges> field)
{
// ...
}
}
public class CommonAggregator <T extends Aggregatable <T>> {
private T sum (
AggregationField <T> field,
List <? extends T> listOfAggregatables)
{
T aggregatable = listOfAggregatables.get(0);
for (T response: listOfAggregatables.subList (1, listOfAggregatables.size())){
aggregatable.addFieldValue(response, field);
}
return aggregatable;
}
}