如何在运行时在不同的类之间传递Callback方法

时间:2016-06-20 19:40:59

标签: java methods interface callback pass-by-reference

我现在花了几天时间处理这个琐碎的问题,但却找不到解决办法。

以下是我要做的事情: 读取行输入并根据具体情况更改数据的解释 关键字。这些关键字可以由客户端对象动态注册。他们 注册关键字“回调”功能(想要更好的词)。 遇到关键字时会调用此“回调”函数 处理输入字符串并返回标准化对象。

为此,我创建了一个HashMap,其中Keyword为键,Method引用为value。 在程序初始化期间,所有想要使用该服务的对象都会注册它。

初始化后,读取输入并检查我的hashmap类型的关键字。如果那里存在类型,则传递值字符串以进行处理,并返回字符串数据中的标准化Object表示。

我设法通过查找其名称和检索来哄骗方法参考 方法。下面的代码可以传递,存储和检索方法,但是当尝试调用它时,它会产生IllegalArgument异常。

在我的研究过程中,我发现了几个类似问题的报告,其中也包括在这个网站上,但大多数都没有解决我的确切问题。

one case中,有人建议调用方法不起作用,因为没有实例化方法的实例。可以通过调用newInstance方法来修复该方法所需的实例化。它对我不起作用,因为我无法从该方法引用中获取新实例。

尝试了各种不同的方法,从接口到Java8方法引用和监听器模式。他们都允许传递方法引用,但我总是遇到一个死机,因为我不得不在XSettings中声明客户端的实例。这恰恰是 我不能做什么,因为我不知道如何改变XSettings。

以下是我的代码:

Locus.java

---------------------------------
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.function.Function;

...

public class Locus implements StringConverter{
    private long x;
    private long y;

    public Locus(int x, int y) {
        super();
        this.x = x;
        this.y = y;     
    }

    public Locus(long x, long y) {
        super();
        this.x = x;
        this.y = y;     
    }

    public Locus(String s) {
        super();

        Locus lc = (Locus) convert(s);
        this.x = lc.x;
        this.y = lc.y;
    }

    private long getLongFromString(String s1){
        long res=0L;
        if(s1.matches("[0-9]*$"))       // Integer detected
            res = (long)(Integer.parseInt(s1));
                                        // Long detected
        else if(s1.matches("[0-9]*[lL]$")){
            s1=s1.split("[lL]")[0];
            if(!s1.isempty())              // don't parse empty strings
                res = Long.parseLong(s1);
        }                                // Double detected
        else if(s1.matches("[0-9][0-9]*\\.[0-9]*$"))
            res = (long) Double.parseDouble(s1);

        return res; 
    }

    public Object convert (String s) {
        long x=0, y=0;

        if(s.contains(",")){
            String[] parts = s.split("\\,");
            parts[0] = parts[0].trim();
            parts[1] = parts[1].trim();

            x = getLongFromString(parts[0]);
            y = getLongFromString(parts[1]);
        }
        return createLocus(x, y);
    }

    public static void init() {
        Method method = null;       
        try {
            method = Locus.class.getDeclaredMethod("convert", String.class);
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }      

        if(method != null)
            XSettings.setTypeHandler("Locus", method);
    }
  ...
}

---------------------------------

XSettings.java

---------------------------------
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Iterator;

public class XSettings {

    // *************************************************
    // Singleton Pattern
    private XSettings(){}

    private static class InstanceHolder {
        public static final XSettings instance = new XSettings();
    }

    public XSettings getInstance() { 
        return InstanceHolder.instance;
    }

    // *************************************************

    static final String cfgFileName="C:\\workspace\\Router\\route.cfg";
                          // Map with the payload data to be queried
    public static HashMap<String, Object> settings = new HashMap<String, Object>();
                          // Map with Key / method-references
    static HashMap<String, Method> typeHandler = new HashMap<String, Method>();

                          // caller method for the object converters 
    public static Object convertToType(String type, String value){
        Object result = null;
      Method method = typeHandler.get(type);

        if(method!=null){  // method exists
            try {
                result = method.invoke(method, value);  // <------ causes IllegalArgumentException
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } else {
            result=value;    // No? Default pass back the input string
        }
        return result;
    }
                      // store the method refernce in map
    public static void setTypeHandler(String key, final Method method) {
        typeHandler.put(key, method);
    }
                         // Store a payload key value pair
    public static void set(String key, Object value) {
        settings.put(key, value);
    }
                           // Retrieve a value by key
    public static Object get(String key) {
        return settings.get(key);
    }

    public static void init(){     // read input
        String line;
        try (
            InputStream fis = new FileInputStream(cfgFileName);
            InputStreamReader isr = new InputStreamReader(fis, Charset.forName("UTF-8"));
            BufferedReader br = new BufferedReader(isr);
            ) 
        {
            while ((line = br.readLine()) != null) {    Parse the lines
                String key="";
                String type="";
                String val="";
                line=line.trim();                    // trim back empty space
                System.out.println(line);

                if(line.isEmpty())                   // skip empty lines
                    continue;
                if(line.charAt(0)=='#')              // skip comment lines
                    continue;

                String[] subs = line.split("=",2);   // split at assignment
                    continue;

                subs[0]=subs[0].trim();              // key Part
                subs[1]=subs[1].trim();              // value Part
                String[] subValues = subs[0].split("::", 2);   //Split at type, key section
                if(subValues.length==1){             // empty value
                    key = subValues[0];
                } else if(subValues.length>1){       // normal key, value case
                    type = subValues[0];
                    key = subValues[1];
                } else {                             // no assignment operator, empty value
                    key = subs[0];              
                }   
                subValues = subs[1].split("\"");     // remove double quotes from value part
                if(subValues.length==0){             // no quotes and no value
                    val = "";
                } else if(subValues.length==1){      // no quotes
                    val = subValues [0];
                } else if(subValues.length==2){      // quotes present, stripped
                    val = subValues [1];                    
                } else                               // more than two quotes treat as a string only
                    val = subs[1];

                if(!typeExists(type)){               // check if type is in methodHandler
                    key = subs[0];                             // No: set key back to full L-Value
                    type = "";
                }

                settings.put(key, convertToType(type, val)); // Call converter
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private static boolean typeExists(String type) {
        boolean res=false;
        if(type!=null)   
            if(!(type.isEmpty()))
                Object m = typeHandler.get(type);
                    res=(m!=null);

        return res;
    }

...

}
---------------------------------

服务功能是XSettings,客户端是Locus。 XSettings读取 逐行输入文件,扫描它以获取关键值对。钥匙可能 包含目标类名称。因此该行分为Type,Key和Value。

价值的解释因类型而异。默认情况下,仅存储字符串 远。当其他对象注册关键字并提供转换方法时 他们被拾起并转换成所需的对象。所以当关键是 查询结果是正确的对象。

我设法将方法引用一直到调用点。该 将对象传递给setTypeHandler时,对象在Locus中看起来是相同的 XSettings的方法,以及从HashMap和之前检索之后的方法 调用。然后是IllegalArgumentException ...

这可能是一种非常“C”式的做法,但我一直认为这样做 相对高效和优雅。但是,如果有更好的类似Java的方式,我会很高兴听到它。

感谢您的关注。真的很期待你的回音。

1 个答案:

答案 0 :(得分:0)

我认为在XSettings.convertToType中,首先需要创建指定类型的实例,然后在此实例上调用指定的方法。

创建此类型的实例意味着:加载此类型的类,使用反射api搜索默认构造函数并调用此构造函数以获取此类型的实例。