从 JAVA 中的 class 对象获取方法引用 8
Get method reference from class object in JAVA 8
我的工厂 class 不知道我有 class 个名称,这些名称是我通过运行时输入获得的。
目前我有使用反射在我的工厂 class 中创建这些 class 实例的解决方案 class,但我想看看是否可以传递方法引用 new
这些 classes 到我的工厂 class 这样我就不需要使用反射了。例如:
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");
}
上面的方法不起作用,因为无法从 Class 对象 cls
.
中获取方法引用 new
如何使用方法参考解决上述问题,而不返回使用反射?
P.S。是的,我有检查输入的代码,所有子 classes 都有一个将字符串作为输入的构造函数。
好吧,使用在运行时指定的类型,即 compile-time 中未知的类型,就是反射的定义。没有反射就不能做反射操作。
你可以做的是生成一个功能接口的实例,它可以在不使用反射的情况下实例化 class,即在调用接口方法之前解决所有问题,就像对 a 的方法引用一样编译时已知的成员在运行时有效:
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);
}
}
当然,生成实例是一种反射操作,具有所有缺点,例如仅在运行时检测错误。由于没有 compile-time 检查的安全性,因此如果您使用通用功能接口尝试此操作,就不会有任何通用类型安全性。
如果你改变了接口而忘了适配这段代码,那么你甚至在执行这个getRuntimeClass
方法时都没有发现错误,而只有在你真正尝试调用接口时才会发现错误生成实例上的方法。
与真正的方法引用不同,这不承担任何缓存。没关系,如果你只在初始化时将一些 class 名称映射到 MyFactory
个实例并保留 MyFactory
个实例。但是,如果您发现自己必须反复将 class 个名称映射到 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");
我的工厂 class 不知道我有 class 个名称,这些名称是我通过运行时输入获得的。
目前我有使用反射在我的工厂 class 中创建这些 class 实例的解决方案 class,但我想看看是否可以传递方法引用 new
这些 classes 到我的工厂 class 这样我就不需要使用反射了。例如:
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");
}
上面的方法不起作用,因为无法从 Class 对象 cls
.
new
如何使用方法参考解决上述问题,而不返回使用反射?
P.S。是的,我有检查输入的代码,所有子 classes 都有一个将字符串作为输入的构造函数。
好吧,使用在运行时指定的类型,即 compile-time 中未知的类型,就是反射的定义。没有反射就不能做反射操作。
你可以做的是生成一个功能接口的实例,它可以在不使用反射的情况下实例化 class,即在调用接口方法之前解决所有问题,就像对 a 的方法引用一样编译时已知的成员在运行时有效:
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);
}
}
当然,生成实例是一种反射操作,具有所有缺点,例如仅在运行时检测错误。由于没有 compile-time 检查的安全性,因此如果您使用通用功能接口尝试此操作,就不会有任何通用类型安全性。
如果你改变了接口而忘了适配这段代码,那么你甚至在执行这个getRuntimeClass
方法时都没有发现错误,而只有在你真正尝试调用接口时才会发现错误生成实例上的方法。
与真正的方法引用不同,这不承担任何缓存。没关系,如果你只在初始化时将一些 class 名称映射到 MyFactory
个实例并保留 MyFactory
个实例。但是,如果您发现自己必须反复将 class 个名称映射到 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");