Mam pytanie dotyczące zastosowania Function.identity()
metody.
Wyobraź sobie następujący kod:
Arrays.asList("a", "b", "c")
.stream()
.map(Function.identity()) // <- This,
.map(str -> str) // <- is the same as this.
.collect(Collectors.toMap(
Function.identity(), // <-- And this,
str -> str)); // <-- is the same as this.
Czy jest jakiś powód, dla którego powinieneś używać Function.identity()
zamiast str->str
(lub odwrotnie). Myślę, że druga opcja jest bardziej czytelna (oczywiście kwestia gustu). Ale czy istnieje jakiś „prawdziwy” powód, dla którego należy być preferowanym?
java
lambda
java-8
java-stream
Przemysław Głębocki
źródło
źródło
t -> t
po prostu dlatego, że jest bardziej zwięzły.Odpowiedzi:
Począwszy od bieżącej implementacji JRE,
Function.identity()
zawsze zwróci tę samą instancję, a każde wystąpienieidentifier -> identifier
spowoduje nie tylko utworzenie własnej instancji, ale nawet odrębną klasę implementacji. Aby uzyskać więcej informacji, zobacz tutaj .Powodem jest to, że kompilator generuje syntetyczną metodę z trywialnym ciałem tego wyrażenia lambda (w przypadku
x->x
równoważnikareturn identifier;
) i informuje środowisko wykonawcze, aby utworzyło implementację funkcjonalnego interfejsu wywołującego tę metodę. Dlatego środowisko wykonawcze widzi tylko różne metody docelowe, a bieżąca implementacja nie analizuje metod w celu ustalenia, czy niektóre metody są równoważne.Więc użycie
Function.identity()
zamiastx -> x
może zaoszczędzić trochę pamięci, ale nie powinno to decydować o twojej decyzji, jeśli naprawdę uważasz, żex -> x
jest to bardziej czytelne niżFunction.identity()
.Możesz również wziąć pod uwagę, że podczas kompilacji z włączonymi informacjami debugowania metoda syntetyczna będzie miała atrybut debugowania linii wskazujący na linie kodu źródłowego zawierające wyrażenie lambda, dlatego masz szansę znaleźć źródło konkretnej
Function
instancji podczas debugowania . Natomiast podczas napotkania instancji zwróconejFunction.identity()
podczas debugowania operacji nie wiadomo, kto wywołał tę metodę i przekazał instancję do operacji.źródło
x -> x
ramki. Czy sugerujesz ustawić punkt przerwania na tej lambda? Zwykle nie jest tak łatwo umieścić punkt przerwania w lambda o jednym wyrażeniu (przynajmniej w Eclipse) ...Function.identity()
tych informacji zostanie utracone. Następnie łańcuch połączeń może pomóc w prostych przypadkach, ale pomyśl o np. Ocenie wielowątkowej, w której oryginalny inicjator nie znajduje się w stosie…new
.new Foo(…)
gwarantuje utworzenie nowej instancji dokładnie tego samego typuFoo
, podczas gdyFoo.getInstance(…)
może zwrócić istniejącą instancję (podtyp)Foo
…W twoim przykładzie nie ma dużej różnicy między,
str -> str
aFunction.identity()
wewnętrznie jest to po prostut->t
.Ale czasami nie możemy użyć,
Function.identity
ponieważ nie możemy użyćFunction
. Spójrz tutaj:to dobrze się skompiluje
ale jeśli spróbujesz skompilować
pojawi się błąd kompilacji, ponieważ
mapToInt
oczekujeToIntFunction
, że nie jest to związane zFunction
. RównieżToIntFunction
nie maidentity()
metody.źródło
i -> i
goFunction.identity()
spowoduje błąd kompilatora.mapToInt(Integer::intValue)
.mapToInt(i -> i)
jest to uproszczeniemapToInt( (Integer i) -> i.intValue())
. Użyj dowolnej wersji, która Twoim zdaniem jest bardziej przejrzysta, dla mniemapToInt(i -> i)
lepiej pokazuje intencje tego kodu.i -> i
wygląda jak funkcja tożsamości, czego nie ma w tym przypadku.i -> i
ponieważ moim celem jest zmapowanie liczby całkowitej na int (comapToInt
całkiem ładnie sugeruje), aby nie wywoływaćintValue()
metody jawnie . Jak to mapowanie zostanie osiągnięte, tak naprawdę nie jest tak ważne. Więc po prostu zgódźmy się nie zgodzić, ale dzięki za wskazanie możliwej różnicy w wydajności, będę musiał kiedyś przyjrzeć się temu bliżej.Ze źródła JDK :
Więc nie, pod warunkiem, że jest poprawny pod względem składniowym.
źródło
t->t
w kodzie źródłowym może stworzyć jeden obiekt, a implementacjaFunction.identity()
jest jednym wystąpieniem. Tak więc wszystkie strony wywołująceidentity()
będą współużytkować ten jeden obiekt, podczas gdy wszystkie witryny jawnie korzystające z wyrażenia lambdat->t
utworzą własny obiekt. MetodaFunction.identity()
ta nie jest w żaden sposób wyjątkowa, ilekroć utworzysz metodę fabryczną, w której zawarte jest powszechnie używane wyrażenie lambda i wywołasz tę metodę zamiast powtarzania wyrażenia lambda, możesz zaoszczędzić trochę pamięci, biorąc pod uwagę bieżącą implementację .t->t
obiektu za każdym razem, gdy wywoływana jest metoda, i przetwarza ten sam obiekt za każdym razem, gdy wywoływana jest metoda?invokedynamic
instrukcję, która zostaje połączona przy pierwszym uruchomieniu przez wykonanie tak zwanej metody bootstrap, która w przypadku wyrażeń lambda znajduje się w plikuLambdaMetafactory
. Ta implementacja decyduje o zwróceniu uchwytu do konstruktora, metody fabrycznej lub kodu zawsze zwracającego ten sam obiekt. Może również zdecydować o zwróceniu łącza do już istniejącego uchwytu (co obecnie się nie dzieje).