Guess you have a class with some c-tors:
public Foo(int i, String s);
public Foo(float fl, String s);
public Foo(String s, Bar b, boolean f);
Now, when you have the following fn:
public Foo doSomething(Object… args)
{
/*… do something before … */
Foo foo = new Foo( ?!? );
/*… do something after … */
return foo;
}
What should one to to call the correct c-tor? Is there a way to translate args back into the ...-form?
No - after all, there could be any references in there, including null references.
You'd have to check the values you've actually been given against the parameter types for the constructor signatures.
I'd regard this as a sign that you probably shouldn't be using varargs in this situation. You could always overload the method with the same signatures as the constructors, and call common helper methods for the before/after parts... or if the time of the constructor call doesn't matter, do it all in a common method, except the construction:
public void doSomething(int i, String s)
{
doSomethingHelper(new Foo(i, s));
}
Apart from inspecting the concrete type of each element in args, casting them down manually, then invoking the proper constructor via reflection, there isn't much you can do. (And that would be ugly like hell...) The compiler needs to bind constructor calls statically, but you only get to know the concrete parameters at runtime.
An alternative would be to provide a constructor with varargs parameter, but this just delegates the problem one level lower. #Jon's suggestion of getting rid of the varargs altogether is better.
You can, of course check the number and type of the arguments and dispatch accordingly. But note my comment above.
Note that Object... is really just syntactic sugar for Object[]. So you have the same options here as you would if your parameter type was Object[].
Related
Why does return type of method not included in signature?
An example
public void method1(String arg){...}
public String method1(String arg){...}
It will cause an error.
This is done because the compiler would not be able to figure out the overload in all contexts.
For example, if you call
String x = method1("aaa");
the compiler knows that you are looking for the second overload. However, if you call
method1("aaa");
like this, the compiler has no idea which one of the two methods you wanted to invoke, because it is OK to call a method returning String and discard the result. To avoid ambiguities like this, Java prohibits overloads that differ solely on the return type.
Since your question doesn't address any particular programming language in the title (I know it does in the tag) I'll share my recent experience with Swift.
In Swift function/method signature actually includes return type. So compiler throws an error only if you call this function/method without explicitly specifying return type, e.g.:
func some() -> Bool {
return true;
}
func some() -> Int {
return 1;
}
let valBool: Bool = some()
let valInt: Int = some()
// this doesn't work: Ambiguous use of 'some'
some()
On top of this Swift even makes it more interesting. It allows you to have 2 functions/methods with the same parameters and return types only if parameters names are different, e.g.:
func some(#foo: Bool) -> Bool {
return foo;
}
func some(#bar: Bool) -> Bool {
return bar;
}
some(foo: true)
some(bar: false)
Thus it gives you semantic differentiation in methods signature
UPD. Since Swift 2.0 external parameter name was changed and now you have to provide external and local names twice even if it's the same
func some(foo foo: Bool) -> Bool {
return foo;
}
func some(bar bar: Bool) -> Bool {
return bar;
}
some(foo: true)
some(bar: false)
You can't overload a method only on it's return type. It's simply illegal. Let's assume for a moment that overloading methods using return type would be legal and you defined two method1 methods. Now we want to call that which returns the String object:
String string = method1(sth);
The JVM theoretically would be able to recognize which method you inteded to call, but what about such call:
method1(sth);
As you can see both methods could be invoked and such operations is unambiguous. The JVM doesn't know which method it should call. This is why such overloading is forbidden.
Because it is impossible to resolve which of the overloaded methods should be called in a case like this:
public static void main(String... args) {
method1("aaa");
}
There are several points to consider when designing things like overload resolution.
Reasons to omit overloading on return type:
Simplify ignoring the function return value (like people often do with the error codes).
Makes program easier to digest for human readers. In particular, that is the reason in Python they do not have function overloads at all. (matter of taste)
C legacy. When language comes from C-family and designers do not consider something to be a big deal it is left as it has always been...
Reasons to add overloading on return type:
Make it difficult to ignore returned values. This may be convenient and saves some typing, but will definitely bite you one day.
Expressivness (of course as opposed to ease of digesting :)). Did you ever wanted to write things like int x = json.get("int_value"); float y = json.get("float_value");? In some languages (like C++) that is still possible to achieve with proxies and cast operators, but overload on return type would be so much easier.
Expressiveness. Every time you pass retun value as a reference to your function just to reuse its resources this could be an overload on return type (with like hidden parameter). Consider string s; getline(cin, s); vs string s = getline(cin);. And this is where expressiveness comes together with referential transparency, and eventually, ease of code digesting.
Now back to your question 'why?'. Since you were asking about Java, the answer is obviously because James valued reasons to omit return type overloads over the reasons to include them in the language.
I had this very same question myself at one point, and while I can see how the compiler wouldn't know which function to call when you don't assign the return value to a variable of the appropriate type, why throw the error at the function level? Why not at the point the function(s) are called? Basically, once you've committed to having the signature differ only by the return value, it's up to you to make sure you use them that way, and then, and only then, would the compiler complain. Of course, it may take some extra work to get this to work with a strict one-pass compiler, but I think it could fly.
you can call function as procedure: method1("arg"); where method1 is the second method in your list (String method1(String arg){}). Compiler then would be unable to distinguish it from the first one (void method1(String arg){}).
Compiler takes care of method binding. When ever it encounters methodName() it has to bind to some method definition, at that point it may not know the return type of method. So method return type is not included in the method signature. Compiler binds the method according to the method signature.
When compiler encounters the method call. It statically binds the method call to one of the methods defined.
Lets see what happens if the return type is included in the method signature
class Example{
public void method1(String arg){ return arg}
public String method1(String arg){}
public static void main(String[] args){
Example e = new Example();
e.method1("abc");
}
}
e.method1("abc") The compiler will not know which method to bind to if return type is included in the method signature.
Method overloading is checked on the basis of number and type of arguments not on the basis of return type. That's why you are getting the error.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Method Overloading for NULL parameter
In the code below the output is
String
and if I remove the method with the parameter of type String then the output is
Object
I know how overloading of methods acts when the parameter types don't match exactly but I can not understand how null can be treated as an Object and/or a String parameter.
What is the explanation for this?
class C {
static void m1(Object x) {
System.out.print("Object");
}
static void m1(String x) {
System.out.print("String");
}
public static void main(String[] args) {
m1(null);
}
}
It always uses the most specific method according to the Java specs, section 15.12.2.5.
The intro is reasonably specific about it:
If more than one member method 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.
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.
Generally speaking, and at least for code readability, it's always best to try to be as explicit as possible. You could cast your null into the type that matches the signature you want to call. But that's definitely a questionable practice. It assumes everyone knows this rule and makes the code more difficult to read.
But it's a good academic question, so I +1 your question.
When multiple overloads match a signature, Java picks the most specific method from among them.
The value of null matches both Object and String, but String is a subclass of Object, so String is picked. If you add another overload with a sibling of String in the class hierarchy, you'd get a compile error.\
// DOES NOT COMPILE
class C {
static void m1(Object x) {
System.out.print("Object");
}
static void m1(String x) {
System.out.print("String");
}
static void m1(Integer x) {
System.out.print("Integer");
}
public static void main(String[] args) {
m1(null);
}
}
Here is a link to a post that discusses your code example at some length.
If you need to force the call of a aprticular overloaded method when passing null as parameter, you have to cast it, like this:
m1((String)null);
By doing this, you make sure you're calling the correct overloaded version of the method.
You have to set the type of null to tell Java what function you want to call.
So you do: m1((Object) null); to call the implementation with the Object parameter and you do m1((String) null); to call the other one.
1. As String is also an object the JVM got confused to call which method at runtime.
2. If you want to specify which method to call at runtime, you can do this by explicit casting
eg:
m1((String)null);
I have a String which can either be of Double or Integer type or some other type. I first need to create a Double or Integer object and then send it over to a overloaded method. Here's my code so far;
public void doStuff1(object obj, String dataType){
if ("Double".equalsIgnoreCase(dataType)) {
doStuff2(Double.valueOf(obj.toString()));
} else if ("Integer".equalsIgnoreCase(dataType)) {
doStuff2(Integer.valueOf(obj.toString()));
}
}
public void doStuff2(double d1){
//do some double related stuff here
}
public void doStuff2(int d1){
//do some int related stuff here
}
I'd like to do this without if/else, with something like this;
Class<?> theClass = Class.forName(dataType);
The problem is 'theClass' still can't be cast to either double or int. I would be gratefull for any ideas.
Thanks.
Found a related thread; Overloading in Java and multiple dispatch
This is not just a problem of dealing with primitive types.
Which method to call is decided in compile time, that is, if you want to be able to call different methods depending on the type of the arguments, you'll need several calls (i.e. you need the if-construct).
In other words, it wouldn't work even if doStuff2 took Integer and Double as arguments (your code is basically as good as it gets).
(In fancy words, this is due to the fact that Java has single dispatch. To emulate multiple dispatch you either need to use conditional statements or a visitor pattern.)
Since the method call is decided at compile time as the another answer told you, overloading won't work for you. I think that this problem can be solved with inheritance. So you write a base class with yourMethod() and override it in your derived classes.
As aioobe says, the choice between overloaded methods is made at compile time based on the static types of the arguments.
If you want to simulate overload choice at runtime, you will need to do some complicated runtime analysis of the different possible methods. It would go something like this:
get all declared methods of the class that declared doStuff2.
filter out the methods whose name is not doStuff2.
filter out the methods whose argument type cannot be assigned from the (dynamic) type of the argument value.
of the remaining methods, pick the one that is the best match ... taking care to deal with "ties" as ambiguous.
This will be tricky to code, and trickier if you also throw in handling of primitive types. It will also make the method calls expensive.
Frankly, some kind of hard-wired dispatching is much simpler. If you don't like if / else tests (or switching on a String in Java 7), then you could do something like this.
Map<String, Operation> map = ...
map.put("Double", new Operation(){
public void doIt(Object obj) {
doStuff2((Double) obj);
}});
map.put("Integer", new Operation(){
public void doIt(Object obj) {
doStuff2((Integer) obj);
}});
...
map.get(typeName).doIt(obj);
... which at least allows you to "plug in" support for new types dynamically.
If you resort to reflection, you'll only have to deal specially with primitive types. So your technique can work, but with the addition of a few explicit tests. If you need to reflectively find a method that accepts a primitive double, use double.class.
I want to make a method that takes 1 required parameter and 1 optional parameter, but I found how to make an optional array which is by making in the parameter (int... b) but this is for an array, I want to make it just either this value is null or the user entered it, I can make it by making 2 methods of the same name but one with the single parameter and one with the 2 parameters, but can it be done with just one method?
Thanks
No, Java doesn't support optional parameters. Another alternative to overloading (which doesn't make much sense for two parameters but does make sense for more) is to use a builder type which represents all the parameters - you can provide a constructor for the builder which contains the required parameters, and then a setter for each of the optional ones, making the setter return the builder itself. So calling the method becomes something like:
foo.doSomething(new ParameterBuilder(10).setBar(2).setBaz(10));
What you are looking for is default arguments support. Java doesn't have this capability but it more or less simulates this capability.
One simple way is to use method overloading.
Another approach is to identify special cases directly and then substitute the default values.
Here's an example of mixing both of those approaches by Ivo Limmen:
public void test() {
this.test(null, null);
}
public void test(String p1) {
this.test(p1, null);
}
public void test(String p1, String p2) {
if(p1 == null) {
...
} else {
...
}
if(p2 == null) {
...
} else {
...
}
}
A very interesting approach I found is to use Design Builder pattern. There is an example here
There is also an interesting discussion here
In java, this is accomplished by something called method overloading. You can create multiple methods with the same name, but different parameters.
For example:
void method1(String arg1){
// only one argument, here you can delegate the work of this method to the implementation that takes the most parameters and pass null for the optional parameters.
method1(arg1, null);
}
void method1(String ar1, String arg2){
// do work, handle the case where arg2 is null.
}
Clients of your code can call either one. To them, it appears as if the second argument is optional.
No, this is exactly what method overloading is for
There are no "default" values for parameters in Java's methods. Either use varargs or method overloading.
in java, is it possible to access the instance to which a method belongs, given only the method?
for example:
public class ClassA {
private ClassB instanceB = new ClassB();
// ...
private void sendMethod () {
instanceB.receiveMethod(foo);
}
public void foo () {}
}
public class ClassB {
public void receiveMethod (Method method) {
Object o = foo.getInstanceOwner(); // just made that part up...
}
}
my feeling is that methods belong to classes, not instances of a class, so the answer is no, but maybe there's some sneaky reflection technique i don't know about. i could always pass 'this' along with method foo, but that seems like extra baggage.
Taken from
A Method provides information about, and access to, a single method on a class or interface. The reflected method may be a class method or an instance method (including an abstract method).
A Method permits widening conversions to occur when matching the actual parameters to invoke with the underlying method's formal parameters, but it throws an IllegalArgumentException if a narrowing conversion would occur.
You can call Method#invoke but you will need the instance of the object you want to call the method on, from the method doc:
Invokes the underlying method
represented by this Method object, on
the specified object with the
specified parameters. Individual
parameters are automatically unwrapped
to match primitive formal parameters,
and both primitive and reference
parameters are subject to method
invocation conversions as necessary.
If the underlying method is static,
then the specified obj argument is
ignored. It may be null.
If the number of formal parameters
required by the underlying method is
0, the supplied args array may be of
length 0 or null.
If the underlying method is an
instance method, it is invoked using
dynamic method lookup as documented in
The Java Language Specification,
Second Edition, section 15.12.4.4; in
particular, overriding based on the
runtime type of the target object will
occur.
If the underlying method is static,
the class that declared the method is
initialized if it has not already been
initialized.
If the method completes normally, the
value it returns is returned to the
caller of invoke; if the value has a
primitive type, it is first
appropriately wrapped in an object.
However, if the value has the type of
an array of a primitive type, the
elements of the array are not wrapped
in objects; in other words, an array
of primitive type is returned. If the
underlying method return type is void,
the invocation returns null.
So the TL:DR is unless you have the actual object you want you call the method on, it is not possible.
public class ClassA {
private ClassB instanceB = new ClassB();
// ...
private void sendMethod () {
Method m = ClassA.class.getMethod("foo", null);
instanceB.receiveMethod(m);
}
public void foo () {}
}
public class ClassB {
public void receiveMethod (Method method) {
Class c = method.getDeclaringClass();
}
}
gives you the owning Class. An instance doesn't own methods.
You can do this, but the proper way in your example would be the use of an interface, because that seems to be what you want: You want to pass in an object that ClassB knows how to operate on.
interface Callback {
void foo();
}
public class ClassA implements Callback {...}
public class ClassB {
public void receiveMethod(Callback cb) {...}
}
This is like asking:
"Given an apple from an Apple orchard, which tree owns this apple?"
The answer to which is:
"No idea, since all apple trees produce apples, it could belong to any tree".
... in other words - you must supply an instance from which the method will be called
EDIT
From one of your comments, I gather you are looking for an alternative of the Observer pattern. You say you don't like the messiness of the Observer pattern and that it is not "generic" enough for you.
I would argue that it is probably one of the least messiest patterns in existence, AND interfaces are by definition as generic as things get!
So, perhaps its an implementation problem you're having. Luckily, I have already posted on SO an Observer implementation in JAVA, to demonstrate how powerful and elegant it is.
Polymorphism and Interfaces in Java (can polymorphism be used to implement interfaces...why?)
In fact: reflection is messier than using an interface, since you can't guarantee at compile time that the type of Object you are invoking an instance of a Method on, even supports that method! (without some error checking code). Versus with interfaces, its not possible to even have that problem.