공부/Java

[Java-14] 람다식(Lambda expression)

줭♪(´▽`) 2021. 7. 6. 14:39

1.람다식(Lambda expression) 

1) 람다식이란?

- 메서드를 하나의 '식(expression)'으로 표현한 것

- 함수를 간략하면서도 명확한 식으로 표현할 수 있게 해줌

- JDK1.8부터 추가

- 익명 함수(anonymous function)라고도 함

 

2) 람다식 작성

반환타입 메서드이름(매개변수 선언) {
    문장들
}

                   ↓

반환타입 메서드이름 (매개변수 선언) -> {
    문장들
}

 

- 기존 메서드

int max(int a, int b) {
    return a > b ? a : b;
}

 

- 이름과 반환타입 제거

(int a, int b) -> {
    return a > b ? a : b;
}

 

- return문 대신 '식'으로 표현 가능

- 연산결과는 자동적으로 반환값이 됨

- 끝에 ';'을 붙이지 않음

(int a, int b) -> a > b ? a : b

 

- 추론이 가능한 경우 매개변수의 타입 생략 가능

- 매개변수 중 하나의 타입만 생략하는 것은 불가능

(a, b) -> a > b ? a : b

 

- 선언된 매개변수가 하나뿐일 경우 괄호() 생략 가능 (단, 매개변수의 타입이 없어야 함)

a -> a * a       // 가능
int a -> a * a  // 불가능

 

- 괄호{} 안의 문장이 하나일 때는 괄호{} 생략 가능 (단, return문이 아니어야 함)

- 문장의 끝에 ';'를 붙이지 않음

(String name, int i) -> {
    System.out.println(name+"="+i);
}

(String name, int i) -> System.out.println(name+"="+i)

 

 

3) 함수형 인터페이스(Functional Interface)

- 람다식을 다루기 위한 인터페이스

- 인터페이스를 구현한 익명 객체를 람다식으로 대체할 수 있기 때문

(익명 개체의 메서드와 람다식의 매개변수의 타입, 개수, 반환값이 일치할 경우)

- 함수형 인터페이스에는 하나의 추상 메서드만 정의되어 있어야 함

(람다식과 인터페이스의 1:1 연결을 위해)

- default와 static 메서드의 개수에는 제약이 없음

 

@FunctionalInterface
interface MyFunction {
    public abstract int max(int a, int b);
}

 

※ 메서드의 매개변수가 함수형 인터페이스 타입일 때

- 해당 메서드를 호출할 때 람다식을 참조하는 참조변수를 매개변수로 지정

- 또는 직접 람다식을 매개변수로 지정

@FunctionalInterface
interface MyFunction {
    void myMethod();
}

void aMethod(MyFunction f) {
    f.myMethod();
}
...

// (1) 람다식을 참조하는 참조변수를 매개변수로
MyFunction f = ()-> System.out.println("myMethod()");
aMethod(f);

// (2) 직접 람다식을 매개변수로
aMethod(()-> System.out.println("myMethod()"));

 

 

※ 메서드의 리턴타입이 함수형 인터페이스 타입일 때

- 함수형 인터페이스와 동등한 람다식을 참조하는 참조변수를 반환

- 또는 직접 람다식을 반환

@FunctionalInterface
interface MyFunction {
    void myMethod();
}

MyFunction myMethod() {
	// (1) 함수형인터페이스와 동등한 람다식을 가리키는 참조변수 반환
    MyFunction f = ()->{};
    return f;
    
    // (2) 직접 람다식을 반환
    return ()->{};
}

 

※ 람다식의 타입과 형변환

- 람다식의 타입이 함수형 인터페이스의 타입과 일치하는 것은 X

- 형변환이 필요하지만 생략 가능함

- 람다식은 오직 '함수형 인터페이스'로만 형변환 가능

MyFunction f = (MyFunction) (()->{});

Object obj = (Object) (()->{});	// Error

Object obj = (Object)(MyFunction)(()->{});
String str = ((Object)(MyFunction)(()->{})).toString();

 

 

※ 외부 변수를 참조하는 람다식

- 람다식 내에서 참조하는 지역변수는 final이 붙지 않았어도 '상수'로 간주되므로 변경 불가능

@FunctionalInterface
interface MyFunction2 {
	void myMethod();	// public absract void myMethod();
}

class Outer {
	int val = 10;
	class Inner {
		int val = 20;
		
		void method(int i) {
			int val = 30;
			
			MyFunction2 f = () -> {
				System.out.println("i: " + i);	// i는 상수로 취급하여 변경 불가능, 100
				System.out.println("val: " + val);	// val은 상수로 취급하여 변경 불가능, 30
				System.out.println("this.val: " + ++this.val);	// Inner 클래스의 val이므로 변경 가능, 21
				System.out.println("Outer.this.val: " + ++Outer.this.val);	// Outer 클래스의 val이므로 변경 가능, 11
			};
			
			f.myMethod();
		}
	}
}

public class LambdaEx2 {
	public static void main(String[] args) {
		Outer outer = new Outer();
		Outer.Inner inner = outer.new Inner();
		inner.method(100);
	}
}

 

4) java.util.function패키지

- 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정의해놓은 것

 

① 주요 함수형 인터페이스

java.lang.Runnable 매개변수 X, 반환값 X
Supplier<T> 매개변수 X, 반환값 O
Consumer<T> 매개변수 O, 반환값 X
Function<T, R> 매개변수 1, 반환값 1
Predicate<T> 조건식 표현
매개변수 1, 반환값 boolean

 

Predicate<String> isEmptyStr = s -> s.length() == 0;
String s = "";

if(isEmptyStr.test(s))
    System.out.println("This is an empty String.");

 

② 매개변수가 2개인 함수형 인터페이스

BiConsumer<T, U> 매개변수 2, 반환값 X
BiPredicate<T, U> 조건식 표현
매개변수 2, 반환값 boolean
BiFunction<T, U, R> 매개변수 2, 결과 1

 

 

5) 메서드 참조(method reference)

- 람다식이 하나의 메서드만 호출하는 경우, 람다식을 간략하게 하는 것

종류 람다 메서드 참조
static메서드 참조 (x) -> ClassName.method(x) ClassName::method
인스턴스메서드 참조 (obj, x) -> obj.method(x) ClassName::method
특정 객체 인스턴스메서드 참조 (x) -> obj.method(x) obj::method

 

 

 

참고 - Java의 정석 3rd Edition (저자 : 남궁성, 출판 : 도우출판)