Java Class.forName 无法编译。获得"cannot find symbol symbol : method"



我正在尝试使用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的循环来包围上面的代码。

我希望这能有所帮助!

最新更新