【问题标题】:How to pass a Callback method between different classes at runtime如何在运行时在不同类之间传递回调方法
【发布时间】:2016-06-20 19:40:59
【问题描述】:

我已经花了几天时间解决这个棘手的问题,但找不到解决方案。

这是我想要做的: 读取一行输入并更改数据的解释,基于某些 关键词。这些关键字可以由客户端对象动态注册。他们 注册一个 keyword 和一个 "callback" 函数(因为需要更好的词)。 当遇到关键字时调用此“回调”函数,以便 处理输入字符串并返回一个标准化的对象。

为此,我创建了一个以关键字为键、方法引用为值的 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 的方式,我会很高兴听到它。

感谢您的关注。非常期待收到您的来信。

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

    • 感谢马丁的提示。但是目前我不知道如何做到这一点。将对此进行调查并报告。
    • 这是我将调用行更改为 ... result = method.getClass().newInstance().invoke(method, value); ...不再是 IllegalArgumentException,现在是 InstantiationException。我想这可能是因为该类中没有空的构造函数。需要检查一下。
    • 解决办法是方法确实没有实例化。但是 getClass 返回的不是轨迹类而是反射类。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-17
    • 1970-01-01
    • 1970-01-01
    • 2014-07-13
    • 2020-06-18
    相关资源
    最近更新 更多