Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Class.forName won't compile. Getting "cannot find symbol symbol : method"

Tags:

java

class

I'm trying to use Class.forName and my Intellij is throwing a compile error. My IntelliJ highlights "theResponse" in red (in testMethod) and gives me this error:

cannot find symbol symbol : method

Here is the code (and test) I'm working with...

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());
  }
}

UPDATE: What I was trying to do was "polymorphically" call theResponse from the class that is returned as a String from a HashMap. How would I do this? I'm (loosely) following this example but I didn't understand it fully (http://sourcemaking.com/refactoring/replace-conditional-with-polymorphism). Here is a simplified version of what I'm trying to do. Hopefully that makes sense.

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();
  }

}
like image 531
Kelly Avatar asked Dec 20 '22 01:12

Kelly


2 Answers

Class.forName() returns an object of type java.lang.Class. java.lang.Class has no method theResponse, as you can see from its Javadoc.

It sounds like what you actually want to do is construct an instance of the PublicRoute class, and call the method on the instance. But you've already constructed such an instance: it's the publicRoute variable you create in your constructor. Why not just use that object instead?


Edit: Ah, I see what you're trying to do. You basically want a form of the Service Locator pattern.

Create an interface, like so:

public interface ResponseProvider {
  String theResponse();
}

Then make all your classes implement that interface:

public class PublicRoute implements ResponseProvider {
  @Override
  public String theResponse() {
    // do whatever
  }
}

Then, when you load your Class<?>, you can use the asSubclass() method to turn your Class<?> into a Class<? extends ResponseProvider> -- then newInstance() will give you back a ResponseProvider object that you can call theResponse() on, like so:

String className = ...;
Class<?> klass = Class.forName(className);
Class<? extends ResponseProvider> responseProviderClass
    = klass.asSubclass(ResponseProvider.class);
ResponseProvider responseProvider = responseProviderClass.newInstance();
return responseProvider.theResponse();

But don't do that by hand -- instead, use the java.util.ServiceLoader class, which is designed for exactly this purpose. You create a special META-INF/services/com.my.package.ResponseProvider file, with a list of all the possible classes that implement that interface, and then ServiceLoader can give you back instances of each of them.

But... consider not doing that, either. The types of problems that you can solve with the Service Locator pattern are often better solved by using Dependency Injection (see also my answer to another question about Dependency Injection). The Guice DI framework, for example, offers a feature called multibindings which looks like exactly what you need.

like image 101
Daniel Pryden Avatar answered May 18 '23 00:05

Daniel Pryden


If theResponse() belongs to http.response.PublicRoute then it should have been

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

But, then there's really no need for Class.forName() as you could use constructor as

return new PublicRoute().theResponse("hi");
like image 22
Ravi K Thapliyal Avatar answered May 18 '23 01:05

Ravi K Thapliyal