从字符串加载类

我想通过String的值来实例化一个类。 我找到了几个教程,展示了这样做的几种方法。 该类必须从某个接口inheritance, ImplementMe有一个名为runMe()的特殊方法。 所以这就是我的尝试:

 ImplmentMe a = (ImplementMe) ImplementMe.class .getClassLoader() .loadClass("my.package.IImplementedYou") .newInstance(); a.runMe(); 

它有效,但它太丑了。 我至少预计不需要演员。 请告诉我有更好的方法。

不,没有更好的方法(按设计)。 你不应该这样做,Java被设计为一种类型安全的语言。 但是,我可以理解你有时需要做这样的事情,为此你可以创建一个像这样的库函数:

 public  T instantiate(final String className, final Class type){ try{ return type.cast(Class.forName(className).newInstance()); } catch(InstantiationException | IllegalAccessException | ClassNotFoundException e){ throw new IllegalStateException(e); } } 

现在,您的客户端代码至少可以调用此方法而不需要转换:

 MyInterface thingy = instantiate("com.foo.bar.MyInterfaceImpl", MyInterface.class); 

尝试Class.forName("my.package.IImplementedYou")

这是我将如何做到这一点:

 ImplementMe noCastNeeded = this.getClassLoader() .loadClass("my.package.IImplementedYou") .asSubclass(ImplementMe.class).newInstance(); 

有一些例外可以捕获,但我认为这没关系。 🙂

无论你是否使用第三方工具包,所有本质上都是如此。 除非期望一个Object否则强制转换对象本身就是强制性的。 但是,您可以为您做一个例行程序:

 public  T instantiateObject(String name, Class cls) throws Exception { return (T) Class.forName(name).newInstance(); } 

您可以使用哪个:

 AClass cls = instantiateObject("com.class.AClass", AClass.class); 

但是如果你到目前为止, String name实际上是多余的(给定AClass是一个具体的类)。 你还不如:

 public  T instantiateObject(Class cls) throws Exception { return (T) Class.forName(cls.getCanonicalName()).newInstance(); } 

您可以使用哪个:

 AClass cls = instantiateObject(AClass.class); 

你可以稍微缩短它

 ImplementMe a = (ImplementMe) Class .forName("my.package.IImplementedYou") .newInstance(); 

但是你无法摆脱演员阵容。 可能有一种方法可以避免强制转换,但前提是您可以避免按名称加载类的子问题。

另一种方法是使用forName ,但它没有比你现在拥有的更好:

 ImplementMe a = (ImplementMe) Class.forName("my.package.IImplementedYou").newInstance(); a.runMe(); 

实际上, forName将在幕后使用getClassLoader().loadClass()来加载类,正如您在Class.java源代码中看到的Class.java

您将需要一个强制转换,因为编译器无法从代码中判断出该对象的类型为ImplementMe。 因此,它需要程序员发出一个强制转换,如果该对象不是一个ImplementMe实例,它将抛出一个ClassCastException。

你有什么可能工作,但你不必使用加载ImplementMe的相同类加载器加载类。 这应该同样有效:

 Object newInstance = this.getClass().getClassLoader().loadClass("my.package.IImplementedYou").newInstance(); 

重要的是,类加载器知道带有“my.package.IImplementedYou”实现的类文件和带有“ImplementMe”实现的类。

您可以明确检查IImplementedYou是否真的像这样实现ImplementMe:

 if(newInstance instanceof my.package.IImplementedYou) { ((ImplementMe)newInstance).runMe(); } 

在创建实例之前,您还可以检查IImlementedYou是否真的在实现接口:

 Class c = this.getClass().getClassLoader().loadClass("my.package.IImplementedYou"); if(ImplementMe.class.isAssignableFrom(c)) { Object newInstance = c.newInstance(); }