What's the Difference Between Instance Method Reference Types in Java 8

What's the difference between instance method reference types in Java 8?

  1. myString::charAt would take an int and return a char, and might be used for any lambda that works that way. It translates, essentially, to index -> myString.charAt(index).

  2. String::length would take a String and return an int. It translates, essentially, to string -> string.length().

  3. 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

  1. You don't need the parentheses surrounding the p. For a better readibility, I would suggest to replace (p) with simply a p. Hence, your lambda expression will become p-> p.isHidden().
  2. 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:

  1. ContainingClass::staticMethodName - reference to a static method
  2. containingObject::instanceMethodName - reference to an instance method of a particular object
  3. ContainingType::methodName - reference to an instance method of an arbitrary object of a particular type
  4. ClassName::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



Leave a reply



Submit