如何用Java 1.8函数替换relective方法访问?

时间:2017-11-15 10:52:15

标签: java reflection lambda

问题:我们需要获取不同类对象的(String)键。 为了实现可扩展性,我们希望将Method配置为用于获取键字符串 - 而不是使用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

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

3 个答案:

答案 0 :(得分:0)

我设法让你的代码通过编译并运行。我不确定为什么它适用于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));
}

答案 1 :(得分:0)

Function<Object, String>是一个接受Object的函数,换句话说任意对象作为参数,所以像String::toString这样的函数需要它的参数是String个实例无法履行合同。这很容易修复,因为您可以使用Object::toString来代替Thread::getName,这需要参数为Thread个实例,但没有这样的替换。

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

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));
}

答案 2 :(得分:-1)

Function选择一个与签名不匹配的参数。

尝试使用不带参数的Callable并返回值