Java Class.forName将无法编译。得到“找不到符号符号:方法”

时间:2013-08-10 22:34:53

标签: java class

我正在尝试使用Class.forName,而我的Intellij正在抛出编译错误。我的IntelliJ以红色突出显示“theResponse”(在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());
  }
}

更新:我试图做的是“多态”调用来自HashMap中返回作为String 的类的响应。我该怎么做?我(松散地)遵循这个例子但我完全不了解它(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();
  }

}

4 个答案:

答案 0 :(得分:6)

Class.forName()返回java.lang.Class类型的对象。从its Javadoc可以看到java.lang.Class没有方法theResponse

听起来你真正想要做的是构造PublicRoute类的实例,并在实例上调用该方法。但是你已经构建了这样一个实例:它是你在构造函数中创建的publicRoute变量。为什么不直接使用该对象呢?


编辑:啊,我知道你要做的是什么。你基本上想要一种Service Locator模式的形式。

创建一个界面,如下所示:

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可以为您提供每个类的实例。

但是......考虑不要这样做。使用Dependency Injection可以更好地解决使用服务定位器模式可以解决的问题类型(另请参阅my answer to another question about Dependency Injection)。例如,Guice DI框架提供了一个名为multibindings的功能,它看起来就像您需要的那样。

答案 1 :(得分:2)

如果theResponse()属于http.response.PublicRoute,那么它应该是

Class c = Class.forName("http.response.PublicRoute");
return ((PublicRoute) c.newInstance()).theResponse("hi");

但是,因为你可以使用构造函数作为

,所以真的不需要Class.forName()
return new PublicRoute().theResponse("hi");

答案 2 :(得分:2)

班级Class没有名为theResponse的方法。从其他代码中看,你看起来不应该使用反射;你已经静态地引用了PublicRoute类,所以没有必要动态加载它。

我想你只想写下这个:

return PublicRoute.theResponse("hi");

或者这个:

return new PublicRoute().theResponse("hi");

(取决于theResponse是静态方法还是实例方法)。

答案 3 :(得分:1)

让我看看我是否理解你要做的事情。你有一个hashmap,它将包含你要尝试调用theResponse(String response)方法的类列表,对吧?我假设你不会知道将被放入hashmap的String,对吧?

其他人是正确的,你不能这样做:

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方法是一个公共非静态方法并且只有一个参数,这就是你的方法。

// 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"));

当然,您需要处理异常,但是您将使用hashmap的循环包围上述代码。

我希望这有帮助!