从JAVA 8中的类对象获取方法引用

时间:2017-02-05 16:35:00

标签: java reflection java-8 method-reference

我的工厂类未知的类名是通过运行时输入获得的。

目前我有解决方案使用反射在我的工厂类中创建这些类的实例,但我想看看是否可以将这些类的方法引用new传递给我的工厂类,以便我不需要使用反射。 e.g:

public interface MyFactory {
    MyClass getInstance(String s);
}
public static MyFactory getRuntimeClass(Class<? extends MyClass> cls) {
    return cls::new;
}
public static void main(String[] args) {
    MyClass mySubClass = getRuntimeClass(Class.forName(args[0]).asSubClass(MyClass.class)).getInstance("test");
}

上述方法无效,因为无法从类对象new获取方法引用cls

如何使用方法引用修复上述内容,而不必恢复使用反射?

P.S。是的我有代码来检查输入,所有子类都有一个构造函数,它接受一个String作为输入。

1 个答案:

答案 0 :(得分:1)

好吧,使用在运行时指定的类型,即在编译时未知,是Reflection的定义。没有反射,你不能做反思操作。

你可以做的是生成一个功能接口的实例,它可以在不使用反射的情况下实例化类,即在调用接口方法之前解决所有问题,就像在编译时已知的成员的方法引用一样在运行时:

public static MyFactory getRuntimeClass(Class<? extends MyClass> cls) {
    try {
        MethodHandles.Lookup l = MethodHandles.lookup();
        MethodHandle c = l.findConstructor(cls,
                             MethodType.methodType(void.class, String.class));
        MethodType ft = c.type().changeReturnType(MyClass.class);
        return (MyFactory)LambdaMetafactory.metafactory(l, "getInstance",
            MethodType.methodType(MyFactory.class), ft, c, ft).getTarget().invokeExact();
    }
    catch(RuntimeException | Error t) {
        throw t;
    }
    catch(Throwable t) {
        throw new IllegalArgumentException(t);
    }
}

当然,生成实例是一种具有所有缺点的反射操作,例如仅在运行时检测错误。由于没有编译时检查的安全性,如果您尝试使用通用功能接口,则不存在任何通用类型安全性。

如果您更改了界面并忘记调整此代码,则可能会发生这样的情况,即您在执行此getRuntimeClass方法时甚至没有发现错误,但仅当您实际尝试调用接口方法时生成的实例。

与真正的方法引用不同,这不承担任何缓存。没关系,如果你只在初始化时将一些类名映射到MyFactory个实例并保留MyFactory个实例。但是如果你发现自己不得不重复地将类名映射到MyFactory个实例,也许使用相同的名称,你可能想要记住它们。最简单的解决方案是:

static ClassValue<MyFactory> MY_FACTORIES = new ClassValue<MyFactory>() {
    protected MyFactory computeValue(Class<?> type) {
        return getRuntimeClass(type.asSubclass(MyClass.class));
    }
};

可以像

一样使用
MyClass mySubClass = MY_FACTORIES.get(Class.forName(args[0])).getInstance("test");