如何用 Java 1.8 函数替换选择方法访问



>问题:我们需要为不同类的对象获取一个(字符串(键。对于可伸缩性,我们希望配置用于获取密钥字符串的方法 - 而不是实现许多 if-else withingOf...

朴素解决方案(带有示例数据(是:

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

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

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

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

Function采用一个与签名不匹配的参数。

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

最新更新