This question already has answers here:
How does method reference casting work?
(3 answers)
Closed last month.
This post was edited and submitted for review last month and failed to reopen the post:
Original close reason(s) were not resolved
I'm learning Java 8 with Lambda, Streams, and method reference. Regarding the example below,
Optional<String> s = Optional.of("test");
System.out.println(s.map(String::toUpperCase).get());
I don't understand how is it possible to use String::toUpperCase as an input for this map() method.
This is the method implementation:
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
return Optional.ofNullable(mapper.apply(value));
}
}
So it requires a function interface, and it has this apply() method: R apply(T t); This method has an input argument.
And toUpperCase() method doesn't have any argument:
public String toUpperCase() {
return toUpperCase(Locale.getDefault());
}
If the abstract method apply(T t) has one argument, then the implemented method should have one argument of the same type. How can parameterless method toUpperCase() implement the apply(T t) method from a function interface?
I try to recreate the same conditions:
I create a functional interface:
public interface Interf {
String m1(String value);
}
Then I create a class with the method reference for m1():
public class Impl {
public String value;
public String toUpp() {
return value.toUpperCase();
}
}
And here is a class for test:
public class Test {
public static void main(String[] args) {
Interf i = String::toUpperCase;
System.out.println(i.m1("hey"));
Interf i1 = Impl::toUpp;
System.out.println(i.m1("hello"));
}
}
There isn't any issue at this statement: Interf i = String::toUpperCase; but there is a compilation error on this line: Interf i1 = Impl::toUpp;. It says:
Non-static method cannot be referenced from a static context
But toUpperCase() is also a non-static method. And even if I make the toUpp() static, it is still not working, it is working only if I add a String argument as an input argument for toUpp(). But then why is it working for String::toUpperCase?
TL;DR
Parameters that a Method reference is expected to consume according to the contract imposed by a Functional interface it implements are NOT necessarily the same as parameters of the method used in the Method reference.
This answer is a journey from this common misconception towards understanding all syntactical flavors of Method references.
Let's take tiny baby steps to dispel misunderstanding, starting from the definition of the Method reference.
What is Method reference
Here is the definition of the Method reference according to the Java Language Specification §15.13. Method Reference Expressions
A method reference expression is used to refer to the invocation of a
method without actually performing the invocation. Certain forms of
method reference expression also allow class instance creation (§15.9)
or array creation (§15.10) to be treated as if it were a method
invocation.
emphasis added
So, a Method reference is a mean of referring the method-invocation without invoking a method. Or in other words, it's a way of describing a certain behavior by delegating to the existing functionality.
Let's take a small detour and have a look at the sibling of a Method reference, a Lambda expression.
Lambda expressions are also a way to describe behavior (without executing it), and both lambdas and Method references should conform to a Functional interface. Let's declare some lambdas.
Consider, we have a domain class Foo and utility class FooUtils.
public class FooUtils {
public static Foo doSomethingWithFoo(Foo foo) {
// do something
return new Foo();
}
}
And we need to define a function of type UnaryOperator<Foo>, let's start with writing a lambda expression:
UnaryOperator<Foo> fooChanger = foo -> FooUtils.doSomethingWithFoo(foo);
Lambda receives an instance of Foo as an argument and feeds it into the existing utility method. Quite simple, right? Nothing special happens inside the lambda's body, and since have defined the type as UnaryOperator<Foo> the lambda should expect Foo. Everything is absolutely predictable, isn't it? Now the question is: can we alternate that?
Sure, we can!
UnaryOperator<Foo> fooChanger = FooUtils::doSomethingWithFoo;
That's where a Method reference comes to the rescue. It provides a shortened syntax by:
1. Dropping the lambda's arguments (they are still there, we're simply not displaying them because we know what they are).
2. Removing the parentheses after the method name. Again the method declaration is known (and let's assume that there are no ambiguities), and we are not performing any prior transformation with arguments, and we are not using any additional arguments apart from those that should come according to the contract of the Functional interface. Only in this case everything is predictable and can a method reference.
Key takeaways:
you may think of method references as if they are shortened lambdas.
the arguments of the method reference are the same the equivalent lambda receives because they are implementations to the same interface. These parameters are still there implicitly, just dropped for the purpose of conciseness. And more importantly, parameters that a method reference consumes should not be confused with parameters expected method it refers to. In other word the first parameters an input of the method reference (and they are compliant with the contract defined by the interface) and the latter related to what happens inside the reference, and have no connection to the first ones.
More examples
Let's examine a few more examples. Let's say we have a simple object representing a coin with a single property isHeads describing which side the coin is showing (i.e. heads or tails).
public static class Coin {
public static final Random RAND = new Random();
private final boolean isHeads;
public Coin() {
this.isHeads = RAND.nextBoolean();
}
private Coin(boolean isHeads) {
this.isHeads = isHeads;
}
public Coin reverse() {
return new Coin(!isHeads);
}
public boolean isHeads() {
return isHeads;
}
}
Let's generate a coin. For that we can use implement of Supplier which very generous, supplier doesn't receive arguments, it produces a value. Let's definable both a lambda and a reference
Supplier<Coin> coinProducer = () -> new Coin(); // no argument required according to the contract of Supplier
Supplier<Coin> coinProducer1 = Coin::new; // Supplier expressed as a reference to no-args constructor
Both don't receive any argument (as per contract of the Supplier), both refer to the no-arguments constructor.
Now let's consider the predicates determining if the coin shows heads implemented via a lambda and a method reference:
Predicate<Coin> isHeads = coin -> coin.isHeads();
Predicate<Coin> isHeads1 = Coin::isHeads;
Again, both the lambda and the method reference are compliant with the Predicate's contract and both receive an instance of Coin as an argument (it can't be otherwise, simply concise syntax of the method reference doesn't show that).
So far, so good? Let's move further and try another way to obtain a Coin, let's define a Function:
Function<Boolean, Coin> booleanToCoin = value -> new Coin(value);
Function<Boolean, Coin> booleanToCoin1 = Coin::new;
Now both the lambda and the reference are consuming a boolean value and making use of the parameterized constructor. Did not notice that method reference describing Supplier<Coin> and Function<Boolean, Coin> looks identical.
Reminder: both Lambda expressions and Method references have no type by itself. They are so-called poly-expressions, which means their type should be inferred by the compiler based on the context in which they appear. Both the lambda and the reference should conform to a Functional interface, and the interface they implement dictates who they are and what they are doing.
In all examples described earlier, arguments of consumed by a method reference appeared to be the same as the ones expected by the referenced method, but it's not mandatory for them to be the identical. It's time to examine a couple or examples where it not the case to dispel the illusions.
Let's consider a UnaryOperator reversing a coin:
UnaryOperator<Coin> coinFlipper = coin -> coin.reverse(); // UnaryOperator requires one argument
UnaryOperator<Coin> coinFlipper1 = Coin::reverse; // UnaryOperator still requires one argument expressed as a reference to no arg method
All implementations of the UnaryOperator receive a Coin instance as an argument, and another coin is being produced as a result of the invocation of reverse(). The fact that reverse is parameterless is not an issue, because we concerned about what it produces, and not what it consumes.
Let's try to define a tougher method reference. To begin with, introduce in the Coin class a new instance method called xor(), which is immensely useful for XOR-ing two coins:
public Coin xor(Coin other) {
return new Coin(isHeads ^ other.isHeads);
}
Now when two object come into play we have more possibilities, let's start with the simplest case one by defining a UnariOperator:
final Coin baseCoin = new Coin();
UnaryOperator<Coin> xorAgainstBase = coin -> baseCoin.xor(coin);
UnaryOperator<Coin> xorAgainstBase1 = baseCoin::xor;
In the above example an instance of Coin defined outside the function is used to perform the transformation via the instance-method.
A little bit more complicated case would be a BinaryOperator for XOR-ing a couple of coins might look like this:
BinaryOperator<Coin> xor = (coin1, coin2) -> coin1.xor(coin2);
BinaryOperator<Coin> xor1 = Coin::xor;
Now we have two arguments coming as an input and a Coin instance should be produce as an output as per BinaryOperators contract.
The interesting thing is the first argument serves as an instance on which the method xor() would be invoked, and the second is passed to the method (note that xor() expect only one argument).
You might ask what would happen if there would be another method for XOR-ing coins. A static method expecting two arguments:
public static Coin xor(Coin coin1, Coin coin2) {
return new Coin(coin1.isHeads ^ coin2.isHeads);
}
Then the compiler would fail to resolve the method reference, because here we have more the one potentially applicable method and none of them can be considered to be more specific than the other since the types of arguments are the same. That would cause a compilation error. But if we would have either of them (not both together), reference Coin::xor would work fine.
Types of Method references
Basically, the examples that we have walked through covered all the types of method references. Now, let's enumerate them.
The official tutorial provided by Oracle re are four kinds of method references:
Reference to a Static method
Class::staticMethod
Example Coin::xor which refers to the static method xor(Coin coin1, Coin coin2).
Examples with standard JDK-classes:
BinaryOperator<Integer> sum = Integer::sum; // (i1, i2) -> Integer.sum(i1, i2)
BiFunction<CharSequence, Iterable<CharSequence>, String> iterableToString
= String::join; // (delimiter, strings) -> String.join(delimiter, strings)
Reference to an instance method of a particular object
instance::instanceMethod
The example illustrating this case would the usage of the instance method xor(Coin other) with a coin defined outside the function, which is internaly used to invoke xor() on it passing the function-argument into the method.
final Coin baseCoin = new Coin();
UnaryOperator<Coin> xorAgainstBase1 = baseCoin::xor; // same as coin -> baseCoin.xor(coin)
Examples with standard JDK-classes:
Set<Foo> fooSet = // initialing the Set
Predicate<Foo> isPresentInFooSet = fooSet::contains;
Reference to an Instance Method of an Arbitrary Object of a Particular Type
Class::methodName
In this case method refernce operates on an instance that comes as an argument (we would have reference to it only it we would use a lambda), therefore containing type, which can be tha actual type or one the super types, is used to refer to this instance.
An example would a Predicate checking if the coin shows heads Coin::isHeads.
Examples with standard JDK-classes:
Function<List<String>, Stream<String>> toStream = Collection::stream;
List<List<String>> lists = List.of(List.of("a", "b", "c"), List.of("x", "y", "z"));
List<String> strings1 = lists.stream()
.flatMap(toStream)
.toList();
// A slightly more complicate example taken from the linked tutorial
// Equivalent lambda: (a, b) -> a.compareToIgnoreCase(b)
String[] stringArray = { "Barbara", "James", "Mary" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
Reference to a Constructor
Class::new
We have cove this case already with the following examples:
Supplier<Coin> refering to no args-constracor implemented as Coin::new;
Function<Boolean, Coin> which makes use of the single-arg constructor by passing incoming boolean value also expressed as Coin::new.
How can toUpperCase() method implement apply(T t) method from Function
interface?
The method reference String::toUpperCase has unbound receiver.
Java 8: Difference between method reference Bound Receiver and UnBound Receiver
Then the argument T t would be the receiver
In your example
public <U, T> Optional<U> map(Function<? super T, ? extends U> mapper) {
...
return Optional.ofNullable(mapper.apply(value));
}
by calling map(String::toUpperCase)
If value, let's say equal to "Example String", would be the receiver so mapper.apply("Example String"); would be equivalent to "Example String".toUpperCase();
Related
public class X{
public static void print(Integer n, Function<Integer, String> fn) {
System.out.println(fn.apply(n));
}
public static void main(String []args){
print(3, Integer::repeat()); //
}
}
Example:
Since 3 is a given number, the value returned by the function and printed on the console should be "aaa".
Your method reference is wrong in a couple ways. First, a method reference doesn't end in parentheses (). Second, the Integer class doesn't have any repeat method.
The String class defines the repeat method, available for the first time in Java 11. But you want to execute on a specific instance of a string, namely the constant "a".
Try "a"::repeat.
Method references as well as lambda expressions should conform to a function interface, by providing an implementation of the abstract method defined by this interface.
And interface Function declares a method apply()
R apply(T t);
You can implement it like that with lambda expression:
Function<Integer, String> fun = num -> String.valueOf(num);
Method repeat() is an instance method of the String class, i.e. you have to invoke it on the instance of the String. So, need to define this string either by outside the lambda or hard-code it inside the body of lambda.
public static final String str = "a";
Function<Integer, String> fun = num -> str.repeat(num);
Now, in order to transform this lambda expression in a method reference, let's first pose the question of what the method reference is.
Method reference - is the way to utilize an existing method to provide an implantation of the behavior defined by a function interface.
There are four kinds of method references (a quote from the Oracles's tutorial):
Reference to a static method ContainingClass::staticMethodName
Reference to an instance method of a particular object containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName
Reference to a constructor ClassName::new
Note that the syntax of method references doesn't require parentheses () because that how the language was designed, and I guess because method references are intended to be concise and expressive.
The type of method reference that we need to implement this function using repeat() method is a reference to an instance method of a particular object (because repeat() is an instance method). And that's how it might look like:
public static final String str = "a";
Function<Integer, String> fun = str::repeat;
print(3, fun); // or print(3, str::repeat);
Note that variables used inside both method references and lambda expressions must be final or effectively final, i.e. should be assigned only once.
I'm experimenting with this code:
interface Callee {
public void foo(Object o);
public void foo(String s);
public void foo(Integer i);
}
class CalleeImpl implements Callee
public void foo(Object o) {
logger.debug("foo(Object o)");
}
public void foo(String s) {
logger.debug("foo(\"" + s + "\")");
}
public void foo(Integer i) {
logger.debug("foo(" + i + ")");
}
}
Callee callee = new CalleeImpl();
Object i = new Integer(12);
Object s = "foobar";
Object o = new Object();
callee.foo(i);
callee.foo(s);
callee.foo(o);
This prints foo(Object o) three times. I expect the method selection to take in consideration the real (not the declared) parameter type. Am I missing something? Is there a way to modify this code so that it'll print foo(12), foo("foobar") and foo(Object o)?
I expect the method selection to take
in consideration the real (not the
declared) parameter type. Am I missing
something?
Yes. Your expectation is wrong. In Java, dynamic method dispatch happens only for the object the method is called on, not for the parameter types of overloaded methods.
Citing the Java Language Specification:
When a method is invoked (§15.12), the
number of actual arguments (and any
explicit type arguments) and the
compile-time types of the arguments
are used, at compile time, to
determine the signature of the method
that will be invoked (§15.12.2). If
the method that is to be invoked is an
instance method, the actual method to
be invoked will be determined at run
time, using dynamic method lookup
(§15.12.4).
As mentioned before overloading resolution is performed at compile time.
Java Puzzlers has a nice example for that:
Puzzle 46: The Case of the Confusing Constructor
This puzzle presents you with two Confusing constructors. The main method invokes a constructor,
but which one? The program's output depends on the answer. What does the program print, or is it
even legal?
public class Confusing {
private Confusing(Object o) {
System.out.println("Object");
}
private Confusing(double[] dArray) {
System.out.println("double array");
}
public static void main(String[] args) {
new Confusing(null);
}
}
Solution 46: Case of the Confusing Constructor
...
Java's overload resolution process operates in two phases. The first phase selects all the methods or constructors that are accessible and applicable. The second phase selects the most specific of the methods or constructors selected in the first phase. One method or constructor is less specific than another if it can accept any parameters passed to the other [JLS 15.12.2.5].
In our program, both constructors are accessible and applicable. The constructor
Confusing(Object) accepts any parameter passed to Confusing(double[]), so
Confusing(Object) is less specific. (Every double array is an Object, but not every Object is a double array.) The most specific constructor is therefore Confusing(double[]), which explains the program's output.
This behavior makes sense if you pass a value of type double[]; it is counterintuitive if you pass null. The key to understanding this puzzle is that the test for which method or constructor is most specific does not use the actual parameters: the parameters appearing in the invocation.
They are used only to determine which overloadings are applicable. Once the compiler determines which overloadings are applicable and accessible, it selects the most specific overloading, using only the formal parameters: the parameters appearing in the declaration.
To invoke the Confusing(Object) constructor with a null parameter, write new
Confusing((Object)null). This ensures that only Confusing(Object) is applicable. More
generally, to force the compiler to select a specific overloading, cast actual parameters to the declared types of the formal parameters.
Ability to dispatch a call to a method based on types of arguments is called multiple dispatch. In Java this is done with Visitor pattern.
However, since you're dealing with Integers and Strings, you cannot easily incorporate this pattern (you just cannot modify these classes). Thus, a giant switch on object run-time will be your weapon of choice.
In Java the method to call (as in which method signature to use) is determined at compile time, so it goes with the compile time type.
The typical pattern for working around this is to check the object type in the method with the Object signature and delegate to the method with a cast.
public void foo(Object o) {
if (o instanceof String) foo((String) o);
if (o instanceof Integer) foo((Integer) o);
logger.debug("foo(Object o)");
}
If you have many types and this is unmanageable, then method overloading is probably not the right approach, rather the public method should just take Object and implement some kind of strategy pattern to delegate the appropriate handling per object type.
I had a similar issue with calling the right constructor of a class called "Parameter" that could take several basic Java types such as String, Integer, Boolean, Long, etc. Given an array of Objects, I want to convert them into an array of my Parameter objects by calling the most-specific constructor for each Object in the input array. I also wanted to define the constructor Parameter(Object o) that would throw an IllegalArgumentException. I of course found this method being invoked for every Object in my array.
The solution I used was to look up the constructor via reflection...
public Parameter[] convertObjectsToParameters(Object[] objArray) {
Parameter[] paramArray = new Parameter[objArray.length];
int i = 0;
for (Object obj : objArray) {
try {
Constructor<Parameter> cons = Parameter.class.getConstructor(obj.getClass());
paramArray[i++] = cons.newInstance(obj);
} catch (Exception e) {
throw new IllegalArgumentException("This method can't handle objects of type: " + obj.getClass(), e);
}
}
return paramArray;
}
No ugly instanceof, switch statements, or visitor pattern required! :)
Java looks at the reference type when trying to determine which method to call. If you want to force your code you choose the 'right' method, you can declare your fields as instances of the specific type:
Integeri = new Integer(12);
String s = "foobar";
Object o = new Object();
You could also cast your params as the type of the param:
callee.foo(i);
callee.foo((String)s);
callee.foo(((Integer)o);
If there is an exact match between the number and types of arguments specified in the method call and the method signature of an overloaded method then that is the method that will be invoked. You are using Object references, so java decides at compile time that for Object param, there is a method which accepts directly Object. So it called that method 3 times.
I'm experimenting with this code:
interface Callee {
public void foo(Object o);
public void foo(String s);
public void foo(Integer i);
}
class CalleeImpl implements Callee
public void foo(Object o) {
logger.debug("foo(Object o)");
}
public void foo(String s) {
logger.debug("foo(\"" + s + "\")");
}
public void foo(Integer i) {
logger.debug("foo(" + i + ")");
}
}
Callee callee = new CalleeImpl();
Object i = new Integer(12);
Object s = "foobar";
Object o = new Object();
callee.foo(i);
callee.foo(s);
callee.foo(o);
This prints foo(Object o) three times. I expect the method selection to take in consideration the real (not the declared) parameter type. Am I missing something? Is there a way to modify this code so that it'll print foo(12), foo("foobar") and foo(Object o)?
I expect the method selection to take
in consideration the real (not the
declared) parameter type. Am I missing
something?
Yes. Your expectation is wrong. In Java, dynamic method dispatch happens only for the object the method is called on, not for the parameter types of overloaded methods.
Citing the Java Language Specification:
When a method is invoked (§15.12), the
number of actual arguments (and any
explicit type arguments) and the
compile-time types of the arguments
are used, at compile time, to
determine the signature of the method
that will be invoked (§15.12.2). If
the method that is to be invoked is an
instance method, the actual method to
be invoked will be determined at run
time, using dynamic method lookup
(§15.12.4).
As mentioned before overloading resolution is performed at compile time.
Java Puzzlers has a nice example for that:
Puzzle 46: The Case of the Confusing Constructor
This puzzle presents you with two Confusing constructors. The main method invokes a constructor,
but which one? The program's output depends on the answer. What does the program print, or is it
even legal?
public class Confusing {
private Confusing(Object o) {
System.out.println("Object");
}
private Confusing(double[] dArray) {
System.out.println("double array");
}
public static void main(String[] args) {
new Confusing(null);
}
}
Solution 46: Case of the Confusing Constructor
...
Java's overload resolution process operates in two phases. The first phase selects all the methods or constructors that are accessible and applicable. The second phase selects the most specific of the methods or constructors selected in the first phase. One method or constructor is less specific than another if it can accept any parameters passed to the other [JLS 15.12.2.5].
In our program, both constructors are accessible and applicable. The constructor
Confusing(Object) accepts any parameter passed to Confusing(double[]), so
Confusing(Object) is less specific. (Every double array is an Object, but not every Object is a double array.) The most specific constructor is therefore Confusing(double[]), which explains the program's output.
This behavior makes sense if you pass a value of type double[]; it is counterintuitive if you pass null. The key to understanding this puzzle is that the test for which method or constructor is most specific does not use the actual parameters: the parameters appearing in the invocation.
They are used only to determine which overloadings are applicable. Once the compiler determines which overloadings are applicable and accessible, it selects the most specific overloading, using only the formal parameters: the parameters appearing in the declaration.
To invoke the Confusing(Object) constructor with a null parameter, write new
Confusing((Object)null). This ensures that only Confusing(Object) is applicable. More
generally, to force the compiler to select a specific overloading, cast actual parameters to the declared types of the formal parameters.
Ability to dispatch a call to a method based on types of arguments is called multiple dispatch. In Java this is done with Visitor pattern.
However, since you're dealing with Integers and Strings, you cannot easily incorporate this pattern (you just cannot modify these classes). Thus, a giant switch on object run-time will be your weapon of choice.
In Java the method to call (as in which method signature to use) is determined at compile time, so it goes with the compile time type.
The typical pattern for working around this is to check the object type in the method with the Object signature and delegate to the method with a cast.
public void foo(Object o) {
if (o instanceof String) foo((String) o);
if (o instanceof Integer) foo((Integer) o);
logger.debug("foo(Object o)");
}
If you have many types and this is unmanageable, then method overloading is probably not the right approach, rather the public method should just take Object and implement some kind of strategy pattern to delegate the appropriate handling per object type.
I had a similar issue with calling the right constructor of a class called "Parameter" that could take several basic Java types such as String, Integer, Boolean, Long, etc. Given an array of Objects, I want to convert them into an array of my Parameter objects by calling the most-specific constructor for each Object in the input array. I also wanted to define the constructor Parameter(Object o) that would throw an IllegalArgumentException. I of course found this method being invoked for every Object in my array.
The solution I used was to look up the constructor via reflection...
public Parameter[] convertObjectsToParameters(Object[] objArray) {
Parameter[] paramArray = new Parameter[objArray.length];
int i = 0;
for (Object obj : objArray) {
try {
Constructor<Parameter> cons = Parameter.class.getConstructor(obj.getClass());
paramArray[i++] = cons.newInstance(obj);
} catch (Exception e) {
throw new IllegalArgumentException("This method can't handle objects of type: " + obj.getClass(), e);
}
}
return paramArray;
}
No ugly instanceof, switch statements, or visitor pattern required! :)
Java looks at the reference type when trying to determine which method to call. If you want to force your code you choose the 'right' method, you can declare your fields as instances of the specific type:
Integeri = new Integer(12);
String s = "foobar";
Object o = new Object();
You could also cast your params as the type of the param:
callee.foo(i);
callee.foo((String)s);
callee.foo(((Integer)o);
If there is an exact match between the number and types of arguments specified in the method call and the method signature of an overloaded method then that is the method that will be invoked. You are using Object references, so java decides at compile time that for Object param, there is a method which accepts directly Object. So it called that method 3 times.
I'm experimenting with this code:
interface Callee {
public void foo(Object o);
public void foo(String s);
public void foo(Integer i);
}
class CalleeImpl implements Callee
public void foo(Object o) {
logger.debug("foo(Object o)");
}
public void foo(String s) {
logger.debug("foo(\"" + s + "\")");
}
public void foo(Integer i) {
logger.debug("foo(" + i + ")");
}
}
Callee callee = new CalleeImpl();
Object i = new Integer(12);
Object s = "foobar";
Object o = new Object();
callee.foo(i);
callee.foo(s);
callee.foo(o);
This prints foo(Object o) three times. I expect the method selection to take in consideration the real (not the declared) parameter type. Am I missing something? Is there a way to modify this code so that it'll print foo(12), foo("foobar") and foo(Object o)?
I expect the method selection to take
in consideration the real (not the
declared) parameter type. Am I missing
something?
Yes. Your expectation is wrong. In Java, dynamic method dispatch happens only for the object the method is called on, not for the parameter types of overloaded methods.
Citing the Java Language Specification:
When a method is invoked (§15.12), the
number of actual arguments (and any
explicit type arguments) and the
compile-time types of the arguments
are used, at compile time, to
determine the signature of the method
that will be invoked (§15.12.2). If
the method that is to be invoked is an
instance method, the actual method to
be invoked will be determined at run
time, using dynamic method lookup
(§15.12.4).
As mentioned before overloading resolution is performed at compile time.
Java Puzzlers has a nice example for that:
Puzzle 46: The Case of the Confusing Constructor
This puzzle presents you with two Confusing constructors. The main method invokes a constructor,
but which one? The program's output depends on the answer. What does the program print, or is it
even legal?
public class Confusing {
private Confusing(Object o) {
System.out.println("Object");
}
private Confusing(double[] dArray) {
System.out.println("double array");
}
public static void main(String[] args) {
new Confusing(null);
}
}
Solution 46: Case of the Confusing Constructor
...
Java's overload resolution process operates in two phases. The first phase selects all the methods or constructors that are accessible and applicable. The second phase selects the most specific of the methods or constructors selected in the first phase. One method or constructor is less specific than another if it can accept any parameters passed to the other [JLS 15.12.2.5].
In our program, both constructors are accessible and applicable. The constructor
Confusing(Object) accepts any parameter passed to Confusing(double[]), so
Confusing(Object) is less specific. (Every double array is an Object, but not every Object is a double array.) The most specific constructor is therefore Confusing(double[]), which explains the program's output.
This behavior makes sense if you pass a value of type double[]; it is counterintuitive if you pass null. The key to understanding this puzzle is that the test for which method or constructor is most specific does not use the actual parameters: the parameters appearing in the invocation.
They are used only to determine which overloadings are applicable. Once the compiler determines which overloadings are applicable and accessible, it selects the most specific overloading, using only the formal parameters: the parameters appearing in the declaration.
To invoke the Confusing(Object) constructor with a null parameter, write new
Confusing((Object)null). This ensures that only Confusing(Object) is applicable. More
generally, to force the compiler to select a specific overloading, cast actual parameters to the declared types of the formal parameters.
Ability to dispatch a call to a method based on types of arguments is called multiple dispatch. In Java this is done with Visitor pattern.
However, since you're dealing with Integers and Strings, you cannot easily incorporate this pattern (you just cannot modify these classes). Thus, a giant switch on object run-time will be your weapon of choice.
In Java the method to call (as in which method signature to use) is determined at compile time, so it goes with the compile time type.
The typical pattern for working around this is to check the object type in the method with the Object signature and delegate to the method with a cast.
public void foo(Object o) {
if (o instanceof String) foo((String) o);
if (o instanceof Integer) foo((Integer) o);
logger.debug("foo(Object o)");
}
If you have many types and this is unmanageable, then method overloading is probably not the right approach, rather the public method should just take Object and implement some kind of strategy pattern to delegate the appropriate handling per object type.
I had a similar issue with calling the right constructor of a class called "Parameter" that could take several basic Java types such as String, Integer, Boolean, Long, etc. Given an array of Objects, I want to convert them into an array of my Parameter objects by calling the most-specific constructor for each Object in the input array. I also wanted to define the constructor Parameter(Object o) that would throw an IllegalArgumentException. I of course found this method being invoked for every Object in my array.
The solution I used was to look up the constructor via reflection...
public Parameter[] convertObjectsToParameters(Object[] objArray) {
Parameter[] paramArray = new Parameter[objArray.length];
int i = 0;
for (Object obj : objArray) {
try {
Constructor<Parameter> cons = Parameter.class.getConstructor(obj.getClass());
paramArray[i++] = cons.newInstance(obj);
} catch (Exception e) {
throw new IllegalArgumentException("This method can't handle objects of type: " + obj.getClass(), e);
}
}
return paramArray;
}
No ugly instanceof, switch statements, or visitor pattern required! :)
Java looks at the reference type when trying to determine which method to call. If you want to force your code you choose the 'right' method, you can declare your fields as instances of the specific type:
Integeri = new Integer(12);
String s = "foobar";
Object o = new Object();
You could also cast your params as the type of the param:
callee.foo(i);
callee.foo((String)s);
callee.foo(((Integer)o);
If there is an exact match between the number and types of arguments specified in the method call and the method signature of an overloaded method then that is the method that will be invoked. You are using Object references, so java decides at compile time that for Object param, there is a method which accepts directly Object. So it called that method 3 times.
I was wondering if there is a way to pass a function as an argument to a method. Is there anyway I can do this in Java 8? Please thoroughly explain the steps I must take in order to do so, and thank you in advance.
Yes. Look at the java.util.function package for the different types of functional interfaces you can use.
What you might want is Function<T, R> which is a signature for a function that takes in a single argument of type T and returns a value of type R. There are other interfaces for more-specific cases. For example, let's say you wanted a predicate of some kind. Then you can use Predicate<T> to describe that you want to accept a method that returns a boolean value based on some interpretation of the value of type T.
This is how a lot of the methods work on streams. For example, the forEach in Stream<T> is a method that accepts an argument of type Consumer<? super T>. This is basically a function that takes in an argument and does something with it.
As far as passing in the functions themselves, you can use method references or create an ad-hoc implementation of a functional interface through lambdas.
Here's a contrived example where I'm iterating over a map and adding all the values to a list:
List<Integer> list = new ArrayList<>();
Map<String, Integer> map = fromSomeMethod();
map.values().stream().forEach(list::add);
Here forEach accepts a consumer function of type Consumer<? super T>, which in this case is the add method from Collection<E> (which List<T> implements). Therefore, you've basically passed in a method as an argument into another method.
Here's another example where I'm using the same method, but this time I'm printing out the elements of a list:
list.stream().forEach(System.out::println);
Using this you can create your own methods that accept other methods, and it is as simple as defining an argument that is of any one of the types defined in java.util.function. For your error callback case, you could do something like this:
public void doSomething(String something, Consumer<ErrorResult> errorHandler) {
//do some stuff
if(errorHappened) {
//call the error handler with a new ErrorResult object
errorHandler.accept(new ErrorResult(...));
}
}
Then let's say you have a method that simply prints out the error result in some class
public class ConsoleErrorHandler {
public void handleError(ErrorResult result) {
System.out.println(result.getErrorMessage());
}
}
Now you can invoke doSomething with a reference to handleError from an instance of ConsoleErrorHandler:
ConsoleErrorHandler handler = new ConsoleErrorHandler();
doSomething("Something", handler::handleError);
You could even do this ad-hoc, with a lambda:
doSomething("Something", (ErrorResult result) -> {
System.out.println(result.getErrorMessage());
});
Notice that because of generics, you get compile-time type-checking so that you cannot simply pass any method that accepts a single argument into doSomething.
There's not function types in Java, so you have to use single method interfaces.
The package java.util.function defines a lot of them, but any interface with a single method can be used.
For instance:
// An interface with a single method that return
// something of type T
interface F<T> {
T doSomething();
}
class A {
// This method expect an instance of the interface F
private static String f( F<String> x ) {
// and then invokes its only method.
return x.doSomething();
}
// Test it
public static void main( String ... args ) {
//Call the method f using a
// this function literal: ()-> "hola"
System.out.println(
f( () -> "hola" )
);
}
}
The function literal:
() -> "hola"
Satisfies the interface F<T> implicitly.
In summary, you specify the type as an interface with a single method (any interface). It's even better if you use one of the existing interfaces in the java.util.function package.
I hope this helps.