Generic implementation of Aggregator - java

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;
}
}

Related

java Generic problems.Why does this work?

public interface IPage<T> extends Serializable {
/** #deprecated */
#Deprecated
default String[] descs() {
return null;
}
/** #deprecated */
#Deprecated
default String[] ascs() {
return null;
}
List<OrderItem> orders();
default Map<Object, Object> condition() {
return null;
}
default boolean optimizeCountSql() {
return true;
}
default boolean isSearchCount() {
return true;
}
default long offset() {
return this.getCurrent() > 0L ? (this.getCurrent() - 1L) * this.getSize() : 0L;
}
default long getPages() {
if (this.getSize() == 0L) {
return 0L;
} else {
long pages = this.getTotal() / this.getSize();
if (this.getTotal() % this.getSize() != 0L) {
++pages;
}
return pages;
}
}
default IPage<T> setPages(long pages) {
return this;
}
default void hitCount(boolean hit) {
}
default boolean isHitCount() {
return false;
}
List<T> getRecords();
IPage<T> setRecords(List<T> records);
long getTotal();
IPage<T> setTotal(long total);
long getSize();
IPage<T> setSize(long size);
long getCurrent();
IPage<T> setCurrent(long current);
default <R> IPage<R> convert(Function<? super T, ? extends R> mapper) {
List<R> collect = (List)this.getRecords().stream().map(mapper).collect(Collectors.toList());
return this.setRecords(collect);
}
default String cacheKey() {
StringBuilder key = new StringBuilder();
key.append(this.offset()).append(":").append(this.getSize());
List<OrderItem> orders = this.orders();
if (CollectionUtils.isNotEmpty(orders)) {
Iterator var3 = orders.iterator();
while(var3.hasNext()) {
OrderItem item = (OrderItem)var3.next();
key.append(":").append(item.getColumn()).append(":").append(item.isAsc());
}
}
return key.toString();
}
}
This is the source code of a framework,
when I use convert() function
default <R> IPage<R> convert(Function<? super T, ? extends R> mapper) {
List<R> collect = (List)this.getRecords().stream().map(mapper).collect(Collectors.toList());
return this.setRecords(collect);
}
what makes me wonder is that The return type is new type variable R
and he just call the this.setRecords(collect);
but setRecords() funcation just receive List <T>!
IPage<T> setRecords(List<T> records);
To verify this, I wrote an interface myself, but the compilation failed
public interface IPage<T> {
IPage<T> setRecords(List<T> list);
default <R> IPage<R> convert() {
List<R> collect = new ArrayList<>();
return this.setRecords(collect); //error
}
}
Can someone help me solve my doubts?Thank you very much!
The source code of my-batis plus contains an additional cast to IPage to change the type of this.
The method code is:
default <R> IPage<R> convert(Function<? super T, ? extends R> mapper) {
List<R> collect = this.getRecords().stream().map(mapper).collect(toList());
return ((IPage<R>) this).setRecords(collect);
}

Java Reflection with Annotation Class not working

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;
}
}

Java generics question on designing the class

I am stuck and unsure how to properly check / ensure that my B is operating on valid types found in the collection.
OperateAll should be generic, accepts a collection of objects, and only uses Bs that are of the type of the objects found in the collection.
public interface B<T> {
public boolean operate(T t);
}
public class OperateAll<T> implements B<T> {
private Collection<T> collection;
public OperateAll(Collection<T> collection) {
this.collection = collection;
}
//I need to ensure T is of type B<T>, so I can do the described if statement
public boolean operate(T t) {
if (t == null) {
return false;
}
for (T item : reference) {
// if !t.operate(item) return false;
}
return true;
}
}
Clarification on the problem:
I need do something like this:
Collection<Integer> collection = new LinkedList<>();
Integer[] numbers = new Integer[]{1, 2, 3, 4, 5};
Collections.addAll(collection, numbers);
B<Integer> op = new OperateAll<>(collection);
B<Integer> validateNumber = new ValidNumber<>();
//if B<String> validateNumber, this should not be allowed as an argument.
op.operate(validateNumber);
This way, validateNumber can check if it can operate on all the items in the collection in op.
If I understood correctly, you don't need the constraint on T. Instead, OperateAll should implement B<B<T>>, not B<T>:
class OperateAll<T> implements B<B<T>> {
private Collection<T> collection;
public OperateAll(Collection<T> collection) {
this.collection = collection;
}
public boolean operate(B<T> t) {
if (t == null) {
return false;
}
for (T item : collection) {
if (!t.operate(item)) return false;
}
return true;
}
}
This makes code like this possible:
Collection<Integer> collection = new LinkedList<>();
Integer[] numbers = new Integer[]{1, 2, 3, 4, 5};
Collections.addAll(collection, numbers);
// note the type of op
B<B<Integer>> op = new OperateAll<>(collection);
B<Integer> validateNumber = x -> x > 3;
op.operate(validateNumber);
B<String> validateString = x -> x.length() > 3;
op.operate(validateString); // error
You can do it this way:
public class OperateAll<T> implements B<B<T>> {
private final Collection<? extends T> collection;
// Bounded wildcard is used here because this class only reads from collection
public OperateAll(Collection<? extends T> collection) {
this.collection = collection;
}
#Override
public boolean operate(B<T> t) {
return t != null && collection.stream().allMatch(t::operate);
}
}
Note: if you want you can implement operate in an imperative way as well:
#Override
public boolean operate(B<T> t) {
if (t == null) return false;
for (T elm : collection)
if (!t.operate(elm)) return false;
return true;
}
Note 2: I suggest using Predicate instead of B.

Java Type Erasure, Generic Lists with Unchecked Assignment

Currently I'm writing a program where I have the following statement.
List<BaseballStatistic> q = BaseballStatistic.FIND.where().eq("teamID", "CHN").query();
Here, it complains
Unchecked assignment: 'java.util.List' to 'java.util.List'. Reason: 'BaseballStatistic.FIND.where().eq("teamID", "CHN")' has raw type, so result of query is erased more...
I have an interface which looks like this
public interface Query<T> {
...
List<T> execute();
}
then an abstract class that implements this interface
public abstract class AbstractQuery<T> implements Query<T> {
Statement _statement = null;
String _tableName;
List<Clause> _clauses;
Class<T> _type;
AbstractQuery(Class<T> type) {
_type = type;
_clauses = new ArrayList<>();
_tableName = type.getAnnotation(Table.class).name();
}
...
public abstract List<T> execute();
}
and finally a concrete implementation:
public class SimpleQuery<T> extends AbstractQuery<T> {
public SimpleQuery(Class<T> type) {
super(type);
}
which houses the following .query function which looks like:
#Override
public List<T> execute() {
try {
JSONObject jsonObject = Peanut.getClient().listStatistics(buildQuery());
if (jsonObject == null || !jsonObject.has("results")) {
return Collections.emptyList();
}
JSONArray columnNames = jsonObject.getJSONArray("columns");
Map<String, Integer> columnNameMap = new HashMap<>();
for (int i = 0; i < columnNames.length(); i++) {
columnNameMap.put((String) columnNames.get(i), i);
}
JSONArray results = jsonObject.getJSONArray("results");
List<T> ts = new ArrayList<>();
for (int i = 0; i < results.length(); i++) {
JSONArray result = results.getJSONArray(i);
T t = _type.newInstance();
for (Field field : ObjectUtils.getFieldsUpTo(t.getClass(), PinotModel.class)) {
if (field.getAnnotation(Column.class) == null) {
continue;
}
Object obj = ObjectUtils.getDefaultValue(field.getType());
String columnName = field.getAnnotation(Column.class).name();
if (columnNameMap.containsKey(columnName)) {
int idx = columnNameMap.get(columnName);
field.setAccessible(true);
field.set(t, ObjectUtils.convertObject(obj, result.get(idx)));
}
}
ts.add(t);
}
return ts;
} catch (Exception e) {
// TODO: Throw Peanut specific error.
Peanut.LOG.error(e);
return Collections.emptyList();
}
}
It seems like here, at compilation, the returned list has lost it's type leading to the warning. If I change the original variable declaration to List the warning will leave, which makes sense.
Is there anyway around this or is there a larger fundamental issue at play?
EDIT:
Query Function that calls execute is here
public List<T> query() {
return _query.execute();
}
And the relationship between SimpleQuery and BaseballStatistic.Find is as follows.
#Table(name = "baseballStats")
public class BaseballStatistic extends PinotModel {
public static final Find FIND = new Find<BaseballStatistic (BaseballStatistic.class) { };
...
and PinotModel looks like
public class PinotModel {
public static class Find<T> {
private final Class<T> type;
protected Find(Class<T> type) {
this.type = type;
}
public Query select(String... s) {
return new SimpleQuery<T>(type).select(s);
}
public Clause where() {
return new SimpleQuery<T>(type).where();
}
public Clause limit(Integer n) {
return new SimpleQuery<T>(type).limit(n);
}
public Clause top(Integer n) {
return new SimpleQuery<T>(type).top(n);
}
public Clause orderBy(String columnName, Order o) {
return new SimpleQuery<T>(type).orderBy(columnName, o);
}
public String tableName() {
return new SimpleQuery<T>(type).getTableName();
}
}
}
There are 2 places that you're missing generic type parameters.
BaseballStatistic.FIND:
public static final Find<BaseballStatistic> FIND = new Find<BaseballStatistic> (BaseballStatistic.class) { };
PinotModel.select:
public Query<T> select(String... s) {
return new SimpleQuery<T>(type).select(s);
}
You're also missing type parameters on PinotModel.where(). Clause would also need a type parameter, including on the AbstractQuery._clauses field.

Get Class of a concrete Map

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.

Categories