Method Overloading ambiguity [duplicate] - java

The sample code is :
public class OverloadingTest {
public static void test(Object obj){
System.out.println("Object called");
}
public static void test(String obj){
System.out.println("String called");
}
public static void main(String[] args){
test(null);
System.out.println("10%2==0 is "+(10%2==0));
test((10%2==0)?null:new Object());
test((10%2==0)?null:null);
}
And the output is :
String called
10%2==0 is true
Object called
String called
The first call to test(null) invokes the method with String argument , which is understandable according to The Java Language Specification .
1) Can anyone explain me on what basis test() is invoked in preceding calls ?
2) Again when we put , say a if condition :
if(10%2==0){
test(null);
}
else
{
test(new Object());
}
It always invokes the method with String argument .
Will the compiler compute the expression (10%2) while compiling ? I want to know whether expressions are computed at compile time or run time . Thanks.

Java uses early binding. The most specific method is chosen at compile time. The most specific method is chosen by number of parameters and type of parameters. Number of parameters is not relevant in this case. This leaves us with the type of parameters.
What type do the parameters have? Both parameters are expressions, using the ternary conditional operator. The question reduces to: What type does the conditional ternary operator return? The type is computed at compile time.
Given are the two expressions:
(10%2==0)? null : new Object(); // A
(10%2==0)? null : null; // B
The rules of type evaluation are listed here. In B it is easy, both terms are exactly the same: null will be returned (whatever type that may be) (JLS: "If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression."). In A the second term is from a specific class. As this is more specific and null can be substituted for an object of class Object the type of the whole expression is Object (JLS: "If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.").
After the type evaluation of the expressions the method selection is as expected.
The example with if you give is different: You call the methods with objects of two different types. The ternary conditional operator always is evaluated to one type at compile time that fits both terms.

JLS 15.25:
The type of a conditional expression is determined as follows:
[...]
If one of the second and third operands is of the null type and the type of the other
is a reference type, then the type of the conditional expression is that reference
type.
[...]
So the type of
10 % 2 == 0 ? null : new Object();
is Object.

test((10%2==0)?null:new Object());
Is the same as:
Object o;
if(10%2==0)
o=null;
else
o=new Object();
test(o);
Since type of o is Object (just like the type of (10%2==0)?null:new Object()) test(Object) will be always called. The value of o doesn't matter.

Your answer is : Runtime because in runtime specify parameter is instance of String or not so in compile-time can't find this.

This is the really nice question.
Let me try to clarify your code that you have written above.
In your first method call
test(null);
In this the null will be converted into string type so calling the test(String obj), as per JLS you are convinced with the call.
In the second method call
test((10%2==0)?null:new Object());
Which is going to return the boolean "true" value. So first boolean "true" value is going to auto cast into Boolean Wrapper class object. Boolean wrapper Object is finding the best match with your new Object() option in the ternary operator. And the method calls with Object as a parameter so it calls the following method
public static void test(Object obj)
For the experiment sake you can try the following combinations then you will get better clarity.
test((10 % 2 == 0) ? new Object() : "stringObj" );
test((10 % 2 == 0) ? new Object() : null );
test((10 % 2 == 0) ? "stringObj" : null );
Finally in the last when you are calling with the following code.
test((10%2==0)?null:null);
This time again it returns as boolean "true" value, and it will again follow the same casts as explained above. But this time there is no new Object() parameter is there in your ternary operator. So it will be auto type cast into null Object. Again it follows same method call as the your first method call.
In the last when you asked for code if you put in if .. else statement. Then also the compiler doing the fair decision with the code.
if(10%2==0) {
test(null);
}
Here all the time your if condition is true and calling this code test(null). Therefore all the time it call the firsttest(String obj) method with String as parameter as explained above.

I think your problem is that you are making the wrong assumption, your expressions:
test((10%2==0)?null:new Object());
and
test((10%2==0)?null:null;
Will always call test(null), and that's why they will go through test (Object).

as #Banthar mentionend the ?: operator assigns a value to a variable first then evaluates the condition.
On the other hand, the if condition you mentioned always returns true, so the compiler will replace the whole if-else block with only the body of the if.

1) the test() method is determined by the type of the parameter at the compilation time :
test((Object) null);
test((Object)"String");
output :
Object called
Object called
2) The compiler is even smarter, the compiled code is equivalent to just :
test(null);
you can check the bytecode with javap -c:
0: aconst_null
1: invokestatic #6 // Method test:(Ljava/lang/String;)V
4: return

This is what Java Language Specifications say about the problem.
If more than one method declaration is both accessible and applicable
to a method invocation, it is necessary to choose one to provide the
descriptor for the run-time method dispatch. The Java programming
language uses the rule that the most specific method is chosen.
This is test(String) method in your case.
And because of that if you add...
public static void test(Integer obj){
System.out.println("Ingeter called");
}
it will show compilation error -The method test(String) is ambiguous for the type OverloadingTest.
Just like JLS says:
It is possible that no method is the most specific, because there are
two or more maximally specific methods. In this case:
If all the maximally specific methods have the same signature, then:
If one of the maximally specific methods is not declared abstract, it
is the most specific method. Otherwise, all the maximally specific
methods are necessarily declared abstract. The most specific method is
chosen arbitrarily among the maximally specific methods. However, the
most specific method is considered to throw a checked exception if and
only if that exception is declared in the throws clauses of each of
the maximally specific methods. Otherwise, we say that the method
invocation is ambiguous, and a compile-time error occurs.

Related

Ternary operator in print statement of java [duplicate]

This question already has answers here:
Unexpected type resulting from the ternary operator
(4 answers)
Closed 4 years ago.
I was playing with ternary operator and noticed something odd. I have code below:
class Main {
static void foo(int a){
System.out.println("int");
}
static void foo(String a){
System.out.println("String");
}
static void foo(Object a){
System.out.println("object");
}
public static void main(String[] args) {
foo(2==3 ? 0xF00:"bar");
System.out.println((2==3 ? 0xF00:"bar").getClass().getName());
}
}
Which results in
object
java.lang.String
First line of result shows that this instruction passed to foo method with object parameter.
Second line that the instruction itself results in String.
Question:
Why if result is String compiler decides to go with Object?
Is this because of the type ambiguity?
If yes then why getting class name returned java.lang.String?
In Java, you have compile time type information and you have run time type information. Compile time type information is what the compiler can deduce about the type of a value or expression just by looking at it, but without executing it. When the compiler sees the expression
2 == 3 ? 0xF00 : "bar"
It does not know whether 2 == 3 will be true or false, because it does not execute code. So all it knows is that the result can be an Integer, or a String. So when the time comes to pick which foo method to call, it picks the one that accepts Object, since that is the only one that it knows will work in both scenarios.
However, when the code is actually running, 2 == 3 will be false, and the result will be a String, whose getClass() method will return String.class. And that is what you need to note: getClass() does not return the type that the variable had at compile time, but it returns the actual type of the object that the variable holds at run time. i.e.
Object o = "Hello!";
System.out.println(o.getClass());
will print java.lang.String, because even though to the compiler it is an Object, at run time it is actually a String.
You can skip to Summary if not interested in reading.
Refer the JavaDoc here: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25
Under 15.25.3. Reference Conditional Expressions It says:
The type of the conditional expression is the
result of applying capture conversion
For capture conversion : https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.10
Summary:
For type determination, capture conversion is used, wherein for your example int is first boxed to Integer and then the closest common super class of Integer and String is fetched, which is Object class. So the type for the Conditional Expression is Object and so the method with Object as parameter is called.
Now for second part, the Conditional operator is evaluated first, then it is unboxed and then .getClass() is evaluated. So it prints java.lang.String.
This is also documented here: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25
Under 15.25. Conditional Operator ? :
At run time, the first operand expression of the conditional
expression is evaluated first. If necessary, unboxing conversion is
performed on the result.
At compile stage, the compiler noticed that the result of 2 == 3 ? 0xF00 : "bar" could be int or String. To be compatible with both, it decide to call foo(Object a).
At runtime, the result of 2 == 3 ? 0xF00 : "bar" is String bar.

Different behavior about conditional operator when using jdk1.7 and jdk1.8 [duplicate]

Consider the following code
public class JDK10Test {
public static void main(String[] args) {
Double d = false ? 1.0 : new HashMap<String, Double>().get("1");
System.out.println(d);
}
}
When running on JDK8, this code prints null whereas on JDK10 this code results in NullPointerException
Exception in thread "main" java.lang.NullPointerException
at JDK10Test.main(JDK10Test.java:5)
The bytecode produced by the compilers is almost identical apart from two additional instructions produced by the JDK10 compiler which are related to autoboxing and seem to be responsible for the NPE.
15: invokevirtual #7 // Method java/lang/Double.doubleValue:()D
18: invokestatic #8 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
Is this behaviour a bug in JDK10 or an intentional change to make the behaviour stricter?
JDK8: java version "1.8.0_172"
JDK10: java version "10.0.1" 2018-04-17
I believe this was a bug which seems to have been fixed. Throwing a NullPointerException seems to be the correct behavior, according to the JLS.
I think that what is going on here is that for some reason in version 8, the compiler considered the bounds of the type variable mentioned by the method's return type rather than the actual type arguments. In other words, it thinks ...get("1") returns Object. This could be because it's considering the method's erasure, or some other reason.
The behavior should hinge upon the return type of the get method, as specified by the below excerpts from §15.26:
If both the second and the third operand expressions are numeric expressions, the conditional expression is a numeric conditional expression.
For the purpose of classifying a conditional, the following expressions are numeric expressions:
[…]
A method invocation expression (§15.12) for which the chosen most specific method (§15.12.2.5) has a return type that is convertible to a numeric type.
Note that, for a generic method, this is the type before instantiating the method's type arguments.
[…]
Otherwise, the conditional expression is a reference conditional expression.
[…]
The type of a numeric conditional expression is determined as follows:
[…]
If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.
In other words, if both expressions are convertible to a numeric type, and one is primitive and the other is boxed, then the result type of the ternary conditional is the primitive type.
(Table 15.25-C also conveniently shows us that the type of a ternary expression boolean ? double : Double would indeed be double, again meaning unboxing and throwing is correct.)
If the return type of the get method wasn't convertible to a numeric type, then the ternary conditional would be considered a "reference conditional expression" and unboxing wouldn't occur.
Also, I think the note "for a generic method, this is the type before instantiating the method's type arguments" shouldn't apply to our case. Map.get doesn't declare type variables, so it's not a generic method by the JLS' definition. However, this note was added in Java 9 (being the only change, see JLS8), so it's possible that it has something to do with the behavior we're seeing today.
For a HashMap<String, Double>, the return type of get should be Double.
Here's an MCVE supporting my theory that the compiler is considering the type variable bounds rather than the actual type arguments:
class Example<N extends Number, D extends Double> {
N nullAsNumber() { return null; }
D nullAsDouble() { return null; }
public static void main(String[] args) {
Example<Double, Double> e = new Example<>();
try {
Double a = false ? 0.0 : e.nullAsNumber();
System.out.printf("a == %f%n", a);
Double b = false ? 0.0 : e.nullAsDouble();
System.out.printf("b == %f%n", b);
} catch (NullPointerException x) {
System.out.println(x);
}
}
}
The output of that program on Java 8 is:
a == null
java.lang.NullPointerException
In other words, despite e.nullAsNumber() and e.nullAsDouble() having the same actual return type, only e.nullAsDouble() is considered as a "numeric expression". The only difference between the methods is the type variable bound.
There's probably more investigation that could be done, but I wanted to post my findings. I tried quite a few things and found that the bug (i.e. no unboxing/NPE) seems to only happen when the expression is a method with a type variable in the return type.
Interestingly, I've found that the following program also throws in Java 8:
import java.util.*;
class Example {
static void accept(Double d) {}
public static void main(String[] args) {
accept(false ? 1.0 : new HashMap<String, Double>().get("1"));
}
}
That shows that the compiler's behavior is actually different, depending on whether the ternary expression is assigned to a local variable or a method parameter.
(Originally I wanted to use overloads to prove the actual type that the compiler is giving to the ternary expression, but it doesn't look like that's possible given the above difference. It's possible there's still another way that I haven't thought of, though.)
JLS 10 doesn't seem to specify any changes to the conditional operator, but I have a theory.
According to JLS 8 and JLS 10, if the second expression (1.0) is of type double and the third (new HashMap<String, Double>().get("1")) is of type Double, then the result of the conditional expression is of type double. The JVM in Java 8 seems to be smart enough to know that, because you're returning a Double, there's no reason to first unbox the result of HashMap#get to a double and then box it back to a Double (because you specified Double).
To prove this, change Double to double in your example, and a NullPointerException is thrown (in JDK 8); this is because the unboxing is now occuring, and null.doubleValue() obviously throws a NullPointerException.
double d = false ? 1.0 : new HashMap<String, Double>().get("1");
System.out.println(d); // Throws a NullPointerException
It seems that this was changed in 10, but I can't tell you why.

What is the difference between "(Object)null" and "null" in Java? [duplicate]

This question already has answers here:
Why cast null to Object?
(4 answers)
Closed 9 years ago.
Take a look at the following example:
class nul
{
public static void main (String[] args)
{
System.out.println (String.valueOf((Object)null));
System.out.println (String.valueOf(null));
}
}
The first println writes null but the second throws a NullPointerException.
Why is only the second line worth an exception? And what is the difference between the two nulls? Is there a real null and a fake null in Java?
The first invocation will call the String.valueOf(Object) method, as you have explicitly typecasted null to Object reference. Conversely, the second one will invoke the overloaded String.valueOf(char[]) method, as char[] is more specific than Object for a null argument.
There are other overloaded versions of this method that accept primitive parameters, but those are not a valid match for a null argument.
From JLS §15.12.2:
There may be more than one such method, in which case the most
specific one is chosen. The descriptor (signature plus return type) of
the most specific method is one used at run time to perform the method
dispatch.
A method is applicable if it is either applicable by subtyping
(§15.12.2.2), applicable by method invocation conversion (§15.12.2.3),
or it is an applicable variable arity method (§15.12.2.4).
[...]
If several applicable methods have been identified during one of the
three phases of applicability testing, then the most specific one is
chosen, as specified in section §15.12.2.5.
Now check the source code of both the methods:
// This won't throw NPE for `obj == null`
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
// This will throw `NPE` for `data == null`
public static String valueOf(char data[]) {
return new String(data);
}
There are lots of overloaded String.valueOf methods in Java. Further, in Java null has any and all types so that anything (that isn't a primitive) can be null.
So, when you call (String.valueOf((Object)null) you call the valueOf method that takes an Object as use explicitly cast null to Object.
In the second example you don't explicitly cast the null to any particular type so in fact you call the valueOf method with a char[] which throws an NPE.
From the JLS §15.12.2
The second step searches the type determined in the previous step for
member methods. This step uses the name of the method and the types of
the argument expressions to locate methods that are both accessible
and applicable, that is, declarations that can be correctly invoked on
the given arguments.
There may be more than one such method, in which case the most
specific one is chosen. The descriptor (signature plus return type) of
the most specific method is one used at run time to perform the method
dispatch.
In this case char[] is more specific than Object so it is called when no explicit cast of null is made.
Although I accepted already an answer I would like to add the exact answer to the question, because the two answers concentrate on explaining the trap I walked into.
The difference between (Object)null and null is that the type of the first is forced to Object but the type of the second is not, as one could think, forced to Object. Instead it could also be an array instead of an object.
So the conclusion is: pass (Object)null instead of null as an argument to a method to be sure to get exactly the method working on objects instead of any other method working on arrays.

Returning null as an int permitted with ternary operator but not if statement

Let's look at the simple Java code in the following snippet:
public class Main {
private int temp() {
return true ? null : 0;
// No compiler error - the compiler allows a return value of null
// in a method signature that returns an int.
}
private int same() {
if (true) {
return null;
// The same is not possible with if,
// and causes a compile-time error - incompatible types.
} else {
return 0;
}
}
public static void main(String[] args) {
Main m = new Main();
System.out.println(m.temp());
System.out.println(m.same());
}
}
In this simplest of Java code, the temp() method issues no compiler error even though the return type of the function is int, and we are trying to return the value null (through the statement return true ? null : 0;). When compiled, this obviously causes the run time exception NullPointerException.
However, it appears that the same thing is wrong if we represent the ternary operator with an if statement (as in the same() method), which does issue a compile-time error! Why?
The compiler interprets null as a null reference to an Integer, applies the autoboxing/unboxing rules for the conditional operator (as described in the Java Language Specification, 15.25), and moves happily on. This will generate a NullPointerException at run time, which you can confirm by trying it.
I think, the Java compiler interprets true ? null : 0 as an Integer expression, which can be implicitly converted to int, possibly giving NullPointerException.
For the second case, the expression null is of the special null type see, so the code return null makes type mismatch.
Actually, its all explained in the Java Language Specification.
The type of a conditional expression is determined as follows:
If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
Therefore the "null" in your (true ? null : 0) gets an int type and then is autoboxed to Integer.
Try something like this to verify this (true ? null : null) and you will get the compiler error.
In the case of the if statement, the null reference is not treated as an Integer reference because it is not participating in an expression that forces it to be interpreted as such. Therefore the error can be readily caught at compile-time because it is more clearly a type error.
As for the conditional operator, the Java Language Specification §15.25 “Conditional Operator ? :” answers this nicely in the rules for how type conversion is applied:
If the second and third operands have the same type (which may be the null
type), then that is the type of the conditional expression.
Does not apply because null is not int.
If one of the second and third operands is of type boolean and the type of the
other is of type Boolean, then the type of the conditional expression is boolean.
Does not apply because neither null nor int is boolean or Boolean.
If one of the second and third operands is of the null type and the type of the
other is a reference type, then the type of the conditional expression is that
reference type.
Does not apply because null is of the null type, but int is not a reference type.
Otherwise, if the second and third operands have types that are convertible
(§5.1.8) to numeric types, then there are several cases: […]
Applies: null is treated as convertible to a numeric type, and is defined in §5.1.8 “Unboxing Conversion” to throw a NullPointerException.
The first thing to keep in mind is that Java ternary operators have a "type", and that this is what the compiler will determine and consider no matter what the actual/real types of the second or third parameter are. Depending on several factors the ternary operator type is determined in different ways as illustrated in the Java Language Specification 15.26
In the question above we should consider the last case:
Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).
This is by far the most complex case once you take a look at applying capture conversion (§5.1.10) and most of all at lub(T1, T2).
In plain English and after an extreme simplification we can describe the process as calculating the "Least Common Superclass" (yes, think of the LCM) of the second and third parameters. This will give us the ternary operator "type". Again, what I just said is an extreme simplification (consider classes that implement multiple common interfaces).
For example, if you try the following:
long millis = System.currentTimeMillis();
return(true ? new java.sql.Timestamp(millis) : new java.sql.Time(millis));
You'll notice that resulting type of the conditional expression is java.util.Date since it's the "Least Common Superclass" for the Timestamp/Time pair.
Since null can be autoboxed to anything, the "Least Common Superclass" is the Integer class and this will be the return type of the conditional expression (ternary operator) above. The return value will then be a null pointer of type Integer and that is what will be returned by the ternary operator.
At runtime, when the Java Virtual Machine unboxes the Integer a NullPointerException is thrown. This happens because the JVM attempts to invoke the function null.intValue(), where null is the result of autoboxing.
In my opinion (and since my opinion is not in the Java Language Specification many people will find it wrong anyway) the compiler does a poor job in evaluating the expression in your question. Given that you wrote true ? param1 : param2 the compiler should determine right away that the first parameter -null- will be returned and it should generate a compiler error. This is somewhat similar to when you write while(true){} etc... and the compiler complains about the code underneath the loop and flags it with Unreachable Statements.
Your second case is pretty straightforward and this answer is already too long... ;)
CORRECTION:
After another analysis I believe that I was wrong to say that a null value can be boxed/autoboxed to anything. Talking about the class Integer, explicit boxing consists in invoking the new Integer(...) constructor or maybe the Integer.valueOf(int i); (I found this version somewhere). The former would throw a NumberFormatException (and this does not happen) while the second would just not make sense since an int cannot be null...
Actually, in the first case the expression can be evaluated, since the compiler knows, that it must be evaluated as an Integer, however in the second case the type of the return value (null) can not be determined, so it can not be compiled. If you cast it to Integer, the code will compile.
private int temp() {
if (true) {
Integer x = null;
return x;// since that is fine because of unboxing then the returned value could be null
//in other words I can say x could be null or new Integer(intValue) or a intValue
}
return (true ? null : 0); //this will be prefectly legal null would be refrence to Integer. The concept is one the returned
//value can be Integer
// then null is accepted to be a variable (-refrence variable-) of Integer
}
How about this:
public class ConditionalExpressionType {
public static void main(String[] args) {
String s = "";
s += (true ? 1 : "") instanceof Integer;
System.out.println(s);
String t = "";
t += (!true ? 1 : "") instanceof String;
System.out.println(t);
}
}
The output is true, true.
Eclipse color codes the 1 in the conditional expression as autoboxed.
My guess is the compiler is seeing the return type of the expression as Object.

Calling closest fitting method

As part of developing a small ScriptEngine, I reflectively call java methods. A call by the script engine gives me the object the method name and an array of arguments. To call the method I tried to resolve it with a call to Class.getMethod(name, argument types).
This however only works when the classes of the arguments and the classes expected by the Method are the same.
Object o1 = new Object();
Object out = System.out;
//Works as System.out.println(Object) is defined
Method ms = out.getClass().getMethod("println",o1.getClass());
Object o2 = new Integer(4);
//Does not work as System.out.println(Integer) is not defined
Method mo = out.getClass().getMethod("println",o2.getClass());
I would like to know if there is a "simple" way to get the right method, if possible with the closest fit for the argument types, or if I have to implement this myself.
Closest fit would be:
Object o1 = new Integer(1);
Object o2 = new String("");
getMethod(name, o1.getClass())//println(Object)
getMethod(name, o2.getClass())//println(String)
Update:
To clarify what I need:
The Script Engine is a small project I write in my free time so there are no strikt rules I have to follow. So I thought that selecting methods called from the Engine the same way the java compiler selects methods at compile time only with the dynamic type and not the static type of the Object would work.(with or without autoboxing)
This is what I first hoped that the Class.getMethod() would solve. But the Class.getMethod() requires the exact same Classes as argument types as the Method declares, using a subclass will result in a no such method Exception. This may happen for good reasons, but makes the method useless for me, as I don't know in advance which argument types would fit.
An alternate would be to call Class.getMethods() and iterate through the returned array and try to find a fitting method. This would however be complicated if I don't just want to take the first "good" method which I come across, so I hoped that there would be an existing solution which at least handles:
closest fit: If arg.getClass() ==
subclass and methods m(Superclass),
m(Subclass) then call m(Subclass)
variable arguments:
System.out.printf(String ,String...)
Support for autoboxing would be nice, too.
If a call cannot be resolved it may throw an exception ( ma(String,Object), ma(Object, String), args= String,String)
(If you made it till here, thanks for taking the time to read it:-))
As others have pointed out there is no standard method that does this, so you are going to have to implement your own overload resolution algorithm.
It would probably make sense to follow javac's overload resolution rules as closely as possible:
http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#292575
You can probably ignore generics for a dynamically-typed scripting language, but you might still benefit from the bridge methods that the compiler generates automatically.
Some pitfalls to watch out for:
Class.isAssignableFrom does not know about automatic widening primitive conversions, because these are syntactic sugar implemented in the compiler; They do not occur in the VM or class hierarchy. e.g. int.class.isAssignableFrom(short.class) returns false.
Similarly Class.isAssignableFrom does not know about auto-boxing. Integer.class.isAssignableFrom(int.class) returns false.
Class.isInstance and Class.cast take an Object as an argument; You cannot pass primitive values to them. They also return an Object, so they cannot be used for unboxing ((int) new Integer(42) is legal in Java source but int.class.cast(new Integer(42)) throws an exception.)
I would suggest that you use getMethods(). It returns an array of all public methods (Method[]).
The most important thing here is:
"If the class declares multiple public member methods with the same parameter types, they are all included in the returned array."
What you will then need to do is to use the results in this array to determine which one of them (if any) are the closest match. Since what the closest match should be depends very much on your requirements and specific application, it does make sense to code it yourself.
Sample code illustrating one approach of how you might go about doing this:
public Method getMethod(String methodName, Class<?> clasz)
{
try
{
Method[] methods = clasz.getMethods();
for (Method method : methods)
{
if (methodName.equals(method.getName()))
{
Class<?>[] params = method.getParameterTypes();
if (params.length == 1)
{
Class<?> param = params[0];
if ((param == int.class) || (param == float.class) || (param == float.class))
{
//method.invoke(object, value);
return method;
}
else if (param.isAssignableFrom(Number.class))
{
return method;
}
//else if (...)
//{
// ...
//}
}
}
}
}
catch (Exception e)
{
//some handling
}
return null;
}
In this example, the getMethod(String, Class<?>) method will return a method that with only one parameter which is an int, float, double, or a superclass of Number.
This is a rudimentary implementation - It returns the first method that fits the bill. You would need to extend it to create a list of all methods that match, and then sort them according to some sort of criteria, and return the best matching method.
You can then take it even further by creating the more general getMethod(String, Class<?>) method, to handle more of the possible "close match" scenarios, and possibly even more than one paramter
HTH
Edit: As #finnw has pointed out, be careful when using Class#isAssignableFrom(Class<?> cls), due to its limitations, as I have in my sample code, testing the primitives separately from the Number objects.
AFAIK, there is no simple way to do this kind of thing. Certainly, there's nothing in the standard Java class libraries to do this.
The problem is that there is no single "right" answer. You need to consider all of your use-cases, decide what the "right method" should be and implement your reflection code accordingly.

Categories

Resources