Unexpected Class.getMethod behaviour - java

A while ago I had a similar question when using Class.getMethod and autoboxing, and it made sense to implement this in your own lookup algorithm. But what really confused me a little was that the following is not working either:
public class TestClass
{
public String doSomething(Serializable s)
{
return s.toString();
}
public static void main(String[] args) throws SecurityException, NoSuchMethodException
{
TestClass tc = new TestClass();
Method m = tc.getClass().getMethod("doSomething", String.class);
}
}
String.class implements the Serializable interface and I really expected it to be included in the lookup method. Do I have to consider this in my own lookup algorithms as well?
EDIT: I did read the Javadoc, so let me emphasise the second part of the question: And if so do you have suggestions on how to do that fast (I already had to add some custom matching and converting algorithms and I don't want it to get too slow)?

As per your edit, you can make use of Class#isAssignableFrom(). Here's a basic kickoff example (leaving obvious (runtime) exception handling aside):
package com.stackoverflow.q2169497;
import java.io.Serializable;
import java.lang.reflect.Method;
public class Test {
public String doSomething(Serializable serializable) {
return serializable.toString();
}
public static void main(String[] args) throws Exception {
Test test = new Test();
for (Method method : test.getClass().getMethods()) {
if ("doSomething".equals(method.getName())) {
if (method.getParameterTypes()[0].isAssignableFrom(String.class)) {
System.out.println(method.invoke(test, "foo"));
}
}
}
}
}
This should print foo to stdout.

The javadoc for Class.getMethod is very explicit:
The parameterTypes parameter is an
array of Class objects that identify
the method's formal parameter types,
in declared order.
It offers no scope for subtypes.

getMethod isn't meant to find methods which are compatible with the given parameter types - it's meant to find methods with exactly the given parameter types.
You'd need to call getMethods() to find all the methods, then filter by name and number of parameters, then work out which of those are actually applicable.

Why would you call getMethod with String.class? Method signatures are exactly mapped. It doesn't make any sense to look up a method by the same criteria as if you will call them.

Related

How to call method of a class with wildcard in Java?

I can't call method serve() below.
public class GenericService {
public static class Service<T> {
public void serve(T t) {
System.out.println(t.toString());
}
}
public static Service<?> service = new Service<String>();
public static void main(String[] args) {
service.serve("Hello World!"); // 'serve(capture<?>)' cannot be applied to '(java.lang.String)'
}
}
How to call this method by force?
Why does Java dislike such calls?
UPDATE
The problem is not ClassCastException as was proposed, because in that case I would be able to write
try {
service.serve("Hello World!"); // 'serve(capture<?>)' cannot be applied to '(java.lang.String)'
}
catch (ClassCastException e) {
System.err.println("You see!? This is why I was disliking your code!");
}
but I can't.
Why?
UPDATE 2
Now, when everybody said out, a new version:
public static Service<? extends String> service = new Service<String>();
public static void main(String[] args) {
service.serve("Hello World!"); // 'serve(capture<?>)' cannot be applied to '(java.lang.String)'
((Service<String>)service).serve("Hello World!"); // Unchecked cast: 'GenericService.Service<capture<? extends String>>' to 'GenericService.Service<String>'
}
what problem is here (don't regard that String is final)?
You misunderstand what the wildcard means (this is actually a common misunderstanding about generics wildcards in Java).
Service<?> does not mean: a Service that can accept any type.
It does mean: a Service of a specific, but unknown type.
You cannot call serve, passing it a String, because the type that the ? stands for is unknown - the compiler cannot check, just by looking at the type of the variable service, if the actual service that it refers to is a Service<String>, a Service<Integer> or a Service<Whatever>, so it can't know if it should be allowed to pass a String to the serve method.
To keep it type-safe, the compiler has no other option than to not allow you to call the method.
How to call this method by force?
You can force it by casting:
((Service<String>) service).serve("Hello World!");
(But keep in mind that casting means you are giving up on type-safety, in general you should avoid casting as much as possible).
Wildcards in generics has not for aim to allow the usage of any type. It is a way to identify an unknown and invariable type.
In your code, ? is of type String but the compiler can't guess it. You could change it later with a Service<Integer> and have ClassCastException at runtime by forcing String objects in it.
There is no way to cast an object to a captured type (= wildcard).
If you want to accept every object that declare the toString() method then you should use Service<Object> instead of Service<?>:
public static Service<Object> service = new Service<Object>();

Expression that behaves differently inside a static method

I'm trying to write an expression or series of statements of Java source code that when written inside a static method evaluates to null, but if the method is non-static evaluates to this.
My initial idea was to 'overload' on static vs non-static, as below:
public class test {
public void method1() {
System.out.println(getThisOrNull());
}
public static void method2() {
System.out.println(getThisOrNull());
}
private static Object getThisOrNull() {
return null;
}
private Object getThisOrNull() {
return this;
}
public static void main(String[] args) {
test t = new test();
System.out.println(t);
t.method1();
t.method2();
}
}
Unfortunately this isn't actually legal Java, you can't 'overload' like that and it just gives a compiler error:
test.java:14: error: method getThisOrNull() is already defined in class test
private Object getThisOrNull() {
^
1 error
Clearly in an ideal world I wouldn't write it like that to begin with, but the problem is this code will be generated automatically by a tool that is not really semantically or syntactically enough to distinguish between the static vs non-static case.
So, how can I write some source code that, although byte for byte identical compiles and behaves differently in depending on the presence of the static modifier for the method?
This can be achieved with a trick and a bit of help from Java's reflection facilities. It's ugly, but it works:
import java.lang.reflect.Field;
public class test {
public void method1() {
System.out.println(getThisOrNull(new Object(){}));
}
public static void method2() {
System.out.println(getThisOrNull(new Object(){}));
}
private static Object getThisOrNull(final Object o) {
for (Field f: o.getClass().getDeclaredFields()) {
if (f.getType().equals(test.class)) {
try {
return f.get(o);
}
catch (IllegalAccessException e) {
// Omm nom nom...
}
}
}
return null;
}
public static void main(String[] args) {
test t = new test();
System.out.println(t);
t.method1();
t.method2();
}
}
This compiles and runs as hoped for:
test#183f74d
test#183f74d
null
The trick that makes this possible is the use of new Object(){}, which creates a new, anonymous class within the existing method that we're trying to figure out if it's static or not. The behaviour of this is subtly different between the two cases.
If the goal were just to figure out if the method is static or not we could write:
java.lang.reflect.Modifiers.isStatic(new Object(){}.getClass().getEnclosingMethod().getModifiers())
Since we want to get this (when available) we need to do something slightly different. Fortunately for us classes defined within the context of an instance of an object in Java get an implicit reference to the class that contains them. (Normally you'd access it with test.this syntax). We needed a way to access test.this if it existed, except we can't actually write test.this anywhere because it too would be syntactically invalid in the static case. It does however exist within the object, as a private member variable. This means that we can find it with reflection, which is what the getThisOrNull static method does with the local anonymous type.
The downside is that we create an anonymous class in every method we use this trick and it probably adds overheads, but if you're backed into a corner and looking for a way of doing this it does at least work.

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

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();
}
}
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.
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");
The class Class does not have a method named theResponse. From the rest of your code, it doesn't look like you should be using reflection here; you're already referring statically to the PublicRoute class, so there's no point loading it dynamically.
I think you just want to write either this:
return PublicRoute.theResponse("hi");
or this:
return new PublicRoute().theResponse("hi");
(depending whether theResponse is a static method or an instance method).
Let me see if I understand what you're trying to do. You've got a hashmap that will contain a list of classes that you're going to try to call the theResponse(String response) method on, right? I'm assuming you won't know the String that will be put into the hashmap either, right?
Others are right in that you can't just do:
Class c = Class.forName("http.response.PublicRoute");
c.theResponse("hi"); // errors because c has no knowledge of theResponse()
You'll need to cast c to http.response.PublicRoute but then as #Ravi Thapliyal pointed out, you won't need Class.forName anymore! You've got a hashmap of names that could potentially be anything so this won't work.
If I'm understanding you correctly to do what you need, you'll need to use reflection in order to attempt to instance the class then call the method on it.
Here's how you'd do it assuming the theResponse method is a public non-static method and has only 1 parameter.
// 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"));
Of course you'll need to handle Exceptions but you'd surround the above code with the loop for your hashmap.
I hope this helps!

Dynamically call method using reflection

I want to write a program which decides which methods to call on an object at runtime.
For example
<method>getXyz<operation>
<arg type="int"> 1 <arg>
<arg type="float"> 1/0 <arg>
Now I have something like above in XML files and I want to decide which method to call at runtime. There can be multiple methods.
I don't want to do something like the following in my code:
if (methodNam.equals("getXyz"))
//call obj.getXyz()
How can I do it using Java reflection?
Also I want to construct the parameter list at runtime. For example, one method can take
2 parameters and another can take n arguments.
You should use Object.getClass() method to get the Class object first.
Then you should use Class.getMethod() and Class.getDeclaredMethod() to get the Method, and finally use Method.invoke() to invoke this method.
Example:
public class Tryout {
public void foo(Integer x) {
System.out.println("foo " + x);
}
public static void main(String[] args) throws Exception {
Tryout myObject = new Tryout();
Class<?> cl = myObject.getClass();
Method m = cl.getMethod("foo", Integer.class);
m.invoke(myObject, 5);
}
}
Also i want to construct the parameter list at runtime.For Example one
method can take 2 parameters and other can take n args
This is not an issue, just create arrays of Class<?> for the types of the arguments, and an array of Objects of the values of the arguments, and pass them to getMethod() and invoke(). It works because these methods accept Class<?>... as argument, and an array fits it.
You can use the following code to a class method using reflection
package reflectionpackage;
public class My {
public My() {
}
public void myReflectionMethod(){
System.out.println("My Reflection Method called");
}
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
Class c=Class.forName("reflectionpackage.My");
Method m=c.getDeclaredMethod("myReflectionMethod");
Object t = c.newInstance();
Object o= m.invoke(t);
}
}
this will work and for further reference please follow the link
http://compilr.org/java/call-class-method-using-reflection/
Have a good look at java.beans.Statement and java.beans.Expression. See here for further details.

Detecting whether a method/function exists in Java

Is there a method/function in Java that checks if another method/function is available just like function_exists(functionName) in PHP?
Here I am referring to a method/function of static class.
You can find out if a method exists in Java using reflection.
Get the Class object of the class you're interested in and call getMethod() with the method name and parameter types on it.
If the method doesn't exist, it will throw a NoSuchMethodException.
Also, please note that "functions" are called methods in Java.
Last but not least: keep in mind that if you think you need this, then chances are that you've got a design problem at hand. Reflection (which is what the methods to inspect the actual Java classes is called) is a rather specialized feature of Java and should not generally be used in business code (although it's used quite heavily and to some nice effects in some common libraries).
I suspect you're looking for Class.getDeclaredMethods and Class.getMethods which will give you the methods of a class. You can then test whether the one you're looking for exists or not, and what it's parameters are etc.
You can use Reflections to lookup if the method exists:
public class Test {
public static void main(String[] args) throws NoSuchMethodException {
Class clazz = Test.class;
for (Method method : clazz.getDeclaredMethods()) {
if (method.getName().equals("fooBar")) {
System.out.println("Method fooBar exists.");
}
}
if (clazz.getDeclaredMethod("fooBar", null) != null) {
System.out.println("Method fooBar exists.");
}
}
private static void fooBar() {
}
}
But Reflection is not really fast so be careful when to use it (probably cache it).
Try using the Class.getMethod() method of the Class class =)
public class Foo {
public static String foo(Integer x) {
// ...
}
public static void main(String args[]) throws Exception {
Method fooMethod = Foo.class.getMethod("foo", Integer.class);
System.out.println(fooMethod);
}
}
Here my solution using reflection...
public static boolean methodExists(Class clazz, String methodName) {
boolean result = false;
for (Method method : clazz.getDeclaredMethods()) {
if (method.getName().equals(methodName)) {
result = true;
break;
}
}
return result;
}
You can use the reflection API to achieve this.
YourStaticClass.getClass().getMethods();
You can do this like this
Obj.getClass().getDeclaredMethod(MethodName, parameterTypes)

Categories

Resources