What's the difference between instance method reference types in Java 8?
myString::charAt
would take anint
and return achar
, and might be used for any lambda that works that way. It translates, essentially, toindex -> myString.charAt(index)
.String::length
would take aString
and return anint
. It translates, essentially, tostring -> string.length()
.String::charAt
would translate to(string, index) -> string.charAt(index)
.
Please Explain Java 8 Method Reference to instance Method using class name
Equivalent lambda expression of HighTemp::lessThanTemp
is
(highTemp1, highTemp2) -> {
return highTemp1.lessThanTemp(highTemp2);
}
This is one of the features of Java8
named Reference to an Instance Method of an Arbitrary Object of a Particular Type
Consider following example,
interface FIface<T> {
int testMethod(T a, T b);
}
class Test2 {
private String str;
Test2(String str) {
this.str = str;
}
int ok(Test2 test2) {
System.out.println("Currnet String : "+ this.str);//Refer to t1
System.out.println("Test String : "+test2.str);//Refer to t2
return 0;
}
}
public class Test {
public static <T> int checkCall(T t1, T t2, FIface<T> fiFace) {
//Here Test2 :: ok is equivalent to t1.ok(t2)
return fiFace.testMethod(t1, t2);
}
public static void main(String[] args) {
checkCall(new Test2("a"), new Test2("b"), Test2 :: ok);
}
}
OUTPUT
Currnet String : a
Test String : b
Note here that Test2 :: ok
is valid for the call even ok
method is not static.
When you call the method checkCall
for the functional interface you still have two arguments which are t1
and t2
and for that valid lambda expression can have parameters as (Test t1, Test t2)
so your method Test2 :: ok
here becomes valid for the call. Internally it works this way t1.ok(t2)
.
So, fiFace.testMethod(t1, t2);
will will invoke method as t1.ok(t2)
Difference between normal instantiation and instantiation including method reference
Method references only comes in four kinds (See "Kinds of method references" in this page):
- References to a static method
- References to an instance method of a particular object
- Reference to an instance method of an arbitrary object of a particular type
- Reference to a constructor
There isn't a kind of method reference that "creates a new object every time", whatever that means. new SomeClass()::methodName
is of the second kind. It refers to the method methodName
of one particular object. That object is a newly created SomeClass
object. It doesn't create any more SomeClass
objects when the method is called, because it is a reference to someMethod
of that particular SomeClass
object that you newly created.
The lambda expression, on the other hand, creates a new SomeClass
every time it is called, because new SomeClass()
is inside the { ... }
.
How is method reference syntax working in Java 8
What is a method refernce
You can see a method reference as a lambda expression, that call an existing method.
Kinds of method references
There are four kinds of method references:
- 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
How to understand it?
The following expression that lists all hidden files:f.listFiles(p -> p.isHidden());
This expression is composed by the instance method listFiles(FileFilter)
and your lambda expression p -> p.isHidden()
that is not a anonymous method but an existing instance method of the class File
.
Note that FileFilter
is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference. Hence, you can write your expression f.listFiles(File::isHidden);
Side notes
- You don't need the parentheses surrounding the
p
. For a better readibility, I would suggest to replace(p)
with simply ap
. Hence, your lambda expression will becomep-> p.isHidden()
. - Your for loop can be replaced by an enhanced for loop:
for (File value : hidden) {
System.out.println(value);
}
Documentation:
Method reference
FileFilter
Method reference for static and instance methods
Why it is not allowing AppTest::makeUppercase
?
The short answer is that AppTest::makeUppercase
isn't valid "reference to an instance method of an arbitrary object of a particular type".AppTest::makeUppercase
must implement interface Function<AppTest, String>
to be valid reference.
Details:
There are 4 kinds of method references in Java:
ContainingClass::staticMethodName
- reference to a static methodcontainingObject::instanceMethodName
- reference to an instance method of a particular objectContainingType::methodName
- reference to an instance method of an arbitrary object of a particular typeClassName::new
- reference to a constructor
Every single kind of method reference requires corresponding Function
interface implementation.
You use as a parameter the reference to an instance method of an arbitrary object of a particular type.
This kind of method reference has no explicit parameter variable in a method reference and requires implementation of the interface Function<ContainingType, String>
. In other words, the type of the left operand has to be AppTest
to make AppTest::makeUppercase
compilable. String::toUpperCase
works properly because the type of parameter and type of the instance are the same - String
.
import static java.lang.System.out;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
class ReferenceSource {
private String value;
public ReferenceSource() {
}
public ReferenceSource(String value) {
this.value = value;
}
public String doInstanceMethodOfParticularObject(final String value) {
return ReferenceSource.toUpperCase(value);
}
public static String doStaticMethod(final String value) {
return ReferenceSource.toUpperCase(value);
}
public String doInstanceMethodOfArbitraryObjectOfParticularType() {
return ReferenceSource.toUpperCase(this.value);
}
private static String toUpperCase(final String value) {
return Optional.ofNullable(value).map(String::toUpperCase).orElse("");
}
}
public class Main {
public static void main(String... args) {
// #1 Ref. to a constructor
final Supplier<ReferenceSource> refConstructor = ReferenceSource::new;
final Function<String, ReferenceSource> refParameterizedConstructor = value -> new ReferenceSource(value);
final ReferenceSource methodReferenceInstance = refConstructor.get();
// #2 Ref. to an instance method of a particular object
final UnaryOperator<String> refInstanceMethodOfParticularObject = methodReferenceInstance::doInstanceMethodOfParticularObject;
// #3 Ref. to a static method
final UnaryOperator<String> refStaticMethod = ReferenceSource::doStaticMethod;
// #4 Ref. to an instance method of an arbitrary object of a particular type
final Function<ReferenceSource, String> refInstanceMethodOfArbitraryObjectOfParticularType = ReferenceSource::doInstanceMethodOfArbitraryObjectOfParticularType;
Arrays.stream(new String[] { "a", "b", "c" }).map(refInstanceMethodOfParticularObject).forEach(out::print);
Arrays.stream(new String[] { "d", "e", "f" }).map(refStaticMethod).forEach(out::print);
Arrays.stream(new String[] { "g", "h", "i" }).map(refParameterizedConstructor).map(refInstanceMethodOfArbitraryObjectOfParticularType)
.forEach(out::print);
}
}
Additionally, you could take a look at this and that thread.
Java 8: Difference between method reference Bound Receiver and UnBound Receiver
The idea of the unBound receiver such as String::length
is you're referring to a
method of an object that will be supplied as one of the lambda's parameters. For example,
the lambda expression (String s) -> s.toUpperCase()
can be rewritten as String::toUpperCase
.
But Bounded refers to a situation when you’re calling a method in a
lambda to an external object that already exists. For example, the lambda expression () -> expensiveTransaction.getValue()
can be rewritten as expensiveTransaction::getValue
.
Situations for three different ways of method reference
(args) -> ClassName.staticMethod(args)
can be ClassName::staticMethod
// This is static (you can think as unBound also)
(arg0, rest) -> arg0.instanceMethod(rest)
can be ClassName::instanceMethod
(arg0
is of type ClassName
) // This is unBound
(args) -> expr.instanceMethod(args)
can be expr::instanceMethod
// This is Bound
Answer retrieved from Java 8 in Action book
Confused with using method reference in Comparator.comparing()
In class Movie
, getTitle()
is an instance method. It therefore takes one argument, namely an argument of type Movie
(the implicit parameter). To call method getTitle()
, you need a target object:
movie.getTitle();
Here the object referenced by movie
is that one argument.
The function type of getTitle()
is thus Movie -> String
, which matches the expected functional interface.
Java 8 reference to a static method vs. instance method
In your example, both the static and the non-static method are applicable for the target type of the filter method. In this case, you can't use a method reference, because the ambiguity can not be resolved. See §15.13.1 Compile-Time Declaration of a Method Reference for details, in particular the following quote and the examples below:
If the first search produces a static method, and no non-static method is applicable [..], then the compile-time declaration is the result of the first search. Otherwise, if no static method is applicable [..], and the second search produces a non-static method, then the compile-time declaration is the result of the second search. Otherwise, there is no compile-time declaration.
In this case, you can use a lambda expression instead of a method reference:
a.stream().filter(item -> A.is(item));
The above rule regarding the search for static and non-static methods is somewhat special, because it doesn't matter, which method is the better fit. Even if the static method would take an Object instead of A, it's still ambiguous. For that reason, I recommend as a general guideline: If there are several methods with the same name in a class (including methods inherited from base classes):
- All methods should have the same access modifiers,
- All methods should have the same final and abstract modifiers,
- And all methods should have the same static modifier
Related Topics
How to Convert/Parse from String to Char in Java
Passing Pointers Between C and Java Through Jni
How to Update an Entity Using Spring-Data-Jpa
Eclipse Optimize Imports to Include Static Imports
Should You Always Code to Interfaces in Java
Why Isn't a Qualified Static Final Variable Allowed in a Static Initialization Block
Java Local VS Instance Variable Access Speed
What Does "Atomic" Mean in Programming
Dealing with Randomly Generated and Inconsistent JSON Field/Key Names Using Gson
Why Isn't This Code Causing a Concurrentmodificationexception
Why Does My Spring Boot App Always Shutdown Immediately After Starting
What Java 8 Stream.Collect Equivalents Are Available in the Standard Kotlin Library
How to Set Java_Home in MAC Permanently
How to Serve Jsps from Inside a Jar in Lib, or Is There a Workaround
Can't a Swing Component Be Added to Multiple Containers