목차
함수형 프로그래밍, 람다식, 스트림
14.1 함수형 프로그래밍의 소개
- 자바의 최근 버전은 함수형 프로그래밍과 병렬 처리를 강조하는 방향으로 나가고 있다.
프로그래밍 패러다임 분류
- 명령형 프로그래밍 (imperative programming)
- 절차 지향 언어 (C, FORTRAN)
- 객체 지향 언어 (C++, Java)
- 선언적 프로그래밍 (declarative programming)
- 논리 프로그래밍 (Prolog)
- 함수형 프로그래밍 (Haskell, Erlang)
명령형 프로그래밍 방법
- 정수가 담긴 ArrayList에서 짝수만 추려내는 예시
public class Imperative {
public static void main(String[] args) {
List<Integer> list = List.of(12, 3, 16, 2, 1, 9, 7, 20);
List<Integer> even = new ArrayList<>();
// 짝수를 찾는다.
for (Integer e : list) {
if (e % 2 == 0) {
even.add(e);
}
}
// 짝수를 출력한다.
for (Integer e : even) {
System.out.println(e);
}
}
}
- 명령형 프로그래밍은 작업을 어떻게 수행하느냐를 중시한다.
함수형 프로그래밍 방법
- 위의 예제를 다음과 같이 간략하게 작성할 수 있다.
public class Test {
public static void main(String[] args) {
List<Integer> list = List.of(12, 3, 16, 2, 1, 9, 7, 20);
list.stream()
.filter(e -> e % 2 == 0)
.forEach(System.out::println);
}
}
- stream() : 리스트 안의 원소들을 하나씩 추출하는 메소드
- filter() : 들어오는 정수 중에서 짝수만 추려냄
- 메소드 앞에 .이 찍힌 것은 메소드 체이닝 기법
- forEach() : 는 들어오는 각 정수에 대하여 전달받은 함수 적용
- System.out::println : 메소드 참조, 함수를 다른 함수로 보내는 방법
- 선언적 프로그래밍은 해야 할 일에 집중한다.
왜 함수형 프로그래밍인가?
- 함수형 프로그래밍은 선언적 프로그래밍 개념을 실제로 구현한 형태이다.
- 개발자는 부작용 없는 함수를 사용하여, 원하는 작업을 기술하고, 시스템에서 이것을 어떻게 구현할지를 결정한다.
- 자바에서는 함수형 프로그래밍의 일부를 스트림 API를 이용하여 지원한다.
- 함수형 프로그래밍은 병렬 처리가 쉽다
함수란 무엇인가?
- 함수는 부작용이 있을 수 있다.
함수가 실행하면서 외부의 변수를 변경한다는 의미 - 대표적인 예가 Random 클래스의 nextInt()
호출될 때마다 난수 발생기의 사태가 변경되고 따라서 반환값이 달라진다. - 함수형 프로그래밍에서 함수는 순수 함수 (부작용이 없는 함수) 라고 한다.
- 순수 함수는 스레드에 대하여 안전하고, 병렬적인 계산이 가능하다.
- 인수만 동일하다면 항상 동일한 결과를 반환한다.
자바와 함수형 프로그래밍
- 자바에서 순수 함수로만 프로그램을 작성한다는 것은 어렵다.
- Scanner 클래스의 nextLine()이나 Random 클래스의 nextInt() 등 사용
- 자바에서는 순수한 함수형 프로그래밍이라기보다는 함수형 스타일을 지원한다고 해야 한다.
- 자바에서 코드의 일부라도 함수형 스타일로 만드려면 어떻게 해야 할까?
- 지역 변수만을 변경 (다른 범위의 변수를 변경하면 안됨)
- 참조하는 객체는 변경 불가능해야 한다.
- 함수나 메소드가 예외를 발생시키지 않아야 한다
객체 지향 프로그래밍과 함수형 프로그래밍
14.2 람다식
함수의 1급 시민 승격
- 기초형의 값이나 객체, 배열 등은 자바에서 1급 시민.
즉, 모든 연산이 허용된 엔티티를 의미.
변수에 저장될 수 있고, 함수의 인수가 될수 있고, 함수에서 반환될 수 있다. - Java 8에서 함수가 1급 시민으로 승격 되었다.
즉, 함수가 값이 된 것. - 함수가 값이 되면 가능한 것
- 변수에 저장할 수 있다.
- 함수를 매개 변수로 받을 수 있다.
- 함수를 반환할 수 있다.
- 강력한 스트림 API의 사용이 용이해진다.
람다식의 필요성
- "동작 매개 변수화"를 사용하여 코드를 전달하는 것이 코드의 빈번한 요구 사항 변경에 대처하는 데 유용하다.
- 익명 클래스를 사용하여 코드 블록을 나타내는 것은 만족스럽지 않다
람다식이란?
- 나중에 실행될 목적으로 다른 곳에 전달될 수 있는 코드 블록
람다식 정의
- (argument) → (body) 구문을 사용하여 작성된다.
- 매개 변수 a와 b를 받아서 a+b를 계산하여 반환하는 메소드를 람다식으로 정의
(int a, int b) -> { return a+b; }
// 비교 원래
public int sum(int a, int b) {
return a+b;
}
- 람다식은 0개 이상의 매개 변수를 가질 수 있다.
- 화살표 →는 람다식에서 매개 변수와 몸체를 구분한다.
- 매개 변수의 형식을 명시적으로 선언할 수 있다. 또는 문맥에서 추정될 수 있다.
빈 괄호는 매개변수가 없음을 나타낸다. - 단일 매개 변수이고 타입은 유추가 가능한 경우에는 괄호를 사용할 필요가 없다.
- 몸체에 하나 이상의 문장이 있으면 중괄호로 묶어야 한다.
람다식의 활용
- 변수에 람다를 할당할 수 있다.
- 함수 인터페이스의 컨텍스트에서 람다식을 사용할 수 있다.
1. 이벤트 처리
// 이전의 방법
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("버튼 클릭!");
}
});
// 람다식을 이용한 방법
button.addActionListener(e -> {
System.out.println("버튼 클릭!");
});
2. Runnable 인터페이스 구현
// 이전의 방법
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("스레드 실행");
}
}).start();
// 람다식을 이용한 방법
new Thread(() -> System.out.println("스레드 실행")).start();
3. 배열에서 forEach()와 같은 함수형 프로그래밍 사용 가능
// 이전의 방법
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
for (Integer n : list) {
System.out.println(n);
}
// 람다식을 이용한 방법
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.forEach(n -> System.out.println(n));
14.3 동작 매개 변수화
14.4 함수형 인터페이스
14.5 메소드 참조
14.6 스트림 API
소소제목
Summary
Programming
'Java > 교재 정리' 카테고리의 다른 글
13 제네릭과 컬렉션 (1) | 2024.06.10 |
---|---|
09 자바 GUI 기초 (0) | 2024.05.03 |
*16 멀티 스레딩 (1) | 2024.05.02 |
08 자바 API 패키지, 예외 처리, 모듈 (0) | 2024.04.27 |
07 추상 클래스, 인터페이스, 중첩 클래스 (0) | 2024.04.26 |