Java中的双重调度自动化

Java中的双重调度自动化,java,double-dispatch,Java,Double Dispatch,我有两个接口Query和Filter(Query是示例中用于简化的一个类,我现在有一个查询),我现在需要编写函数Query.applyFilter()取决于什么过滤器是实过滤器,即NameFilter和DateFilter和每隔一次过滤器的不同功能 我的解决办法如下: interface Filter { public abstract void modifyQuery(Query query); }; class NameFilter implements Filter{ p

我有两个接口
Query
Filter
Query
是示例中用于简化的一个类,我现在有一个查询),我现在需要编写函数
Query.applyFilter()
取决于什么过滤器是实过滤器,即
NameFilter
DateFilter
每隔一次过滤器的不同功能

我的解决办法如下:

interface Filter {
    public abstract void modifyQuery(Query query);
};

class NameFilter implements Filter{
    public void modifyQuery(Query query){
        query.applyFilter(this);
    }
};

class DateFilter implements Filter{
    public void modifyQuery(Query query){
        query.applyFilter(this);
    }
};

class Query {
    public void applyFilter(Filter filter){
        filter.modifyQuery(this);
    }

    void applyFilter(NameFilter* filter) {
        //"applying NameFilter";
    }

    void applyFilter(DateFilter* filter) {
       //apply dateFilter
   }
}
好的,接下来我需要为每个过滤器类重写
modifyQuery()
实现

然后,我有了在C++中避免这种情况的解决方案:我们使用模板并在
modifyQuery()
中强制转换:

#包括
#包括
#包括
#包括
使用名称空间std;
类查询;
类过滤器
{
公众:
虚空修改查询(查询*查询)=0;
};
模板
类筛选器:公共IFilter
{
公众:
虚空修改查询(查询*查询);
};
类数据过滤器;
类名称过滤器;
类查询
{
公众:
无效应用过滤器(IFilter*过滤器)
{

cout在找到正确的方法时,Java不考虑方法参数的运行时类型。Java只考虑变量类型而不是值

解决方案:

Java 6注释可以用来注释方法、实现多方法和值分派。所有这些都可以在运行时完成,而不需要任何特殊的编译或预处理,并且使用起来仍然相当友好

我们需要引入两个“简单”注释进行注释:

方法
:此方法实现了什么多方法

参数
:我们应该分配什么值

然后,我们可以处理注释,并构建一个实现特定多方法的方法列表。该列表需要排序,以便最具体的方法排在第一位。“最具体”意味着对于每个方法参数(从左到右),参数类型/值更专业(例如,它是一个子类或与指定值匹配)。调用多方法意味着调用最具体的适用方法。“适用”意味着方法原型与实际运行时参数和“最具体”匹配这意味着我们可以简单地搜索已排序的列表,并找到第一个适用的列表

注释处理可以封装在一个类中,然后可以在用户定义的方法中使用该类,该方法只需使用实际的运行时参数调用多方法调度代码

实施

接口Multi实现用于标记Multi方法的运行时方法注释:

package jmultimethod;

import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Multi {

    public String value();
}
接口V实现用于指定分派值的运行时参数注释:

package jmultimethod;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface V {

    public String value();
}
多方法代码如下所示:

package jmultimethod;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

public class Multimethod {

    protected String name;
    protected final ArrayList<Method> methods = new ArrayList<Method>();
    protected final MethodComparator methodComparator = new MethodComparator();

    public Multimethod(String name, Class... classes) {
        this.name = name;
        for(Class c: classes) {
            add(c);
        }
    }

    public void add(Class c) {
        for(Method m: c.getMethods()) {
            for(Annotation ma: m.getAnnotations()) {
                if(ma instanceof Multi) {
                    Multi g = (Multi) ma;
                    if(this.name.equals(g.value())) {
                        methods.add(m);
                    }
                }
            }
        }
        sort();
    }

    protected void sort() {
        Method[] a = new Method[methods.size()];
        methods.toArray(a);
        Arrays.sort(a, methodComparator);
        methods.clear();
        for(Method m: a) {
            methods.add(m);
        }
    }

    protected class MethodComparator implements Comparator<Method> {
        @Override
        public int compare(Method l, Method r) {
            // most specific methods first 
            Class[] lc = l.getParameterTypes();
            Class[] rc = r.getParameterTypes();
            for(int i = 0; i < lc.length; i++) {
                String lv = value(l, i);
                String rv = value(r, i);
                if(lv == null) {
                    if(rv != null) {
                        return 1;
                    }
                }
                if(lc[i].isAssignableFrom(rc[i])) {
                    return 1;
                }
            }
            return -1;
        }
    }

    protected String value(Method method, int arg) {
        Annotation[] a = method.getParameterAnnotations()[arg];
        for(Annotation p: a) {
            if(p instanceof V) {
                V v = (V) p;
                return v.value();
            }
        }
        return null;
    }

    protected boolean isApplicable(Method method, Object... args) {
        Class[] c = method.getParameterTypes();
        for(int i = 0; i < c.length; i++) {
            // must be instanceof and equal to annotated value if present 
            if(c[i].isInstance(args[i])) {
                String v = value(method, i);
                if(v != null && !v.equals(args[i])) {
                    return false;
                }
            } else {
                if(args[i] != null || !Object.class.equals(c[i])) {
                    return false;
                }
            }
        }
        return true;
    }

    public Object invoke(Object self, Object... args) {
        Method m = null; // first applicable method (most specific)
        for(Method method: methods) {
            if(isApplicable(method, args)) {
                m = method;
                break;
            }
        }
        if(m == null) {
            throw new RuntimeException("No applicable method '" + name + "'.");
        }
        try {
            return m.invoke(self, args);
        } catch (Exception e) {
            throw new RuntimeException("Method invocation failed '" + name + "'.");
        }
    }
}
  • 带注释方法的名称可以是Java喜欢的任何名称。它很可能与多方法名称不同,因为某些方法的原型可能非常相似,从而导致Java编译器的名称冲突(可能是因为编译器可能存在空值问题)。此外,该方法应该对Multimethod类可见(例如,公共)

  • 通过创建多方法对象来处理注释,例如

     protected Multimethod mm = new Multimethod("myMultimethod", getClass());
    
  • 根据需要使用参数定义多方法“入口点”方法。此方法使用上面创建的多方法对象进行调度,例如

     public void myMultimethod(Object X, Object Y) {
       mm.invoke(this, X, Y);
    }
    
  • 然后,可以像任何普通Java方法一样调用multimethod,例如

  • myMultimethod(1,空)

    限制:

    值分派仅适用于Java注释支持的值,例如字符串类型的值

    以下代码基于

    程序输出(部分编辑以适应屏幕)为:

    提议:

    interface Filter {
        public abstract void modifyQuery(Query query);
    };
    
    class NameFilter implements Filter{
        public void modifyQuery(Query query){
            // Modify query based on "Name"...
        }
    };
    
    class DateFilter implements Filter{
        public void modifyQuery(Query query){
            // Modify query based on "Date"...
        }
    };
    
    class Query {
        public void applyFilter(Filter filter){
            filter.modifyQuery(this);
        }
    
        // No need for other applyFilter() methods - all filters are instances of Filter.
    }
    

    但是,您可以将泛型用作接口的一部分:您可以定义
    Filter
    ,例如,
    NameFilter
    作为扩展
    Filter
    。不知道这是否会有所帮助,但是,您是否考虑过构建器模式?这样您可以为查询创建一个构建器,向构建器添加过滤器,并最终构建
    ld()
    您的
    查询
    。看起来您需要
    接口过滤器
    “我需要为每个过滤器类重写modifyQuery()实现”-好吧,这不是你的类层次结构的全部目的吗?-当你所有的过滤器都是
    过滤器的实例时,为什么你需要
    Query
    中的那些重载方法呢?此外:你有没有注意到你用
    Filter.modifyQuery(这个);
    Query.applyFilter(这个)产生了无休止的递归
    ?@Hanno:只有在没有实现
    void applyFilter(ThisConcreteFilter)的情况下,我才会产生无休止的递归
    。我需要不同的函数,因为我想对它们进行不同的管理。可能会添加其他查询实现。例如,我将SQL DB更改为NoSQL one,我应该能够在不修改筛选器的情况下对其进行更改。例如,筛选器不应该是什么查询(或者最好不知道查询存在,但我无法实现)然后,您需要首先定义一个接口:哪些数据将被提供给过滤器,以及过滤器将返回什么-以一种独立于
    查询
    类型的方式,因此您需要识别用于此接口的不同
    查询
    实现的公共属性。-您希望将过滤器的功能放在哪里y?很自然,它将进入每个过滤器类,
    Query
    将永远不必对不同的过滤器类型进行不同的处理。这就是为什么您首先定义公共接口
    filter
    。哦,好吧,假设查询是接口,所有实现都在QueryImpl中,当过滤器知道什么样的数据时,这是不自然的但是,我使用您的
    查询
    接口应该表示一个抽象,它将是val
     public void myMultimethod(Object X, Object Y) {
       mm.invoke(this, X, Y);
    }
    
    package jmultimethod;
    
    public class AsteroidTest {
    
        class Asteroid {}
    
        class Spaceship {}
    
        @Multi("collide")
        public void collideOO(Object X, Object Y) {
           log("?? Bang, what happened? ", X, Y);
        }
    
        @Multi("collide")
        public void collideAA(Asteroid X, Asteroid Y) {
            log("AA Look at the beautiful fireworks! ", X, Y);
        }
    
        @Multi("collide")
        public void collideAS(Asteroid X, Spaceship Y) {
            log("AS Is it fatal? ", X, Y);
        }
    
        @Multi("collide")
        public void collideSA(Spaceship X, Asteroid Y) {
            log("SA Is it fatal? ", X, Y);
        }
    
        @Multi("collide")
        public void collideSS(Spaceship X, Spaceship Y) {
            log("SS Who's fault was it? ", X, Y);
        }
    
        @Multi("collide")
        public void collide1S(String X, Spaceship Y) {
            log("1S any string? ", X, Y);
        }
    
        @Multi("collide")
        public void collide2S(@V("hi") String X, Spaceship Y) {
            log("2S 'hi' value? ", X, Y);
        }
    
        protected Multimethod mm = new Multimethod("collide", getClass());
    
        public void collide(Object X, Object Y) {
            mm.invoke(this, X, Y);
        }
    
        public void run() {
            Object A = new Asteroid();
            Object S = new Spaceship();
            collide(A, A);
            collide(A, S);
            collide(S, A);
            collide(S, S);
            collide(A, 1);
            collide(2, A);
            collide(S, 3);
            collide(4, S);
            collide(5, null);
            collide(null, null);
            collide("hi", S);
            collide("hello", S);
        }
    
        public void log(Object... args) {
            for(Object o: args) {
                if(o instanceof String) {
                    System.out.print(" " + (String) o);
                } else {
                    System.out.print(" " + o);
                }
            }
            System.out.println();
        }
    
        public static void main(String[] args) throws Exception {
            AsteroidTest t = new AsteroidTest();
            t.run();
        }
    }
    
    AA Look at the beautiful fireworks!  Asteroid@1f24bbbf Asteroid@1f24bbbf
    AS Is it fatal?  Asteroid@1f24bbbf Spaceship@24a20892
    SA Is it fatal?  Spaceship@24a20892 Asteroid@1f24bbbf
    SS Who's fault was it?  Spaceship@24a20892 Spaceship@24a20892
    ?? Bang, what happened?  Asteroid@1f24bbbf 1
    ?? Bang, what happened?  2 Asteroid@1f24bbbf
    ?? Bang, what happened?  Spaceship@24a20892 3
    ?? Bang, what happened?  4 Spaceship@24a20892
    ?? Bang, what happened?  5 null
    ?? Bang, what happened?  null null
    2S 'hi' value?  hi Spaceship@24a20892
    1S any string?  hello Spaceship@24a20892
    
    interface Filter {
        public abstract void modifyQuery(Query query);
    };
    
    class NameFilter implements Filter{
        public void modifyQuery(Query query){
            // Modify query based on "Name"...
        }
    };
    
    class DateFilter implements Filter{
        public void modifyQuery(Query query){
            // Modify query based on "Date"...
        }
    };
    
    class Query {
        public void applyFilter(Filter filter){
            filter.modifyQuery(this);
        }
    
        // No need for other applyFilter() methods - all filters are instances of Filter.
    }