从字符串加载类

时间:2011-02-01 16:27:30

标签: java reflection

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

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

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

9 个答案:

答案 0 :(得分:34)

不,没有更好的方法(按设计)。你不应该这样做,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);
    }
}

现在您的客户端代码至少可以调用此方法而不进行强制转换:

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

答案 1 :(得分:11)

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

答案 2 :(得分:6)

我将如何做到这一点:

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

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

答案 3 :(得分:2)

无论您是否使用第三方工具包,都会发生这种情况。 除非期待Object,否则强制转换对象本身就是强制性的。但是,您可以为您做一个例行程序:

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

您可以使用:

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

但是如果你走到这一步,String name实际上是多余的(假设AClass是一个具体的类)。你可能也是这样:

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

您可以使用:

AClass cls = instantiateObject(AClass.class);

答案 4 :(得分:1)

你可以缩短它有点像

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

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

答案 5 :(得分:1)

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

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

事实上,forName将在幕后使用getClassLoader().loadClass()来加载课程,您可以在Class.java source code 中看到。

答案 6 :(得分:0)

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

答案 7 :(得分:0)

您所拥有的可能有用,但您不必使用加载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(); 
}

答案 8 :(得分:-1)

(MyInterface)Class.forName(className).newInstance()
相关问题