那麼,使用在運行時指定的類型,即在編譯時未知的是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");
使用我想你誤解了該方法的參考是什麼。只需使用你的標準反射,'Class#newInstnace'。 –
'cls :: new'試圖獲得對'Class'構造函數的引用。它失敗是因爲'Class'沒有接受單個'String'參數的公共構造函數。沒有辦法用方法引用來做你想要的。我建議只要跟@SotiriosDelimanolis建議。 –
@Sotirios Delimanolis:'Class.newInstance'只支持默認的構造函數。 – Holger