我正在尝试使用class.forName,而我的Intellij抛出了一个编译错误。我的IntelliJ以红色突出显示"响应"(在testMethod中),并给出以下错误:
cannot find symbol symbol : method
这是我正在使用的代码(和测试)。。。
package http.response;
public class TestClass {
public TestClass() {
PublicRoute publicRoute = new PublicRoute();
}
public String testMethod() throws ClassNotFoundException {
Class c = Class.forName("http.response.PublicRoute");
return c.theResponse("hi");
}
}
package http.response;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
public class TestClassTest {
@Test
public void test() throws ClassNotFoundException {
TestClass testClass = new TestClass();
assertEquals("public", testClass.testMethod());
}
}
更新:我试图做的是"多态地"调用类中的Response,该类从HashMap返回作为字符串。我该怎么做?我(粗略地)遵循这个例子,但我没有完全理解(http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism)。这是我尝试做的一个简化版本。希望这是有意义的。
package http.response;
import java.util.HashMap;
public class TestClass {
HashMap map;
public TestClass(HashMap map) {
this.map = map;
}
public String testMethod(String lookupValue) throws ClassNotFoundException {
String className = map.get(lookupValue);
Class c = Class.forName("http.response." + className);
return c.theResponse();
}
}
Class.forName()
返回类型为java.lang.Class
的对象。java.lang.Class
没有方法theResponse
,您可以从它的Javadoc中看到。
听起来您实际上想要做的是构造PublicRoute
类的实例,并调用该实例上的方法。但是您已经构建了这样一个实例:它是您在构造函数中创建的publicRoute
变量。为什么不直接使用那个对象呢?
编辑:啊,我明白你想做什么了。你基本上想要一种服务定位器模式。
创建一个接口,如下所示:
public interface ResponseProvider {
String theResponse();
}
然后让你所有的类实现这个接口:
public class PublicRoute implements ResponseProvider {
@Override
public String theResponse() {
// do whatever
}
}
然后,当您加载Class<?>
时,您可以使用asSubclass()
方法将Class<?>
转换为Class<? extends ResponseProvider>
——然后newInstance()
将返回一个ResponseProvider
对象,您可以在该对象上调用theResponse()
,如下所示:
String className = ...;
Class<?> klass = Class.forName(className);
Class<? extends ResponseProvider> responseProviderClass
= klass.asSubclass(ResponseProvider.class);
ResponseProvider responseProvider = responseProviderClass.newInstance();
return responseProvider.theResponse();
但不要手动执行,而是使用java.util.ServiceLoader
类,它正是为此目的而设计的。创建一个特殊的META-INF/services/com.my.package.ResponseProvider
文件,其中包含实现该接口的所有可能类的列表,然后ServiceLoader
可以为您返回每个类的实例。
但是。。。考虑一下也不要那样做。使用ServiceLocator模式可以解决的问题类型通常可以通过使用依赖注入来更好地解决(另请参阅我对另一个关于依赖注入的问题的回答)。例如,Guice DI框架提供了一个名为multibindings的功能,它看起来正是您所需要的。
如果theResponse()
属于http.response.PublicRoute
,那么它应该是
Class c = Class.forName("http.response.PublicRoute");
return ((PublicRoute) c.newInstance()).theResponse("hi");
但是,实际上不需要Class.forName()
,因为您可以使用构造函数作为
return new PublicRoute().theResponse("hi");
类Class
没有名为theResponse
的方法。从代码的其余部分来看,您不应该在这里使用反射;您已经静态地引用了PublicRoute
类,因此动态加载它没有意义。
我想你只想写这两个:
return PublicRoute.theResponse("hi");
或者这个:
return new PublicRoute().theResponse("hi");
(取决于theResponse
是静态方法还是实例方法)。
让我看看我是否理解你要做的事情。你有一个哈希图,其中包含一个类列表,你将尝试对其调用theResponse(String response)
方法,对吗?我想你也不会知道将被放入哈希图中的字符串,对吧?
其他人是对的,你不能只做:
Class c = Class.forName("http.response.PublicRoute");
c.theResponse("hi"); // errors because c has no knowledge of theResponse()
您需要将c转换为http.response.PublicRoute
,但正如@Ravi Thapliyal所指出的,您将不再需要Class.forName
!你有一个名字的哈希图,可能是任何东西,所以这不起作用。
如果我正确理解你需要做什么,你需要使用反射来尝试实例化类,然后调用它上的方法。
以下是假设theResponse
方法是一个公共的非静态方法并且只有1个参数时的操作方法。
// Declare the parameter type
Class[] paramString = new Class[1];
paramString[0] = String.class;
String className = map.get(lookupValue);
// Instance the class
Class cls = Class.forName("http.response." + className);
Object obj = cls.newInstance();
// Call the method and pass it the String parameter
method = cls.getDeclaredMethod("theResponse", paramString);
method.invoke(obj, new String("hi"));
当然,您需要处理Exceptions,但您需要用hashmap的循环来包围上面的代码。
我希望这能有所帮助!