when to use Identity function in Java?
It's useful when the API forces you to pass a function, but all you want is to keep the original value.
For example, let's say you have a Stream<Country>
, and you want to make turn it into a Map<String, Country>
. You would use
stream.collect(Collectors.toMap(Country::getIsoCode, Function.identity()))
Collectors.toMap()
expects a function to turn the Country into a key of the map, and another function to turn it into a value of the map. Since you want to store the country itself as value, you use the identity function.
In Java why is Function.identity() a static method instead of something else?
TL;DR Using Function.identity()
creates only one object, so it's very memory efficient.
Third implementation doesn't compile, because T
is undefined, so that's not an option.
In second implementation, every time you write Function::identity
a new object instance is created.
In first implementation, whenever you call Function.identity()
, an instance to the same lambda object is returned.
It is simple to see for yourself. Start by creating the two identity
methods in the same class, so rename them to identity1
and identity2
to keep them separately identifiable.
static <T> Function<T, T> identity1() {
return t -> t;
}
static <T> T identity2(T in) {
return in;
}
Write a test
method that accepts a Function
and prints the object, so we can see it's unique identity, as reflected by the hash code.
static <A, B> void test(Function<A, B> func) {
System.out.println(func);
}
Call the test
method repeatedly to see if each one gets a new object instance or not (my code is in a class named Test
).
test(Test.identity1());
test(Test.identity1());
test(Test.identity1());
test(Test::identity2);
test(Test::identity2);
for (int i = 0; i < 3; i++)
test(Test::identity2);
Output
Test$$Lambda$1/0x0000000800ba0840@7adf9f5f
Test$$Lambda$1/0x0000000800ba0840@7adf9f5f
Test$$Lambda$1/0x0000000800ba0840@7adf9f5f
Test$$Lambda$2/0x0000000800ba1040@5674cd4d
Test$$Lambda$3/0x0000000800ba1440@65b54208
Test$$Lambda$4/0x0000000800ba1840@6b884d57
Test$$Lambda$4/0x0000000800ba1840@6b884d57
Test$$Lambda$4/0x0000000800ba1840@6b884d57
As you can see, multiple statements calling Test.identity1()
all get the same object, but multiple statements using Test::identity2
all get different objects.
It is true that repeated executions of the same statement gets the same object (as seen in result from the loop), but that's different from result obtained from different statements.
Conclusion: Using Test.identity1()
creates only one object, so it's more memory efficient than using Test::identity2
.
Why can't I replace (t - t) with Function.identity() in the argument to Optional.filter?
Summarising the comments, I can answer my own question.
Predicate<T>
is unrelated to Function<T, Boolean>
, and they can't be used in each other's place, even if it would often appear to make logical sense.
Predicate<T>
likely exists because there are times when you might want to compose predicates using the methods: and
, or
and negate
, and these methods just wouldn't makes sense on Function<T, U>
. Java does not permit methods to be defined only for a subset of the possible generic parameters, so predicates are more ergonomically modeled by Predicate<T>
rather than Function<T, Boolean>
for common use-cases.
Another reason for having a separate interface for predicates is that Java does not permit primitives in generic parameters. That means you cannot define a predicate as Function<T, boolean>
— it would have to be Function<T, Boolean>
, so a caller of the predicate would have to handle the possibility of null
return values. Using a specialized interface allows the return value of test
to be a boolean
instead and allows callers to assume the result is never null
.
A possible replacement in my original use case is Boolean::booleanValue
, which can be used where either Predicate<Boolean>
or Function<Boolean, Boolean>
is required.
Java8: Using Function::identity in Collectors.toMap(..) creates an argument mismatch error
You should not use a method reference. You should use Function.identity()
.
If you use a method reference, you're basically trying to pass a Supplier<Function<T, T>>
.
Map<String, Person> map = list.stream().collect(
Collectors.toMap(Person::getId, Function.identity())
);
Compiler issue with IntConsumer returns Function.identity().apply()
z
is always an expression.
Function.identity().apply(z)
is an expression statement. It can be used as an expression since it returns a value. But it's interpreted as a statement as long as the value is being ignored.
An expression statement is executed by evaluating the expression; if the expression has a value, the value is discarded.
https://docs.oracle.com/javase/specs/jls/se12/html/jls-14.html#jls-14.8
IntConsumer#accept(int value)
expects void
to be returned. In case of a single-line lambda body, it must be a statement.
Compare
// not allowed - expressions
IntConsumer a = (i) -> 2 + 2;
IntConsumer b = (i) -> true ? 1 : 0;
IntConsumer c = (i) -> (Function.identity().apply(i));
IntConsumer d = (i) -> (System.out.println(i));
IntConsumer e = (i) -> (new Object());
// allowed - expression statements
IntConsumer f = (i) -> Function.identity().apply(i);
IntConsumer g = (i) -> System.out.println(i);
IntConsumer h = (i) -> new Object();
object calls itself using lambda, confused
Roughly speaking, it's very similar to e -> e
, or Function.identity()
.
As the JLS put it,
15.27.4. Run-Time Evaluation of Lambda Expressions
At run time, evaluation of a lambda expression is similar to evaluation of a class instance creation expression, insofar as normal completion produces a reference to an object. Evaluation of a lambda expression is distinct from execution of the lambda body.
It doesn't "call itself", it's a lambda that takes an object and returns that object without any intermediate action.
Java 8 chain lambdas
Your question reminds me of the lambda expression builder pattern. I would introduce a new interface to do the is().then()
operation and add it to a Map
in the TypeChecker
like:
class Matcher<T> {
private final Map<Class<?>, UnaryOperator> mapping = new HashMap<>();
private final T value;
private Matcher(T value) {
this.value = value;
}
public static <T> Matcher<T> when(T v) {
return new Matcher<>(v);
}
public <S> Condition<S> is(Class<S> clazz) {
return callback -> {
mapping.put(clazz, callback);
return this;
};
}
public void execute() {
mapping.forEach((key, val) -> {
if (key.isInstance(value)) {
val.apply(value);
}
});
}
interface Condition<S> {
Matcher<?> then(UnaryOperator<? super S> callback);
}
}
Easily tested with and should produce your desired results:
Matcher.when(123).is(String.class).then(s -> {
System.out.println("STRING");
return s;
})
.is(Integer.class).then(i -> {
System.out.println("INTEGER");
return i;
}).execute();
Related Topics
Java8 Lambdas VS Anonymous Classes
How to Fix the "Java.Security.Cert.Certificateexception: No Subject Alternative Names Present" Error
Java List.Add() Unsupportedoperationexception
Keyword for the Outer Class from an Anonymous Inner Class
Simpledateformat and Locale Based Format String
How to Parse a JSON and Turn Its Values into an Array
Border with Rounded Corners & Transparency
How to Query Xml Using Namespaces in Java with Xpath
Convenient Way to Parse Incoming Multipart/Form-Data Parameters in a Servlet
How to Restart a Java Application
Converting Symbols, Accent Letters to English Alphabet
Using Heapdumponoutofmemoryerror Parameter for Heap Dump for Jboss
Rotating Coordinate Plane for Data and Text in Java
How to Use Jaroutputstream to Create a Jar File
Java, Calculate the Number of Days Between Two Dates
How to Read and Copy the Http Servlet Response Output Stream Content for Logging
Spring Resttemplate - How to Enable Full Debugging/Logging of Requests/Responses