有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

反射从Java8中的类对象获取方法引用

我的工厂类不知道我通过运行时输入获得的类名

目前我有一个解决方案,可以使用反射在我的工厂类中创建这些类的实例,但是我想看看是否可以将这些类的方法引用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");
}

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

如何使用方法引用修复上述问题,而不恢复使用反射

是的,我有代码来检查输入,所有的子类都有一个以字符串作为输入的构造函数


共 (1) 个答案

  1. # 1 楼答案

    嗯,使用运行时指定的类型(即编译时未知的类型)是反射的定义。如果没有反射,则无法执行反射操作

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

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