Lambda Expressions
Lambda 표현식은 Java 8에서 시작하여 Java 언어에 강력하게 추가되었습니다. 이것은 람다의 개념을 소개하면서 각 튜토리얼을 진행할 때 실제로 사용하는 방법을 점진적으로 가르치는 일련의 튜토리얼입니다.
이 시리즈의 자습서는 다음과 같습니다. 처음부터 시작하여 진행하는 것이 좋지만 원하는 곳에서 자유롭게 시작할 수 있습니다!
첫 번째 Lambda 표현 작성
2014 년 Java SE 8은 람다 표현식 개념을 도입했습니다. Java SE 8이 출시되기 전날을 기억한다면 익명의 클래스 개념을 기억할 것입니다. 그리고 아마도 람다 표현이 익명의 수업 사례를 작성하는 또 다른 간단한 방법이라고 들었을 것입니다.
그 시절을 기억하지 못하면 익명의 수업에 대해 듣거나 읽었을 수 있으며이 모호한 구문이 두렵습니다.
좋은 소식은 람다 표현을 작성하는 방법을 이해하기 위해 익명의 수업을 거치지 않아도된다는 것입니다. 또한 많은 경우 Java 언어에 람다를 추가하기 때문에 더 이상 익명 클래스가 필요하지 않습니다.
람다 표현을 작성하면 세 단계를 이해하는 것으로 분류됩니다:
- 쓰려는 람다 표현의 유형을 식별
- 올바른 구현 방법 찾기
- 이 방법을 구현합니다.
Lambda 표현식 유형 식별
모든 것은 Java 언어로 된 유형을 가지며이 유형은 컴파일시 알려져 있습니다. 따라서 항상 람다 식의 유형을 찾을 수 있습니다. 변수 유형, 필드, 메소드 매개 변수 또는 리턴 된 메소드 유형일 수 있습니다.
람다 식의 유형에는 제한이 있습니다. 기능적 인터페이스 여야합니다. 따라서 기능적 인터페이스를 구현하지 않는 익명 클래스는 람다 표현식으로 작성할 수 없습니다.
기능 인터페이스가 무엇인지에 대한 완전한 정의는 약간 복잡합니다. 이 시점에서 알아야 할 것은 기능 인터페이스가 하나만있는 인터페이스라는 것입니다 초록 방법.
Java SE 8부터는 인터페이스에서 구체적인 방법이 허용된다는 것을 알고 있어야합니다. 인스턴스 메소드가 될 수 있습니다 기본 방법, 정적 인 방법이 될 수 있습니다. 이 방법은 그렇지 않기 때문에 계산되지 않습니다 초록 방법.
주석을 추가해야합니까 @FunctionalInterface 기능적으로 만들기 위해 인터페이스에?
아뇨. 이 주석은 인터페이스가 실제로 작동하는지 확인하는 데 도움이됩니다. 기능 인터페이스가 아닌 유형에이 주석을 넣으면 컴파일러가 오류를 발생시킵니다.
기능적 인터페이스의 예
JDK API에서 가져온 몇 가지 예를 살펴 보겠습니다. 소스 코드에서 주석을 제거했습니다.
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
그만큼 Runnable 인터페이스는 하나의 추상적 인 방법 만 가지고 있기 때문에 실제로 기능적입니다. 그만큼 @FunctionalInterface 주석이 도우미로 추가되었지만 필요하지 않습니다.
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
// the body of this method has been removed
}
}
그만큼 Consumer<T> 인터페이스는 기능적입니다. 하나의 추상 방법과 계산되지 않는 기본적이고 구체적인 방법이 있습니다. 다시 한번 @FunctionalInterface 주석이 필요하지 않습니다.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
// the body of this method has been removed
}
default Predicate<T> negate() {
// the body of this method has been removed
}
default Predicate<T> or(Predicate<? super T> other) {
// the body of this method has been removed
}
static <T> Predicate<T> isEqual(Object targetRef) {
// the body of this method has been removed
}
static <T> Predicate<T> not(Predicate<? super T> target) {
// the body of this method has been removed
}
}
그만큼 Predicate<T> 인터페이스는 조금 더 복잡하지만 여전히 기능적인 인터페이스입니다:
- 하나의 추상적 인 방법이 있습니다
- 계산하지 않는 세 가지 기본 방법이 있습니다
- 그리고 둘 다 계산하지 않는 두 가지 정적 방법이 있습니다.
올바른 구현 방법 찾기
이 시점에서 작성해야 할 람다 표현의 유형을 식별했으며 좋은 소식은 다음과 같습니다. 가장 어려운 부분을 수행했습니다: 나머지는 매우 기계적이고 쉽게 할 수 있습니다.
람다 식은이 기능 인터페이스에서 유일한 추상 방법의 구현입니다. 따라서 올바른 구현 방법을 찾는 것은이 방법을 찾는 것입니다.
이전 단락의 세 가지 예에서 잠시 찾을 수 있습니다.
에 대한 Runnable 인터페이스:
public abstract void run();
에 대한 Predicate 인터페이스:
boolean test(T t);
그리고 Consumer<T> 인터페이스:
void accept(T t);
Lambda 표현식으로 올바른 방법 구현
구현 된 첫 번째 Lambda 표현 작성 Predicate<String>
이제 마지막 부분 : 람다 자체를 작성합니다. 당신이 이해해야 할 것은 당신이 쓰고있는 람다 표현은 당신이 찾은 추상적 방법의 구현이라는 것입니다. 람다 표현식 구문을 사용하면 코드에서이 구현을 멋지게 인라인 할 수 있습니다.
이 구문은 세 가지 요소로 구성됩니다:
- 파라미터 블록;
- 화살을 묘사 한 ASCII 예술의 작은 조각: ->. Java는 빈약 한 화살 (->) 뚱뚱한 화살 (=>);
- 방법의 본문 인 코드 블록.
이에 대한 예를 보자. 인스턴스가 필요하다고 가정하십시오 Predicate 그 반환 true 정확히 3 자가있는 문자열의 경우.
- 람다 표현의 유형은 Predicate
- 구현해야하는 방법은 boolean test(String s)
그런 다음 매개 변수 블록을 작성합니다. 이는 메소드 서명의 간단한 복사 / 붙여 넣기입니다: (String s).
그런 다음 빈약 한 화살표를 추가하십시오: ->.
그리고 방법의 몸. 결과는 다음과 같아야합니다:
Predicate<String> predicate =
(String s) -> {
return s.length() == 3;
};
구문 단순화
이 구문은 많은 것을 추측 할 수있는 컴파일러 덕분에 단순화 할 수 있으므로 작성할 필요가 없습니다.
먼저 컴파일러는 귀하가 Predicate 인터페이스이며이 방법은 String 논쟁으로. 그래서 (String s) 단순화 할 수 있습니다 (s). 인수가 하나만있는 경우 괄호를 제거하여 한 단계 더 나아갈 수도 있습니다. 그러면 인수 블록이 s. 둘 이상의 인수가 있거나 인수가없는 경우 괄호를 유지해야합니다.
둘째, 방법 본문에는 한 줄의 코드 만 있습니다. 이 경우 곱슬 머리띠 나 return 키워드.
최종 구문은 실제로 다음과 같습니다:
Predicate<String> predicate = s -> s.length() == 3;
그리고 이것은 우리를 첫 번째 모범 사례로 인도합니다. 람다를 짧게 유지하여 간단하고 읽을 수있는 코드의 한 줄에 불과합니다.
구현 Consumer<String>
어떤 시점에서 사람들은 지름길을 유혹 할 수 있습니다. 개발자가 "소비자가 물건을 가져 가서 아무것도 반환하지 않는다"고 말하는 것을들을 수 있습니다". 또는 "문자열에 정확히 3 개의 문자가 있으면 술어가 참입니다". 대부분의 경우 람다 표현식, 구현 한 추상적 방법 및이 방법을 유지하는 기능적 인터페이스간에 혼동이 있습니다.
그러나 기능적 인터페이스, 추상적 인 방법 및이를 구현하는 람다 표현이 서로 밀접하게 연결되어 있기 때문에 이러한 말하기 방식은 실제로 완전히 의미가 있습니다. 모호하지 않는 한 괜찮습니다.
람다를 쓰자 String 인쇄 System.out. 구문은 다음과 같습니다:
Consumer<String> print = s -> System.out.println(s);
여기서 우리는 람다 표현의 단순화 된 버전을 직접 썼습니다.
실행 가능 구현
구현 Runnable 구현을 작성하는 것으로 밝혀졌습니다 void run(). 이 인수 블록은 비어 있으므로 괄호로 작성해야합니다. 기억하십시오 : 하나의 인수가있는 경우에만 괄호를 생략 할 수 있습니다. 여기에는 0이 있습니다.
따라서 실행 중임을 알려주는 실행 가능 항목을 작성해 보겠습니다:
Runnable runnable = () -> System.out.println("I am running");
람다 표현 호출
이전으로 돌아 갑시다 Predicate 이 술어가 메소드에서 정의되었다고 가정하십시오. 주어진 문자열이 실제로 길이 3인지 테스트하는 데 어떻게 사용할 수 있습니까?
람다를 쓰는 데 사용 된 구문에도 불구하고이 람다는 인터페이스의 인스턴스라는 것을 명심해야합니다 Predicate. 이 인터페이스는 test() 그것은 String 그리고 boolean.
다음과 같은 방법으로 작성하겠습니다:
List<String> retainStringsOfLength3(List<String> strings) {
Predicate<String> predicate = s -> s.length() == 3;
List<String> stringsOfLength3 = new ArrayList<>();
for (String s: strings) {
if (predicate.test(s)) {
stringsOfLength3.add(s);
}
}
return stringsOfLength3;
}
이전 예제에서와 같이 술어를 어떻게 정의했는지 확인하십시오. 이후 Predicate 인터페이스는이 방법을 정의 boolean test(String), 에 정의 된 방법을 호출하는 것은 완벽하게 합법적입니다 Predicate 유형의 변수를 통해 Predicate. 이 술어 변수가 메소드를 정의하는 것처럼 보이지 않기 때문에 처음에는 혼란스러워 보일 수 있습니다.
우리와 함께,이 코드를 작성하는 훨씬 더 좋은 방법이 있습니다.이 자습서에서는 나중에 볼 수 있습니다.
따라서 람다를 작성할 때마다이 lamdba가 구현하고있는 인터페이스에 정의 된 모든 방법을 호출 할 수 있습니다. 추상적 인 방법을 호출하면 람다 자체의 코드가 호출됩니다.이 람다는 해당 방법의 구현이기 때문입니다. 기본 메소드를 호출하면 인터페이스에 작성된 코드가 호출됩니다. 람다가 기본 메소드를 무시할 수있는 방법은 없습니다.
지역 가치 포착
익숙해지면 람다를 쓰는 것이 매우 자연스러워집니다. 이들은 Collections Framework, Stream API 및 JDK의 다른 많은 장소에 매우 잘 통합되어 있습니다. Java SE 8부터 람다는 최고입니다.
람다 사용에는 제약이 있으며 이해해야 할 컴파일 타임 오류가 발생할 수 있습니다.
다음 코드를 고려해 봅시다:
int calculateTotalPrice(List<Product> products) {
int totalPrice = 0;
Consumer<Product> consumer =
product -> totalPrice += product.getPrice();
for (Product product: products) {
consumer.accept(product);
}
}
이 코드가 멋지게 보일지라도 컴파일하려고하면 다음과 같은 오류가 발생합니다 totalPrice 이것에서 Consumer 이행:
람다 발현에 사용되는 변수는 최종적이거나 효과적으로 최종적이어야합니다
그 이유는 다음과 같습니다. lambdas는 본문 외부에 정의 된 변수를 수정할 수 없습니다. 그들은있는 한 읽을 수 있습니다 final, 즉, 불변입니다. 변수에 액세스하는이 프로세스를 캡처: 람다는 할 수 없다 포획 변수, 그들은 단지 할 수 있습니다 포획 값. 최종 변수는 실제로 값입니다.
오류 메시지는 변수가 최종, Java 언어의 고전적인 개념입니다. 또한 변수가 될 수 있음을 알려줍니다 효과적으로 최종. 이 개념은 Java SE 8에서 도입되었습니다. 변수를 명시 적으로 선언하지 않더라도 final, 컴파일러가 당신을 위해 할 수 있습니다. 이 변수가 람다에서 읽히고 수정하지 않으면 final 당신을위한 선언. 물론 이것은 컴파일 된 코드에서 수행되며 컴파일러는 소스 코드를 수정하지 않습니다. 이러한 변수는 호출되지 않습니다 최종; 그들은 불린다 효과적으로 최종 변수. 이것은 매우 유용한 기능입니다.
람다 직렬화
Lambda 표현식은 직렬화 될 수 있도록 제작되었습니다.
람다 표현을 직렬화하려는 이유는 무엇입니까? 람다 식은 필드에 저장 될 수 있으며,이 필드는 생성자 또는 세터 방법을 통해 액세스 될 수 있습니다. 그런 다음 런타임에 객체의 상태를 알지 못하고 람다 표현식을 가질 수 있습니다.
따라서 기존 직렬화 가능한 클래스와의 이전 버전과의 호환성을 유지하기 위해 람다 표현을 직렬화 할 수 있습니다.
※ 쿠팡 파트너스 활동을 통해 일정액의 수수료를 제공 받을 수 있습니다
'∮explotación≒ 개발' 카테고리의 다른 글
java 예외( Exception ) (0) | 2023.03.26 |
---|---|
java 주석 (0) | 2023.03.15 |
java Introducing Generics (0) | 2023.03.08 |
java Interfaces (0) | 2023.03.04 |
Oracle 오라클 조인 [ ANSI JOIN, Oracle Join ] (0) | 2023.02.28 |
댓글