【问题标题】:How to replace relective method access by Java 1.8 functions?如何用 Java 1.8 函数替换反射方法访问?
【发布时间】:2017-11-15 10:52:15
【问题描述】:

问题:我们需要为不同类别的对象获取一个(字符串)键。 为了可扩展性,我们想要配置方法来获取密钥字符串——而不是使用 intanceOf 实现许多 if-else……

简单的解决方案(带有示例数据)是:

public static String getKey(Object object, Map<Class<?>, Method> keySources) {
    Method source = keySources.get(object.getClass());

    if (source == null) {
        return null;
    }

    try {
        return (String) source.invoke(object);
    } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        throw new RuntimeException("Error at 'invoke': " + e.getMessage(), e);
    }
}

public static void main(String[] args) {
    Map<Class<?>, Method> keySources = new HashMap<>();

    try {
        keySources.put(String.class, String.class.getMethod("toString"));
        keySources.put(Thread.class, Thread.class.getMethod("getName"));
    } catch (NoSuchMethodException | SecurityException e) {
        throw new RuntimeException("Error at 'getMethod': " + e.getMessage(), e);
    }

    System.out.println(getKey("test", keySources));
    System.out.println(getKey(new Thread("name"), keySources));
}

所需的解决方案如下:

public static String getKey(Object object, Map<Class<?>, Function<Object, String>> keySources) {
    Function<Object, String> source = keySources.get(object.getClass());

    if (source == null) {
        return null;
    }

    return source.apply(object);
}

public static void main(String[] args) {
    Map<Class<?>, Function<Object, String>> keySources = new HashMap<>();

    keySources.put(String.class, String::toString);
    keySources.put(Thread.class, Thread::getName);

    System.out.println(getKey("test", keySources));
    System.out.println(getKey(new Thread("name"), keySources));
}

但是String::toString 给出编译错误:The type String does not define toString(Object) that is applicable here

约束:我们不能修改类,因为它们是生成的。

【问题讨论】:

    标签: java reflection lambda


    【解决方案1】:

    我设法让您的代码通过编译并运行。我不确定为什么它适用于 lambda 表达式,但不适用于方法引用。

    也许有更好的方法来做到这一点。

    public static <T> String getKey(T object, Map<Class<?>, Function<? extends Object, String>> keySources) 
    {
        Function<T, String> source = (Function<T, String>) keySources.get(object.getClass());
    
        if (source == null) {
            return null;
        }
    
        return source.apply(object);
    }
    
    public static void main (java.lang.String[] args) throws Exception 
    {
        Map<Class<?>, Function<? extends Object, String>> keySources = new HashMap<>();
    
        keySources.put(String.class, s -> s.toString());
        keySources.put(Thread.class, (Thread t) -> t.getName());
    
        System.out.println(getKey("test", keySources));
        System.out.println(getKey(new Thread("name"), keySources));
    }
    

    【讨论】:

      【解决方案2】:

      Function&lt;Object, String&gt; 是一个接受Object 的函数,换句话说,任意对象 作为参数,所以像String::toString 这样的函数,要求它的参数是String 实例可以不履行合同。这很容易解决,因为您可以使用 Object::toString 代替,但是,对于要求参数为 Thread 实例的 Thread::getName,没有这样的替换。

      由于您通过映射键确保参数的类型正确,您可以通过将每个特定函数转换为执行类型转换的 Function&lt;Object,String&gt; 来解决此问题:

      public static <T,R> void put(Class<T> cl,
                                   Function<T,R> f, Map<Class<?>,Function<Object,R>> map) {
          map.put(cl, obj -> f.apply(cl.cast(obj)));
      }
      public static String getKey(Object object,
                                  Map<Class<?>, Function<Object, String>> keySources) {
          return keySources.getOrDefault(object.getClass(), x -> null).apply(object);
      }
      public static void main(String[] args) {
          Map<Class<?>, Function<Object, String>> keySources = new HashMap<>();
      
          put(String.class, String::toString, keySources);
          // or put(String.class, Function.identity(), keySources);
          put(Thread.class, Thread::getName, keySources);
      
          System.out.println(getKey("test", keySources));
          System.out.println(getKey(new Thread("name"), keySources));
      }
      

      【讨论】:

        【解决方案3】:

        Function带一个参数,与签名不匹配。

        尝试使用Callable,它不带参数并返回一个值

        【讨论】:

        • 有一个参数:获取key的对象
        • 那是“this”,不是参数。
        猜你喜欢
        • 1970-01-01
        • 2023-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-19
        • 2011-11-25
        相关资源
        最近更新 更多