Java 모범 사례에서 가능할 때마다 Lambda 표현식을 사용하고 있습니까?


52

최근에 Java 8에 도입 된 Lambda 표현식을 마스터했습니다. 기능적 인터페이스를 사용할 때마다 항상 기능적 인터페이스를 구현하는 클래스를 작성하는 대신 Lambda 표현식을 사용하는 경향이 있습니다.

이것이 좋은 습관으로 간주됩니까? 또는 기능 인터페이스에 Lambda를 사용하는 것이 적절하지 않은 상황입니까?


122
"가능할 때마다 기술 X를 사용하고 있습니까?"라는 질문에 대한 유일한 대답은 "아니오, 가능할 때마다가 아니라 현명 할 때마다 기술 X를 사용하는 것" 입니다.
Doc Brown

람다 (익명)를 사용할 때와 다른 종류의 기능 구현 (예 : 메소드 참조)을 사용하는시기는 정말 흥미로운 질문입니다. 나는 며칠 전에 Josh Bloch를 만날 기회가 있었는데 이것이 그가 이야기 한 요점 중 하나였습니다. 그가 말한 바에 따르면 이 정확한 질문을 다루는 다음 버전의 Effective Java에 새 항목을 추가 할 계획이라는 것을 알고 있습니다 .
Daniel Pryden

@DocBrown 댓글이 아닌 답변이어야합니다.
gnasher729

1
@ gnasher729 : 확실히 아닙니다.
Doc Brown

답변:


116

람다를 사용하지 않도록 고려해야 할 여러 가지 기준이 있습니다.

  • 크기 람다가 클수록 람다를 둘러싼 논리를 따르기가 더 어려워집니다.
  • 반복 반복 되는 로직에 대해 명명 된 함수를 작성하는 것이 좋습니다. 그러나 아주 간단한 람다를 반복해도 괜찮습니다.
  • 명명 좋은 의미 론적 이름을 생각할 수 있다면, 코드에 명확성을 더해 주므로 대신 사용해야합니다. 같은 이름을 말하고 있지 않습니다 priceIsOver100. x -> x.price > 100그 이름만큼이나 분명합니다. 나는 그런 이름 isEligibleVoter이 긴 조건 목록을 대체 한다는 것을 의미합니다 .
  • 중첩 중첩 람다는 실제로 읽기가 매우 어렵습니다.

배 밖으로 가지 마십시오. 소프트웨어는 쉽게 변경됩니다. 확실치 않은 경우에는 두 가지 방법 모두를 쓰고 어느 것이 더 읽기 쉬운 지보십시오.


1
람다는 소량의 데이터 처리에서 비효율적이므로 람다를 통해 처리 할 데이터의 양을 고려해야합니다. 그들은 큰 데이터 세트의 병렬화에서 실제로 빛을 발합니다.
CraigR8806

1
@ CraigR8806 여기에서는 성능이 중요하지 않다고 생각합니다. 익명 함수를 사용하거나 기능 인터페이스를 확장하는 클래스를 작성하는 것은 성능면에서 동일해야합니다. 명령형 스타일 루프를 통해 스트림 / 고차 함수 API를 사용하는 것에 대해 이야기 할 때 성능 고려 사항이 나오지만,이 질문에서는 두 가지 경우 모두 다른 구문으로 기능적 인터페이스를 사용하고 있습니다.
puhlen

17
"간단히 퍼져있는 아주 간단한 람다를 반복해도 괜찮습니다."반드시 서로 바뀌지 않는 경우에만. 코드가 정확하기 위해 모두 동일한 논리 여야 하는 경우 한 곳에서만 변경해야하도록 실제로 모두 동일한 방법으로 만들어야합니다.
jpmc26

전반적으로 이것은 정말 좋은 대답입니다. 람다의 대안으로 메소드 참조를 사용한다고 언급하면이 답변에서 볼 수있는 구멍에만 패치됩니다.
Morgen

3
확실하지 않은 경우, 두 가지 방법으로 작성하고 읽기 쉬운 코드를 유지 관리하는 사람에게 물어보십시오.
corsiKa

14

따라 다릅니다. 다른 장소에서 동일한 람다를 사용하는 것을 발견 할 때마다 인터페이스를 구현하는 클래스 구현을 고려해야합니다. 그러나 익명의 내부 클래스를 사용했다면 람다가 훨씬 낫다고 생각합니다.


6
메소드 참조를 사용할 가능성을 잊지 마십시오! 오라클은 정확한 이유로 정적 메소드와 기본 메소드를 추가했습니다 (Java 9에서 훨씬 더 추가).
Jörg W Mittag

13

Karl Bielefeldt의 답변을 지원하지만 간단한 추가 내용을 제공하고 싶습니다.

  • 디버깅 람다의 내부 영역 일부 IDE의 투쟁 및 람다의 컨텍스트 내부 멤버 변수를 표시 고투. 바라건대이 상황이 줄을 바꿀 것이지만, 람다로 흩어져있을 때 다른 사람의 코드를 유지하는 것은 성 가실 수 있습니다.

.NET의 경우에도 마찬가지입니다. 필자가 람다를 피할 필요는 없지만 반드시 다른 것을하면 죄책감을 느끼지 않습니다.
user1172763

3
이 경우 잘못된 IDE를 사용하고 있습니다. IntelliJ와 Netbeans는 특정 영역에서 꽤 잘 작동합니다.
David Foerster

1
@ user1172763 Visual Studio에서 멤버 변수를 보려면 람다 컨텍스트에 도달 할 때까지 호출 스택을 이동할 수 있다는 것을 알았습니다.
Zev Spitz

6

엔 클로징 범위의 로컬 변수에 액세스

허용되는 칼 Bielefeldt 의해 답변 올바른 것입니다. 하나 더 구별 할 수 있습니다.

  • 범위

클래스 내부의 메소드 안에 중첩 된 람다 코드 는 해당 메소드 및 클래스에서 발견 된 효과적으로 최종 변수에 액세스 할 수 있습니다 .

기능 인터페이스를 구현하는 클래스를 작성한다고해서 호출 코드 상태에 직접 액세스 할 수있는 것은 아닙니다.

Java Tutorial인용 하려면 (강조 광산) :

로컬 및 익명 클래스와 마찬가지로 람다 식은 변수를 캡처 할 수 있습니다. 그것들은 둘러싸는 범위의 지역 변수에 동일한 접근 권한을 갖습니다 . 그러나 로컬 및 익명 클래스와 달리 람다 식에는 그림자 문제가 없습니다 (자세한 내용은 그림자 참조). 람다 식의 범위는 어휘 범위입니다. 즉, 상위 유형에서 이름을 상속받지 않거나 새로운 범위의 범위를 도입합니다. 람다 식의 선언은 둘러싼 환경에서와 마찬가지로 해석됩니다.

따라서 긴 코드를 가져와 이름을 지정하면 이점이 있지만 엔 클로징 방법 및 클래스의 상태에 직접 액세스 할 수있는 단순성과 비교하여 무게를 측정해야합니다.

보다:


4

이것은 nit-picking 일 수 있지만 다른 답변에서 만든 다른 모든 훌륭한 점에는 다음을 추가합니다.

가능하면 방법 참조를 선호 하십시오. 비교:

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);

employees.stream()
         .map(employee -> employee.getName())
         .forEach(employeeName -> System.out.println(employeeName));

방법 참조를 사용하면 일반적으로 중복 및 / 또는 같은 게으른 이름으로 인도하는 람다의 주장 (들)의 이름을 할 필요가 저장 e또는 x.


0

Matt McHenry의 답변을 바탕으로 람다는 일련의 메서드 참조로 다시 작성할 수있는 람다와 메서드 참조간에 관계가있을 수 있습니다. 예를 들면 다음과 같습니다.

employees.stream()
         .forEach(employeeName -> System.out.println(employee.getName()));

vs

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.