1. 람다식 (Lambda Expression)
1) 람다식은 함수(메서드)를 간단한 식으로 표현하는 방법. 익명 함수를 생성하기 위한 식으로 객체 지향 언어보다 함수 지향 언어에 가까움.
2) 람다식의 형태는 매개 변수를 가진 코드 블록이지만 자바는 런타임 시 이것을 함수적 인터페이스의 익명 구현 객체로 취급함.
람다식 → 매개 변수를 가진 코드 블록 → 익명 구현 객체 |
3) 자바에 람다식을 도입한 이유
- 자바코드가 매우 간결해짐.
- 컬렉션의 요소를 필터링하거나 매핑해서 결과를 쉽게 얻을 수 있음.
- 객체 지향 프로그래밍 보다 효율적인 경우가 있음.
=> 대용량 데이터일 경우, 데이터 포장 객체를 생성 후 처리하는 것보다는 데이터를 바로 처리하는 것이 속도에 유리.
2. 람다식 기본 문법
1) 매개변수 앞에 붙는 타입은 런타임 시 대입되는 값에 따라 인식되기 때문에 생략 가능.
(a) -> { System.out.println(a); }
2) 매개 변수가 하나거나 실행문이 하나라면 각각 괄호( )와 중괄호{ } 생략 가능.
//하나의 매개변수만 있을 경우 괄호() 생략 가능
a -> { System.out.println(a); }
//하나의 실행문만 있을 경우 중괄호{ } 생략 가능
a -> System.out.println(a);
3) 매개 변수가 없다면 괄호( )를 반드시 사용함.
( ) -> { 실행문; ... }
4) 결과값을 리턴해야 한다면 중괄호 안에 return문을 사용할 수 있음.
(x, y) -> { return x + y; };
5) 중괄호에 return문만 있을 경우 return과 중괄호를 생략 가능.
(x,y) -> x + y;
3. 타겟 타입과 함수적 인터페이스
1) 람다식이 대입 될 인터페이스를 람다식의 타겟 타입 이라고 함.
2) 익명 구현 객체를 만들 때 사용할 인터페이스.
3) 함수적 인터페이스 (@FunctionalInterface)
- 하나의 추상 메소드가 선언된 인터페이스를 함수적 인터페이스라고 하며, 함수적 인터페이스만 람다식의 타겟 타입이 될 수 있음.
- 인터페이스에 @FunctionalInterface 어노테이션을 붙이면 두개 이상의 추상 메소드가 선언되지 않도록 컴파일러가 체크해줌. (선택사항)
4) 매개 변수와 리턴값이 없는 람다식
- method() 호출은 람다식의 중괄호를 실행시킴.
5) 매개변수가 있는 람다식
6) 매개변수와 리턴값이 있는 람다식
4. 클래스 멤버와 로컬 변수 사용
1) 람다식의 실행 블록에는 클래스의 멤버 및 로컬 변수를 사용할 수 있음.
2) 클래스의 멤버 사용
- 람다식 실행 블록에는 클래스의 멤버인 필드와 메서드를 제약없이 사용할 수 있음.
- 람다식의 this 키워드는 람다식을 실행한 객체의 참조임.
public class Example {
public int outterField = 10;
class Inner {
int innerField = 20;
void method() {
//람다식
MyFunctionalInterface fi = () -> {
System.out.println("outterField: " + outterField);
//바깥 객체의 참조를 얻기 위해서는 클래스명.this 를 사용함
System.out.println("outterField: " + Example.this.outterField + "\n");
System.out.println("innerField: " + innerField);
//람다식 내부에서 this는 Inner 객체를 참조함.
System.out.println("innerField: " + this.innerField + "\n");
};
fi.method();
}
}
}
3) 로컬 변수의 사용
- 외부 로컬 변수는 람다식에서 사용되면 final 특성을 가지기 때문에 람다식에 사용된 이후 값을 변경할 수 없음. (컴파일 에러가 남)
public class UsingLocalVariable {
void method(int arg) { //arg는 final 특성을 가짐
int localVar = 40; //localVar는 final 특성을 가짐
//arg = 31; //final 특성 때문에 수정 불가
//localVar = 41; //final 특성 때문에 수정 불가
//람다식
MyFunctionalInterface fi = 0 -> {
//로컬변수 사용
System.out.println("arg:" + arg);
System.out.println("localVar:" +localVar+ "\n");
};
fi.method();
}
}
5. 표준 API의 함수적 인터페이스
1) java.util.function 패키지로 제공.
2) 매개타입으로 사용되어 람다식을 매개값으로 대입할 수 있게 해줌.
3) 한 개의 추상 메소드를 가지는 인터페이스들은 모두 람다식 사용이 가능함.
4) 함수적 인터페이스는 인터페이스에 선언된 추상 메서드의 매개값과 리턴 유무 따라 구분됨.
5) 종류
- Consumer : 매개값은 있고 리턴값이 없는 추상메소드를 가지고 있음.
- Supplier : 매개 변수가 없고 리턴값이 있는 추상메소드를 가짐..
- Function : 매개값과 리턴값이 있는 추상메소드를 가진다. 매개값을 리턴값으로 매핑(타입 변환).
- Operator : 매개값과 리턴값이 있는 추상메소드를 가진다. 매개값을 이용해 연산 후 동일한 타입으로 리턴.
- Predicate : 매개값과 boolean 리턴값이 있는 추상메소드를 가진다. 매개값을 조사해 true 혹은 false를 리턴한다.
**Predicate 사용 예시
//원래는 Predicate<String, Boolean> 이라고 써야하지만,
//반환타입이 항상 Boolean 이기 때문에 Boolean은 생략가능.
//앞에 String 써있어서 간단하게 s로 생략 가능
Predicate<String> isEmptyStr = s -> s.length()==0; //람다식 반환타입은 boolean 이어야 함.
//앞에 String 써있어서 간단하게 s로 생략 가능
String s = ""
if(isEmptyStr.test(s)) //if(s.length()==0 과 같은 의미
System.out.println("This is an empty String.");
6) 매개변수가 2개인 함수형 인터페이스
- 만약 매개변수가 2개 이상이라면 직접 만들어도 됨.
interface TriFunction<T,U,V,R> {
R apply(T t, U u, V v);
7) 매개변수의 타입과 반환타입이 일치하는 함수형 인터페이스
6. Predicate의 결합 (and(), or(), negate() 디폴트 메소드와 isEqual() 정적 메소드)
1) and(), or(), negate() 디폴트 메소드로 두 Predicate를 하나로 결합.
2) 이 메소드들은 각각 논리 연산자인 &&, ||, ! 으로 대응됨.
Predicate<Integer> p = i -> i<100;
Predicate<Integer> q = i -> i<200;
Predicate<Integer> r = i -> i%2 == 0;
Predicate<Integer> notP = p.negate(); // !(i<100) 과 동일. i>=100 과 동일.
Predicate<Integer> all = notP.and(q).or(r); // 100<=i && i<200 || i%2==0
Predicate<Integer> all2 = notP.and(q.or(r)); // 100<=i && (i<200 || i%2==0)
//출력할 때는 Predicate가 가지고 있는 test() 메서드 호출.
System.out.println(all.test(2));
System.out.println(all2.test(2));
3) Predicate< T > 함수적 인터페이스는 isEqual() 정적 메소드도 제공.
7. andThen()과 compose() 디폴트 메소드
1) 함수적 인터페이스는 하나 이상의 디폴트 및 정적 메소드를 가지고 있음.
2) 두 개의 함수적 인터페이스를 순차적으로 연결해 실행함.
3) 첫 번째 리턴값을 두 번째 매개값으로 제공해 최종 결과값을 리턴.
4) andThen()과 compose()의 차이점은 어떤 함수적 인터페이스부터 처리하느냐는 것.
8. 함수형 인터페이스를 사용하는 컬렉션 프레임워크의 메서드 (와일드카드 생략)
- 사용 예시
list.forEach(i->System.out.print(i+",")); //list의 모든 요소를 출력
list.removeIf(x-> x%2==0 || x%3==0); //2 또는 3의 배수 제거
list.replaceAll(i->i*10); //모든 요소에 10 곱하기
map.forEach((k,v)->System.out.print("{"+k+","+v+"},")); //map의 모든 요소를 {k,v} 형식으로 출력
9. 메서드 참조
1) 하나의 메서드만 호출하는 람다식은 '메서드 참조'로 더 간단히 할 수 있음.
종류 | 람다 | 메서드 참조 |
static 메서드 참조 | (x) -> ClassName.method(x) | ClassName::method |
인스턴스 메서드 참조 | (obj.x) -> obj.method(x) | ClassName::method |
2) 메소드를 참조해서 매개 변수의 정보 및 리턴 타입을 알아내 람다식에 불필요한 매개 변수를 제거하는 것이 목적.
3) static 메서드 참조 예시
Integer method(String s) { //그저 Integer.parseInt(String s)만 호출
return Integer.parseInt(s);
}
//람다식으로 나타내면
Function<String, Integer> f = (String s) -> Integer.parseInt(s);
//중복되는 String, s를 없애고 더 간단히 메서드 참조로 나타내면
Function<String, Integer> f = Integer::parseInt;
4) 생성자의 메서드 참조
- 생성자가 오버로딩되어 여러개인 경우 컴파일러는 함수적 인터페이스의 추상 메소드와 동일한 매개 변수 타입, 개수를 가지고 있는 생성자를 찾아 실행함.
//메서드 참조 전
(a,b) -> return new 클래스(a,b);
//생성자 참조 표현
클래스 :: new
//람다식으로 표현
Supplier<MyClass> s = () -> new MyClass();
//생성자 메서드 참조로 표현
Supplier<MyClass> s = MyClass::new;
//람다식으로 표현
Function<Integer, MyClass> s = i -> new MyClass(i);
//생성자 메서드 참조로 표현
Function<Integer, MyClass> s = MyClass::new;
//람다식으로 표현
Supplier<MyClass> s = () -> new MyClass();
//생성자 메서드 참조로 표현
Supplier<MyClass> s = MyClass::new;
5) 배열과 메서드 참조
//람다식으로 표현
Function<Integer, int[]> f = x -> new int[x];
//배열과 메서드 참조로 표현
Function<Integer, int[]> f = int[]::new;
6) 정적 메서드와 인스턴스 메서드 참조
//정적 메서드 참조
클래스 :: 메서드
//인스턴스 메서드 참조
참조 변수 :: 메서드
참고 : [한빛미디어] 이것이 자바다 (신용권의 Java 프로그래밍 정복) Chapter 14.람다식
참고 : [도우출판] JAVA의 정석(3ND EDITION)-자바의 정석 최신 Java 8.0 포함 Chapter 14.람다와 스트림
'JAVA' 카테고리의 다른 글
List 컬렉션 (0) | 2022.08.28 |
---|---|
컬렉션 프레임워크 (0) | 2022.08.28 |
생성자 super() vs 참조변수 super (0) | 2022.08.28 |
상속과 포함 (0) | 2022.08.28 |
static 멤버 & 인스턴스 멤버 (0) | 2022.08.28 |
댓글