问题
Suppose we have a fixed type hierarchy, e.g. as depicted below. It is a well-defined tree, where each node has one parent (except for the root).
Each type has an action associated with it, that should be performed upon successful matching. This does not mean that an action corresponds to a method on said type. It's just arbitrary association.
What are intelligent ways to match objects against a type hierarchy? Each object should be matched against the most specific type possible. The objects are already created.
回答1:
Use recursive search from the root.
Once no match can be found in children, remember the matched object if its level is deeper than the last match.
Pseudocode:
class MatchContext {
public int level;
public Node result;
}
public boolean match(Node object, int level, MatchContext ctx) {
if (no match)
return false;
boolean found = false;
for (all children in object) {
if (match(child, level + 1, ctx))
found = true;
}
if (!found && level > ctx.level) {
ctx.level = level;
ctx.result = this;
}
return found;
}
Invoke it like this:
MatchContext ctx;
if (match(root, 0, ctx))
myAction(ctx.result);
回答2:
The tree hierarchy is already implicitly defined by the declaration of the classes. You just need to traverse one tree branch via chained getSuperclass()
calls to the type you want to lookup. The tree nodes(class types) can then be organized using a simple hash map.
Given that the type hierarchy is static, you could define it as enum
public enum ClassType{
HOMINOIDEA(HOMINOIDEA.class),
HOMINIDAE(HOMINIDAE.class),
HOMININAE(HOMININAE.class),
//and so on
UNKNOWN(null);
private static final Map<Class<?>, ClassType> typesMap = new HashMap<>();
public final Class<?> type;
static{
for (ClassType classType : EnumSet.allOf(ClassType.class)){
if(classType.type != null){
typesMap.put(classType.type, classType);
}
}
}
private ClassType(Class<?> type){
this.type = type;
}
public static ClassType getClassTypeOf(Class<?> type){
for(Class<?> lookupType = type; lookupType != null; lookupType = lookupType.getSuperclass()){
ClassType classType = typesMap.get(lookupType);
if(classType != null){
return classType;
}
}
return UNKNOWN;
}
}
and then map the class types to actions:
public static void main(String[] args){
EnumMap<ClassType, Action> actionMap = new EnumMap<>(ClassType.class);
actionMap.put(ClassType.HOMININAE, new HomininaeAction());
Homininae h = new Homininae();
actionMap.get(ClassType.getClassTypeOf(h)); //action associated with homininaes
}
Here is an other in some terms more dynamic version
public class ActionDispatcher {
private final Map<Class<?>, Consumer<?>> actionMap = new HashMap<>();
public <T> void registerAction(Class<T> type, Consumer<? super T> action){
actionMap.put(type, action);
}
@SuppressWarnings("unchecked")
public void dispatchActionFor(Object object){
Consumer<Object> action = ((Consumer<Object>)getActionFor(object.getClass()));
if(action != null){
action.accept(object);
}
}
private Consumer<?> getActionFor(Class<?> type){
for(Class<?> lookupType = type; lookupType != null; lookupType = lookupType.getSuperclass()){
Consumer<?> action = actionMap.get(lookupType);
if(action != null){
return action;
}
}
return null;
}
//demo
public static void main(String[] args){
ActionDispatcher dispatcher = new ActionDispatcher();
dispatcher.registerAction(Number.class, n -> System.out.println("number: " + n));
dispatcher.registerAction(Double.class, d -> System.out.println("double: " + d));
dispatcher.registerAction(String.class, s -> System.out.println("first char: " + s.charAt(0)));
dispatcher.registerAction(Object.class, o -> System.out.println("object: " + o));
dispatcher.dispatchActionFor(new Integer(3));
dispatcher.dispatchActionFor(new Double(3.0));
dispatcher.dispatchActionFor("string");
dispatcher.dispatchActionFor(new Thread());
}
}
The output of this is:
number: 3
double: 3.0
first char: s
object: Thread[Thread-0,5,main]
来源:https://stackoverflow.com/questions/42002904/matching-against-type-hierarchy