以下方法执行排序。
public List<Comparator<Entity>> sort(Map<String, String> map) {
List<Comparator<Entity>> list = new ArrayList<Comparator<Entity>>();
for (Map.Entry<String, String> entry : map.entrySet()) {
boolean sortOrder = entry.getValue().equalsIgnoreCase("asc");
switch (entry.getKey()) {
case "id":
list.add(sortOrder ? Comparator.comparing(Entity::getId) : Comparator.comparing(Entity::getId, Comparator.reverseOrder()));
break;
case "size":
list.add(sortOrder ? Comparator.comparing(Entity::getSize) : Comparator.comparing(Entity::getSize, Comparator.reverseOrder()));
//break;
}
}
return list;
}
上述方法返回的列表按以下方式使用。
// map is initialized somewhere based on client's interactions with sorting.
// Based on client's interactions, map may be empty or it may contain one or more ordering fields.
if (MapUtils.isNotEmpty(map)) { // map = new LinkedHashMap<String, String>();
List<Comparator<Entity>> comparators = sort(map);
Comparator<Entity> comparator = comparators.remove(0);
for (Comparator<Entity> c : comparators) {
comparator = comparator.thenComparing(c);
}
list = list.stream().sorted(comparator).collect(Collectors.toList());
} else {
// This is the default ordering.
list = list.stream().sorted(Comparator.comparing(Entity::getId).reversed()).collect(Collectors.toList());
}
Entity
包含名为id
的{{1}}类型Integer
和size
类型BigDecimal
,list
类型{{1} }}
由于有几个其他类具有相同的数据类型的相同字段,我希望这个方法是通用的,所以它必须只被定义一次,
List<Entity>
但是这样做,像public <T extends Object> List<Comparator<T>> sort(Map<String, String> map, Class<T> clazz) {
List<Comparator<T>> list = new ArrayList<Comparator<T>>();
// Sorting logic.
return list;
}
这样的表达式不会编译得那么明显,因为泛型类型参数T::getId
的计算结果为T
。
有没有办法在不知道实际类类型的情况下对代码进行编码,以便在需要时可以防止此方法在任何地方重复?
答案 0 :(得分:4)
一种简单的方法,不必依赖反射魔法,就是为具有相同字段的所有类型引入一个公共接口,其数据类型与Entity
相同。
请考虑以下IdSize
接口与以下Entity
。
interface IdSize {
Integer getId();
BigDecimal getSize();
}
class Entity implements IdSize {
private Integer id;
private BigDecimal size;
@Override
public Integer getId() {
return id;
}
@Override
public BigDecimal getSize() {
return size;
}
}
然后你可以使你的方法像这样通用:
public <T extends IdSize> List<Comparator<T>> sort(Map<String, String> map) {
List<Comparator<T>> list = new ArrayList<Comparator<T>>();
for (Map.Entry<String, String> entry : map.entrySet()) {
boolean sortOrder = entry.getValue().equalsIgnoreCase("asc");
Comparator<T> comparator = null;
switch (entry.getKey()) {
case "id":
comparator = Comparator.comparing(IdSize::getId);
break;
case "size":
comparator = Comparator.comparing(IdSize::getSize);
break;
default: // do something here, throw an exception?
}
list.add(sortOrder ? comparator : comparator.reversed());
}
return list;
}
(我重构了一些switch-case语句以删除重复的代码。)。此外,您可能还想添加一个默认子句。
答案 1 :(得分:3)
使用接口:
public interface Sizable {
BigDecimal getSize();
}
public interface Id {
int getId();
}
让您的类实现这些接口并在通用方法中使用它们:
public <T extends Id & Sizable> List<Comparator<T>> sort(Map<String, String> map) {
// ...
}
答案 2 :(得分:2)
你可能需要一些更有活力的东西。一些注释可能会有所帮助
class Shoe
@Column("id")
@Sortable
public int getId(){ ... }
@Column("Description")
public String getDescription(){...}
给定任何类,您可以反映要显示的列,可以排序的列(&#34; id&#34;,...)和列的值("getId()"
,...) 。
答案 3 :(得分:2)
如果你想创建一个复合Comparator
,那么首先填写List
是没有意义的。只需在一次操作中完成:
public static <T> Comparator<T> getOrdering(
Map<String, String> map, Map<String,Comparator<T>> defined) {
return map.entrySet().stream().map(e -> {
Comparator<T> c=defined.get(e.getKey());
return e.getValue().equalsIgnoreCase("asc")? c: c.reversed();
})
.reduce(Comparator::thenComparing)
.orElseThrow(()->new IllegalArgumentException("empty"));
}
这适用于任意类型,但需要为类型提供现有比较器的映射。但是这个map
不是限制,它实际上改进了操作,因为它删除了硬编码的现有命名属性比较器集。您可以使用任意类型Entity
作为示例,如下所示:
Map<String,Comparator<Entity>> map=new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
map.put("id", Comparator.comparing(Entity::getID));
map.put("size", Comparator.comparing(Entity::getSize));
Comparator<Entity> cmp=getOrdering(param, map);
而param
是您问题的有序地图,从属性名称映射到"asc"
或"desc"
。保存预定义比较器的map
可以在初始化代码中创建一次,然后重新使用。
创建代码看起来并不复杂,值得实现动态解决方案,但是,如果你仍然希望这样做,下面是为任意类生成这样一个映射的代码:
public final class DynamicComparators<T> {
public static <T> Map<String,Comparator<T>> getComparators(Class<T> cl) {
return CACHE.get(cl).cast(cl).comps;
}
private static final ClassValue<DynamicComparators> CACHE
=new ClassValue<DynamicComparators>() {
@Override protected DynamicComparators computeValue(Class<?> type) {
return new DynamicComparators<>(type);
}
};
private final Class<T> theClass;
private final Map<String, Comparator<T>> comps;
private DynamicComparators(Class<T> cl) {
theClass=cl;
Map<String,Comparator<T>> map=new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
try {
BeanInfo bi=Introspector.getBeanInfo(cl);
MethodHandles.Lookup l=MethodHandles.lookup();
MethodType invoked=MethodType.methodType(Function.class);
for(PropertyDescriptor pd: bi.getPropertyDescriptors()) {
Method m=pd.getReadMethod();
if(m==null) continue;
Class<?> t=m.getReturnType();
if(!t.isPrimitive() && !Comparable.class.isAssignableFrom(t))
continue;
MethodHandle mh=l.unreflect(m);
MethodType mt=mh.type();
@SuppressWarnings("unchecked")Comparator<T> cmp
= Comparator.comparing((Function<T,Comparable>)LambdaMetafactory
.metafactory(l, "apply", invoked, mt.generic(), mh, mt)
.getTarget().invokeExact());
map.put(pd.getName(), cmp);
}
} catch(Throwable ex) {
throw new RuntimeException(ex);
}
this.comps=Collections.unmodifiableMap(map);
}
@SuppressWarnings("unchecked") <U> DynamicComparators<U> cast(Class<U> cl) {
if(cl!=theClass) throw new ClassCastException();
return (DynamicComparators<U>)this;
}
}