람다식(Lambda expression)
- 메서드(함수)를 하나의 ‘식(expression)’으로 표현한 것
- 람다식은 메서드(함수)를 간략하면서도 명확한 식으로 표현할 수 있게 해준다.
- 람다식으로 인해 자바는 객체지향언어인 동시에 함수형 언어가 되었다.
- 객체지향언어의 메서드(클래스에 종속적) == 절차지향언어의 함수(클래스에 독립적)
public int max(int a, int b) { return a > b ? a : b; } |
(a, b) -> a > b ? a : b |
람다식으로 표현 |
-> 람다식은 메서드의 매개변수로 전달어 질 수 있고 메서드의 결과로 반환될 수 있다.(람다식으로 인해 메서드를 변수처럼 다루는 것이 가능해짐)
-> 메서드를 람다식으로 표현하면 메서드의 이름과 반환값이 없어지므로, 람다식을 ‘익명 함수(anonymous function)’이라도 한다.
람다식 작성하기
1. 메서드의 이름과 반환타입을 제거하고 매개변수 선언부와 몸통 { } 사이에 ‘->’추가한다.
2. 반환값이 있는 경우, 식이나 값만 적고 return문 생략 가능(끝에‘:’안 붙임)
3. 매개변수의 타입이 추론 가능하면 생략가능(대부분의 경우 생략가능, )
-> (int a, b) -> a > b ? a : b와 같이 두 매개변수 중 어느 하나의 타입만 생략하는 것은 허용되지 않음
주의사항
1. 매개변수가 하나인 경우 괄호( ) 생략가능(타입이 없을 때만)
2. 블록 안의 문장이 하나뿐 일 때, 괄호 { } 생략 가능(끝에 세미콜론 ‘; 안붙임)
단, 하나뿐인 문장이 return문이면 괄호 { } 생략불가(보통 return문 생략)
메서드 | 람다식 |
int max(int a, int b) { return a > b ? a : b; } |
(a , b) -> a > b ? a : b; |
int printVar(String name, int i) { System.out.println(name+"="+i); } |
(name , i) -> System.out.println(name+"="+i) |
int square(int x) { return x*x; } |
x -> x * x |
int roll() { return (int)(Math.random()*6); } |
( ) -> (int)(Math.random()*6) |
int sumArr(int[] arr) { int sum = 0; for(int i: arr) sum += i; return sum; } |
(int[] arr) -> { int sum = 0; for(int i: arr) sum += i; return sum; } |
함수형 인터페이스(Functional Interface)
람다식은 익명 함수? 익명 객체!
-> 람다식은 익명 함수가 아니라 익명 객체이다.
(a, b) -> a > b ? a : b |
new Object( ) { // 객체의 선언과 생성을 동시에 int max(int a, int b) { return a > b ? a : b; } } |
-> 코드는 동일하다. 원래 오른쪽 코드로 작성되어 있던 것을 왼쪽 코드(람다식)으로 작성
-> 람다식(익명 객체)을 다루기 위해서는 참조변수가 필요하다
public class Ex14_0 {
public static void main(String[] args) {
// Object obj = (a, b) -> a > b ? a : b; // 람다식. 익명객체
Object obj = new Object() {
int max(int a, int b) {
return a > b? a:b;
}
};
// int value = obj.max(3,5); // 에러 max 메서드 사용 불가 -> obj를 참조변수로 사용 불가능
}
}
- 추상메서드 max를 갖고 있는 MyFunction 인터페이스 생성(@FunctionalInterface)
-> MyFunction을 구현한 익명클래스의 객체(클래스의 선언, 객체 생성 동시에)
-> MyFunction인터페이스에 정의된 메서드 max( ) 익명클래스의 메서드 max는 선언부가 일치하기 때문에 익명 객체를 아래와 같이 람다식으로 대체할 수 있다.
함수형 인터페이스 타입의 참조변수로 람다식을 참조할 수 있음(단, 함수형 인터페이스의 메서드와 람다식의 매개변수 개수와 반환타입이 일치해야 함)
- 함수형 인터페이스: 단 하나의 추상메서드만 선언된 인터페이스
-> 함수형 인터페이스는 람다식을 다루기 위해서 사용
-> 람다식을 참조변수로 다룰 수 있다는 것은 메서드를 통해 람다식을 주고받을 수 있다는 것을 의미함
예제)
public class Ex14_0 {
public static void main(String[] args) {
// Object obj = (a, b) -> a > b ? a : b; // 람다식. 익명객체
MyFunction f = new MyFunction() {
public int max(int a, int b) { // (1)오버라이딩 규칙 - 접근제어자는 좁게 못바꾼다.
return a > b ? a : b;
}
};
// 위의 익명 객체를 람다식으로. 람다식을 다루기 위한 참조변수의 타입은 함수형 인터페이스로 한다.
MyFunction f = (a,b) -> a > b ? a : b; // 람다식. 익명객체
int value = f.max(3,5); // 함수형 인터페이스
System.out.println("value=" + value); // value=5
}
}
@FunctionalInterface // 함수형 인터페이스는 단 하나의 추상 메서드만 가져야 함(컴파일러가 체크)
interface MyFunction {
// public abstract int max(int a, int b);
int max(int a, int b); // public abstract이 생략되었다.
}
-> (1)
오버라이딩 규칙에서 자손클래스는 접근제어자는 부모조상보다 범위가 좁으면 안됨.
MyFunction 인터페이스의 메서드는 모두 public abstract이기 때문에 main메서드에서 max를 오버라이딩 하려면 public을 붙여야 함
-> 람다식과 함수형 인터페이스에 선언된 추상메서드의 매개변수 타입과, 반환타입도 같아야 함!
함수형 인터페이스 타입의 매개변수, 반환타입
- 함수형 인터페이스 타입의 매개변수
<MyFunction 인터페이스>
@FunctionalInterface
interface MyFunction {
void myMethod();
-> 매개변수에 함수형 인터페이스(MyFunction)가 오고 함수형 인터페이스안에 있는 메서드(myMethod) 호출(람다식 호출)
-함수형 인터페이스 타입의 반환타입
MyFunction myMethod() {
MyFunction f = ()->{};
return f;
}
// 위의 두 줄을 한 줄로 MyFunction -> 람다식 반환
MyFunction myMethod() {
return ()->{};
}
예제)
@FunctionalInterface
interface MyFunction {
void run(); // public abstract 생략! 원래는 public abstract void run(); 추상메서드 run
}
public class Ex14_1 {
static void execute(MyFunction f) { // 매개변수 타입이 MyFunction인 execute메서드
f.run();
}
static MyFunction getMyFunction() { // 반환타입이 MyFunction인 getMyFunction메서드
// MyFunction f = () -> System.out.println("f3.run()");
// return f;
return () -> System.out.println("f3.run()"); // 위의 두줄을 람다식으로 한 줄로
}
public static void main(String[] args) {
// 람다식으로 MyFunction의 run()을 구현
MyFunction f1 = () -> System.out.println("f1.run"); // 매개변수 xx 리턴타입 xx MyFunction 인터페이스의 run()과 동일
MyFunction f2 = new MyFunction() { // 익명클래스로 run()을 구현
public void run() { // public을 반드시 붙여야함(오버라이딩 규칙)
System.out.println("f2.run()");
}
};
MyFunction f3 = getMyFunction();
f1.run();
f2.run();
f3.run();
execute(f1);
execute(()->System.out.println("run()"));
}
}
'멀티캠퍼스 풀스택 과정 > Java의 정석' 카테고리의 다른 글
자바의 정석11-3 스트림(Stream), 스트림 생성 (0) | 2022.01.16 |
---|---|
자바의 정석11-2 java.util.Function 패키지의 함수형 인터페이스와 메서드 참조 (0) | 2022.01.16 |
자바의 정석10-5 쓰레드의 동기화(Synchronization) (0) | 2022.01.13 |
자바의 정석10-4 데몬 쓰레드(daemon thread)와 쓰레드의 실행제어 (0) | 2022.01.13 |
자바의 정석10-3 쓰레드의 우선순위와 쓰레드 그룹 (0) | 2022.01.13 |