Java 从字符串加载类

Java 从字符串加载类,java,reflection,Java,Reflection,我想用字符串的值实例化一个类。我找到了一些教程,其中展示了几种方法。该类必须从某个接口继承,ImplementMe,该接口有一个名为runMe()的特殊方法。下面是我试过的: ImplmentMe a = (ImplementMe) ImplementMe.class .getClassLoader() .loadClass("my.package.IImplementedYou")

我想用字符串的值实例化一个类。我找到了一些教程,其中展示了几种方法。该类必须从某个接口继承,
ImplementMe
,该接口有一个名为
runMe()
的特殊方法。下面是我试过的:

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

它是有效的,但它太难看了。至少我认为不需要石膏。请告诉我有更好的方法。

试试
Class.forName(“my.package.IImplementedYou”)
从本质上讲,无论您是否使用第三方工具包,都会发生这种情况强制转换对象本质上是强制性的,除非需要
对象
。但是,您可以为自己制定一个程序:

public <T> T instantiateObject(String name, Class<T> cls) throws Exception {
    return (T) Class.forName(name).newInstance();
}
但是如果您走到这一步,
字符串名称实际上是多余的(给定AClass是一个具体的类)。你不妨:

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

你可以把它缩短一点,就像

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

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

不,没有更好的方法(按设计)。您不应该这样做,Java是作为类型安全语言设计的。但是,我可以理解您有时需要这样做,为此,您可以创建如下库函数:

public <T> T instantiate(final String className, final Class<T> type){
    try{
        return type.cast(Class.forName(className).newInstance());
    } catch(InstantiationException
          | IllegalAccessException
          | ClassNotFoundException e){
        throw new IllegalStateException(e);
    }
}
if(newInstance instanceof my.package.IImplementedYou) {  
    ((ImplementMe)newInstance).runMe(); 
}

另一种选择是使用
forName
,但它不会比您目前拥有的更好:

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

实际上,
forName
将在后台使用
getClassLoader().loadClass()
加载类,正如您在
class.java

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

您所拥有的可以工作,但您不必使用加载ImplementMe的同一个类加载器加载该类。这应该同样有效:

Object newInstance = this.getClass().getClassLoader().loadClass("my.package.IImplementedYou").newInstance();
重要的是,类加载器既知道实现了“my.package.IImplementedYou”的类文件,也知道实现了“ImplementMe”的类

您可以明确检查IImplementedYou是否真的实现了ImplementMe,如下所示:

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

我会这样做:

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

有一些例外情况需要抓住,但我认为没关系。:)

不管怎样,你都需要演员。编译器无法自动将
对象
升级到
实现我
。这太奇怪了。我认为通过使用
ImplementMe.class.getClassLoader()
,它会得到这样一个提示:类必须从
ImplementMe
继承。哦,好吧。
class
有一个
asSubclass
方法。因此,
…loadClass(…).asSubclass(ImplementMe.class).newInstance()将返回一个
ImplementMe
实例(如果成功)。您可能会注意到他仍然需要强制转换。您不需要取消选中强制转换。只需使用
返回type.cast(Class.forName(className).newInstance())。优点是它可以立即抛出(失败速度更快)。
(T)Class.forName(cls.getCanonicalName()).newInstance()为什么这么复杂?怎么样
cls.newInstance()?但是在我的解释中,接口类是加载的,而不是实现类true,
cls.newInstance()
会更短。使用第二种方法,它必须是一个具体的类。
ImplementMe noCastNeeded = 
    this.getClassLoader()
        .loadClass("my.package.IImplementedYou")
        .asSubclass(ImplementMe.class).newInstance();
(MyInterface)Class.forName(className).newInstance()