简体   繁体   English

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

[英]Get method reference from class object in JAVA 8

I have class names unknown to my factory class that I obtained via runtime input. 我有通过运行时输入获得的工厂类未知的类名。

Currently I have solution to use reflection to create instances of these classes in my factory class, but I want to see if it is possible to pass the method reference new of these classes to my factory class so that I don't need to use reflection. 当前,我有使用反射在我的工厂类中创建这些类的实例的解决方案,但是我想看看是否可以将这些类的new方法引用传递给我的工厂类,这样我就不需要使用反射。 eg: 例如:

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

The above does not work because the method reference new is not obtainable from the Class object cls . 上面的方法不起作用,因为无法从Class对象cls获得引用new的方法。

How do you fix the above with method reference, without reverting back to use reflection? 如何使用方法参考修复以上内容,而又不返回使用反射?

PS Yes I have code to check the input, all the subclasses all have a constructor that takes a String as input. PS是的,我有检查输入的代码,所有子类都有一个以String作为输入的构造函数。

Well, working with types specified at runtime, ie unknown at compile-time, is the definition of Reflection. 好吧,使用在运行时指定的类型(即在编译时未知的类型)是Reflection的定义。 You can't do reflective operations without Reflection. 没有反射,您将无法进行反射操作。

What you can do, is to generate an instance of your functional interface which can instantiate the class without using reflection, ie having everything resolved prior to calling the interface method, much like the method reference to a member known at compile time works at runtime: 您可以做的是生成功能接口的实例,该实例可以在不使用反射的情况下实例化该类,即在调用接口方法之前先解决所有问题,就像在编译时对成员的方法引用在运行时一样:

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

Of course, generating the instance is a reflective operation with all of its disadvantages, like detecting errors at runtime only. 当然,生成实例是一种反射性操作,具有所有缺点,例如仅在运行时检测错误。 Since there is no safety of compile-time checks, there wouldn't be any generic type safety, if you try this with a generic functional interface. 由于没有编译时检查的安全性,因此,如果您使用通用功能接口进行尝试,就不会有任何通用类型安全性。

If you change the interface and forget to adapt this code, it might happen, that you even don't spot the mistake when executing this getRuntimeClass method, but only when you actually try to invoke the interface method on the generated instance. 如果更改接口而忘记修改此代码,则可能会发生,甚至在执行此getRuntimeClass方法时都不会发现错误,而只有在您实际尝试在生成的实例上调用接口方法时才发现错误。

Unlike real method references, this doesn't bear any caching. 与真实的方法引用不同,它不进行任何缓存。 That's ok, if you only map some class names to MyFactory instances at initialization time and keep the MyFactory instances. 没关系,如果仅在初始化时将一些类名映射到MyFactory实例并保留MyFactory实例。 But if you find yourself having to map class names to MyFactory instances repeatedly, perhaps with the same names, you might want to remember them. 但是,如果您发现自己不得不将类名重复地映射到MyFactory实例,也许具有相同的名称,那么您可能想记住它们。 The easiest solution is: 最简单的解决方案是:

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

which can be used like 可以像

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM