从字符串加载类
我想通过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(); }