https://sundaland.tistory.com/282
# Predicate는 boolean 값으로 참과 거짓을 반환하는 test 메소드를 가지고 있다.
Predicate<객채 타입> 프리디케이트 호출 변수명 으로 구성된다. 호출시 객체타입이 일치하지 않아도 컴파일 오류는 발생하지 않지만 런타임때는 오류가 발생한다.
람다 표현식 (Lambda Expressions)
익명 클래스의 한 가지 문제는 익명 클래스의 구현이 메서드가 하나가 포함된 인터페이스와 같이 매우 간단한 경우 익명 클래스의 신택스가 다루기 힘들고 명확하지 않게 보일 수 있다.
람다 표현식을 사용하면 특정 기능을 메서드 아규먼트로 처리하거나 코드를 데이터로 처리할 수 있다.
Fisrt Class Citizen이라고도 불린다.
람다 표현식은 매우 높은 확률 함수형(Functional) 인터페이스에서 사용된다.
# 리스트는 순서가 지정된 컬렉션 인터페이스다. 컬렉션인 여러 요소를 하나의 단위로 그룹화하는 객체이다. 컬렉션은 집합체(aggregagte) 데이터를 저장, 검색, 조작 및 전달하는 데 사용된다.
람다 코드 예시
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
(p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
로컬 및 익명 클래스와 달리 람다 표현식에는 섀도잉 문제가 없다.
람다 표현식은 레시컬 스코프 (Lexical Scope)를 가지며 이는 람다 표현식이 슈퍼타입으로부터 어떤 이름도 상속받지 않으며, 새로운 스코프 레벨을 도입하지 않는다는 것을 으미한다.
람다 표현식 내의 변수 참조는 둘러싸고 있는 환경과 동일하게 해석된다.
람파 표현식은 새로운 레벨의 범위 지정을 도입하지 않기 때문에 컴파일러 오류가 발생할 수 있다.
타겟 타입 (Target Typing)
JAVA 런타임은 메소드를 호출할 때 클래스의 데이터 타입을 예상하므로 람다 표현식은 위 코드의 형태를 사용한다.
이러만 메서드가 기대하는 데이터 타입을 타겟 타입이라고 한다.
람다 표현식의 타입을 결정하기 위해 JAVA 컴파일러는 람다 표현식이 발견된 컨텍스트 또는 사황의 타켓 타입을 사용한다. 따라서 JAVA 컴파일러가 타겟 타입을 결정할 수 있는 상황에서만 람다 표식을 사용할 수 있다.
- 변수 선언 (Variable declarations)
- (Assignments)
- 반환 값 (Return statements)
- 배열 초기화 (Array initializers)
- 메서드 또는 생성자 (Method or constructor)
- 매게변수 (Arguments)
- 람다 표현식 바디 (Lambda expression bodies)
- 조건식 (Conditional expressions)
- 캐스트 표현 (Cast expressions)
타겟 타입과 메서드 매게변수
메서드 매게변수의 경우, JAVA 컴파일러는 오버로드 해결 및 타입 매게변수 추론이라는 두 가지 다른 언어 기능을 사용하여 타겟 타입을 결정한다.
타겟 타입 직렬화 (Serialization)
타겟 타입과 캡쳐된 매게변수가 직렬화 가능한 경우 람다 표현식을 직렬화할 수 있다. 그러나 내부 클래스와 마찬가지로 람다 표식의 직렬화는 권장디지 않는다.
Syntax of Lambda Expressions
람다 표현식은 다음과 같은 요소로 구성된다.괄호 ([ ]) 안에 쉼표 ( , )로 구분된 formal 파라미터 리스트이다.
람다 표현식에서 파라미터 데이터 형식을 생략할 수 있다. 또한 파라미터가 하나만 있는 경우 괄호를 생략할 수 있다.
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
Arrow 토큰, ( -> )
단일 표현식 또는 명령문 블록으로 구성된 본문이다.
p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
단일 표현식을 지정하면 JAVA 런타임을 표현식을 평가한 다음 해당 값을 리턴한다. 또는 return 명령문을 사용할 수 있다.
p -> {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
리턴 명령문은 표현식은 아니다. 람다 표현식에서는 명령문은 중괄호 ({ })로 묶어야 한다.
람다 표현식에서 return 명령문은
그러나 void 메서드 호출을 중괄호로 묶을 필요가 없다.
email -> System.out.println(email)
람다 표현식은 메서드 선언과 매우 유사하다. 람다 표현식을 익명 메서드로 간주할 수 있다.
1. 단일 표현식을 사용하는 람다 표현식
(int x, int y) -> x + y
2. 블록을 사용하는 람다 표현식
(int x, int y) -> {
int sum = x + y;
return sum;
}
3. 파라미터가 없는 람다 표현식
() -> System.out.println("Hello, World!")
4. 단일 파라미터를 사용하는 람다 표현식 (타입 생략)
a -> a * 2
다음은 계산기 코드의 예시다.
public class Calculator {
interface IntegerMath {
int operation(int a, int b);
}
public int operateBinary(int a, int b, IntegerMath op) {
return op.operation(a, b);
}
public static void main(String... args) {
Calculator myApp = new Calculator();
IntegerMath addition = (a, b) -> a + b;
IntegerMath subtraction = (a, b) -> a - b;
System.out.println("40 + 2 = " +
myApp.operateBinary(40, 2, addition));
System.out.println("20 - 10 = " +
myApp.operateBinary(20, 10, subtraction));
}
}
40 + 2 = 42
20 - 10 = 10
Accessing Local Variables of the Enclosing Scope
로컬 및 익명 클래스와 마찬가지로 람파 표현식을 변수를 캡처할 수 있다. 람다 표현식은 둘러싸는 범위의 지역 변수에 대해 동일한 액세스 권한을 갖는다.
그러나 로컬 및 익명 클래스와 달리 람다 표현식에는 섀도잉 문제가 없다. 렉시컬 스코프 (Lexical)를 가진다.
람다 표현식이 슈퍼타입으로부터 어떤 이름도 상속받지 않으며, 새로운 스코프 레벨을 도입하지 않는다는 것을 의미한다.
람다 표현식 내의 선언은 둘러싸고 있는 환경에서와 동일하게 해석된다.
public class LambdaScopeTest {
public int x = 0;
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
// 메서드 인수 x는 가장 안쪽 스코프에서 정의됨
System.out.println("x = " + x); // 메서드 인수
System.out.println("this.x = " + this.x); // FirstLevel 클래스의 인스턴스 변수
System.out.println("LambdaScopeTest.this.x = " + LambdaScopeTest.this.x);
// LambdaScopeTest 클래스의 인스턴스 변수
Runnable r = () -> {
// 람다 표현식은 메서드 인수를 섀도잉하지 않음
System.out.println("x = " + x); // 메서드 인수
System.out.println("this.x = " + this.x); // FirstLevel 클래스의 인스턴스 변수
System.out.println("LambdaScopeTest.this.x = " + LambdaScopeTest.this.x);
// LambdaScopeTest 클래스의 인스턴스 변수
};
r.run();
}
}
public static void main(String... args) {
LambdaScopeTest st = new LambdaScopeTest();
LambdaScopeTest.FirstLevel fl = st.new FirstLevel();
fl.methodInFirstLevel(23);
}
}
외부 클래스 LambdaScopeTest의 x 변수는 클래스의 인스턴스 변수이다.
내부 클래스 FirstLevel의 x 변수는 클래스의 인스턴스 변수이다. methodInFirstLevel 메서드는 인수로 x 변수를 받는다.
람다 표현식 내에서 x 변수는 methodInFirstLevel 메서드의 인수를 가르킨다.
람다 표현식의 this.x 변수는 FirstLevel 클래스의 인스턴스 변수를 가르킨다.
import java.util.function.Consumer;
public class LambdaScopeTest {
public int x = 0;
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
int z = 2;
Consumer<Integer> myConsumer = (y) ->
{
// The following statement causes the compiler to generate
// the error "Local variable z defined in an enclosing scope
// must be final or effectively final"
//
// z = 99;
System.out.println("x = " + x);
System.out.println("y = " + y);
System.out.println("z = " + z);
System.out.println("this.x = " + this.x);
System.out.println("LambdaScopeTest.this.x = " +
LambdaScopeTest.this.x);
};
myConsumer.accept(x);
}
}
public static void main(String... args) {
LambdaScopeTest st = new LambdaScopeTest();
LambdaScopeTest.FirstLevel fl = st.new FirstLevel();
fl.methodInFirstLevel(23);
}
}
x = 23
y = 23
z = 2
this.x = 1
LambdaScopeTest.this.x = 0
람다 표현식이 새로운 레벨의 범위 지정을 도입하지 않기 때문에 컴파일러는 "람다 표현식의 파라미터 x는 바깥쪽 범위에 정의된 다른 지역 변수를 다시 선언할 수 없습니다"라는 오류를 생성합니다. 결과적으로, 포함 범위의 필드, 메소드 및 지역 변수에 직접 액세스할 수 있습니다.
중첩 클래스, 지역 클래스, 익명 클래스, 람다 표현식은 언제 사용해야 하는가?
중첩 클래스는 한 곳에서만 사용되는 클래스는 논리적으로 그룹화하고, 캡슐화 사용을 증가시키며, 더 읽기 쉽고 유지보수가 용이한 코드를 생성하는데 도움이 된다.
지역 클래스는 클래스의 인스턴스를 여러 개 생성해야 하거나, 생성자에 접근해야 하거나, 새로운 이름이 있는 타입을 도입해야 할 경우에 사용한다. (나중에 추가적인 메소드를 호출해야하는 경우)익명 클래스는 필드를 선언하거나 추가 메소드를 정의해야 할 경우에 사용한다.람파 표현식은 다른 코드에 전달하고자 하는 단일 행동 단위를 캡슐화를 경우 사용한다. 함수형 인터페이스의 간단한 인스턴스가 필요하고 앞서 언급된 기준들이 해당되지 않는 경우에 사용한다.중첩 클래스는 요구 사항이 로컬 클래스와 유사하고 타입을 더 넓게 사용하고자 하며, 로컬 변수나 메스트 파라미터에 접근할 필요가 없는 경우에 사용한다.non-public 필드와 메소드에 접근해야 하는 포함 인스턴스가 필요한 경우 비정적 중첩 클래스를 사용한다, 만약 이러한접근이 필요 없다면 정적 중첩 클래스를 사용한다.
퀴즈
해당 코드는 런타임 오류를 유발시킨다. 코드를 적절하게 수정하라.
public class LambdaTest2 {
public static void main(String[] args) {
int value = 5;
Runnable r = () -> {
value = 10;
System.out.println("Value: " + value);
};
r.run();//
}
}
답: value = 10; 코드 라인을 삭제한다.
int value는 코드 상으로는 보이지 않지만 final로 선언된 상태이기 때문에 값을 변경할 수 없다.
# 람다 표현식은 자바 컴파일러가 자동으로 값을 return해 주기 때문에 필요하지 않다. 하지만 브레이스를 넣으면 명령문이 되기 때문에 반드시 return 값을 넣어야 한다.
public class LambdaTest6 {
public static void main(String[] args) {
StringFormatter formatter = (input) -> input.toUpperCase();
System.out.println(formatter.format("Hello Lambda"));
}
}
public class LambdaTest6 {
public static void main(String[] args) {
StringFormatter formatter = (input) -> {
return input.toUpperCase();
};
System.out.println(formatter.format("Hello Lambda"));
}
}
'자바 튜토리얼' 카테고리의 다른 글
인터페이스와 상속 (Interface and Inheritance) [1] (0) | 2024.07.08 |
---|---|
인터페이스 (Interface) (0) | 2024.07.08 |
클래스와 객체 (Classes and Objects) [3] (0) | 2024.07.04 |
클래스와 객체 (Classes and Objects) [2] (0) | 2024.07.03 |
클래스와 객체 (Classes and Objects) [1] (0) | 2024.07.02 |