Search
Duplicate
📒

[Java Study] 10-x. 람다 표현식1

상태
미진행
수업
Java Study
주제
Stream
4 more properties

람다란 무엇인가?

NOTE
람다 표현식 → 메서드로 전달할 수 있는 익명 함수를 단수화한 것이다!
람다 표현식
// String 형식의 파라미터를 가지며, int를 반환 (String s) -> s.length() // Apple 형식의 파라미터를 가지며, boolean을 반환 (Apple a) -> a.getLength() > 150 // int 형식의 파라미터 2개를 가지며, 리턴값이 없음 (int x, int y) -> { System.out.println("Result :"); System.out.println(x + y); } // 파라미터가 없으며, int 42를 반환 () -> 42 //Apple 형식의 파라미터 2개를 가지며, int(두 사과의 무게 비교 결과)를 반환 (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
Java
복사

람다의 특징

익명
보통의 메서드와 달리 이름이 없다.
함수
람다는 메서드처럼 특정 클래스에 종속되지 않으므로 함수라고 부른다.
하지만 메서드처럼 파라미터 리스트, 바디, 반환 형식 등을 포함한다.
전달
람다 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 있다.
간결성
익명 클래스처럼 자질구레한 코드를 구현할 필요가 없다.

어디에, 어떻게 람다를 사용하는가?

NOTE
함수형 인터페이스라는 문맥에서 람다 표현식을 사용할 수 있다!
public interface comparator<T> { int compare(T o1, T o2); } public interface Runnable { void run(); }
Java
복사
앞서 배웠던 Comparatorm Runnable 등이 함수형 인터페이스
함수 디스크립터
람다 표현식의 시크니처를 의미한다.
@FunctionalInterface
함수형 인터페이스임을 가리키는 어노테이션
해당 어노테이션을 붙였는데, 함수형 인터페이스가 아니라면 에러가 발생한다.

람다 활용 (실행 어라운드 패턴)

NOTE
실행 어라운드 패턴 ⇒ 실제 자원을 처리하는 코드를 설정과 정리 두 과정이 둘러싸는 패턴!
초기화/정리가 작업을 둘러싼다.
public String processFile() throw IOException { try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) { return br.readLine(); // <- 실제 필요한 작업을 하는 행이다. } }
Java
복사
try-with-resource 구문( 자원을 명시적으로 닫을 필요가 없음 )
public String processFile() throws IOException { BufferedReader br = null; try { br = new BufferedReader(new FileReader("data.txt")); // 초기화 return br.readLine(); // 실제 필요한 작업을 하는 행이다. } finally { if (br != null) { try { br.close(); // 정리 코드 부분 } catch (IOException e) { // Ignore the exception or handle it as per your application's requirement. } } } }
Java
복사
위의 코드를 일반적인 try-catch로 하면 이렇게된다.

1. 동작 파라미터화를 기억하라

NOTE
String result = processFile((BufferedReader br) -> br.readLine() + br.readLine());
Java
복사
processFile의 동작을 파라미터화한다!
현재 코드는 파일에서 한 번에 할 줄만 읽을 수 있다.
한 번에 두 줄을 읽거나, 가장 자주 사용되는 단어를 반환하려면 어떻게 해야하는가?
기존의 설정, 정리 과정은 재사용하고 processFile 메서드만 다른 동작을 수행하도록 명령할 수 있으면 좋을것이다.

2. 함수형 인터페이스를 통한 동작전달

NOTE
@FunctionalInterface public interface BufferedReaderProcessor { String process(BufferedReader b) throws IOException; } public String processFile(BufferedReaderProcessor p) throws IOException { ... }
Java
복사
함수형 인터페이스를 통한 값 전달

3. 동작 실행

NOTE
public String processFile(BufferedReaderProcessor p) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) { return p.process(br); } }
Java
복사
함수형 인터페이스의 process를 불러서 동작을 실행시킴

4. 람다 전달

NOTE
// 한 행을 처리하는 코드다. String oneLine = processFile((BufferedReader br) -> br.readLine()); // 두 행을 처리하는 코드다. String twoLines = processFile((BufferedReader br) -> br.readLine() + br.readLine());
Java
복사
람다 사용

함수형 인터페이스

NOTE
별도로 정리한다!

형식 검사, 형식 추론, 제약

형식 검사

NOTE
람다가 사용되는 컨텐스트(context)를 이용해서 람다의 형식(type)을 추론할 수 있다!
List<Apple> heavierThan150g = filter(inventory, (Apple apple) -> apple.getWeight() > 150);
Java
복사
어떤 콘텍스트에서 기대되는 람다 표현식의 형식을 대상 형식이라 부른다.
1.
filter 메서드의 선언 확인
2.
filter 메서드의 두 번쨰 파라미터로 Predicate<Apple> 형식(대상 형식)을 기대
3.
Predicate<Apple>은 test라는 한 개의 추상 메서드를 정의하는 함수형 인터페이스
4.
test 메서드는 Apple을 받아 boolean을 반환하는 함수 디스크립터 묘사
5.
filter 메서드로 전달되는 인수는 이와 같은 요구사항을 만족해야함

같은 람다, 다른 함수형 인터페이스

NOTE
대상 형식이라는 특징 때문에 같은 람다 표현식도 호환되는 추상 메서드를 가진 다른 함수형 인터페이스로 사용할 수 있다!
Comparator<Apple> c1 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()); ToIntBiFunction<Apple, Apple> c2 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()); BiFunction<Apple, Apple, Integer> c3 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
Java
복사
동일 람다 이지만, 다른 함수형 인터페이스로 받을 수 있다!

형식 추론

NOTE
자바 컴파일러는 람다 표현식이 사용된 컨텍스트(대상 형식)을 이용해 람다 표현식과, 함수형 인터페이스를 추론한다!
//형식을 추론하지 않음 Comparator<Apple> c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()); //형식을 추론함 Comparator<Apple> c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight());
Java
복사
타입이 명시적이지 않으면 추한다!

지역 변수 사용

NOTE
람다 표현식에서는 익명 함수가 하는 것처럼 자유 변수를 활용할 수 있다!
// portNumber 변수를 캡처하는 람다 예제 int portNumber = 1337; Runnable r = () -> System.out.println(portNumber);
Java
복사
람다 캡쳐링
// portNumber에 값을 두 번 할당하므로 컴파일할 수 없는 코드다. int portNumber = 1337; Runnable r = () -> System.out.println(portNumber); portNumber = 31337;
Java
복사
람다 캡쳐링이 불가능한 상황
람다에서 접근하는 변수가 스택에 올라가 있으면, 해제되어도 접근을 시도할 수 있다. (문제원인!)