2017-02-05 67 views
-1

我有我的工廠類未知的類名,我通過運行時輸入獲得。從JAVA中的類對象獲取方法引用8

目前我有解決方案使用反射在我的工廠類中創建這些類的實例,但我想看看是否有可能將這些類的方法參考new傳遞給我的工廠類,以便我不需要使用反射。例如爲:

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.是的,我有代碼來檢查輸入,所有的子類都有一個構造函數,它將一個字符串作爲輸入。

+0

使用我想你誤解了該方法的參考是什麼。只需使用你的標準反射,'Class#newInstnace'。 –

+0

'cls :: new'試圖獲得對'Class'構造函數的引用。它失敗是因爲'Class'沒有接受單個'String'參數的公共構造函數。沒有辦法用方法引用來做你想要的。我建議只要跟@SotiriosDelimanolis建議。 –

+0

@Sotirios Delimanolis:'Class.newInstance'只支持默認的構造函數。 – Holger

回答

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