Java 8 Lambdas, Function.Identity() or T->T

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



Leave a reply



Submit