4

我正在尝试使用 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());
  }
}

更新: 我试图做的是“多态地”从类中调用 theResponse,该类作为字符串从 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();
  }

}
4

4 回答 4

6

Class.forName()返回一个类型的对象java.lang.Classjava.lang.Class没有方法,从它的 JavadoctheResponse可以看出。

听起来您实际上想要做的是构造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可以为您返回每个类的实例。

但是……考虑不要那样做。您可以使用服务定位器模式解决的问题类型通常可以通过使用依赖注入更好地解决(另请参阅我对另一个关于依赖注入的问题的回答)。例如,Guice DI 框架提供了一个称为多重绑定的功能,它看起来正是您所需要的。

于 2013-08-10T22:40:02.597 回答
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");
于 2013-08-10T22:39:14.743 回答
2

该类Class没有名为 的方法theResponse。从您的其余代码来看,您似乎不应该在这里使用反射;您已经静态地引用了PublicRoute该类,因此没有必要动态加载它。

我想你只想写这个:

return PublicRoute.theResponse("hi");

或这个:

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

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

于 2013-08-10T22:40:30.257 回答
1

让我看看我是否明白你想要做什么。你有一个哈希表,其中包含一个类列表,你将尝试调用该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"));

当然,您需要处理异常,但您需要在上面的代码中加上哈希图的循环。

我希望这有帮助!

于 2013-08-11T04:14:26.180 回答