함수형 프로그래밍?
- 함수를 값으로 취급한다.
- 0개 이상의 인수를 가지며, 한 개 이상의 결과를 반환해야하고, 부작용(no side effect)이 없어야 한다.
- 함수나 메서드가 어떤 예외도 일으키지 않아야 한다.
여기서 부작용(Side Effect)이란
- 자료구조를 고치거나 필드에 값을 할당(setter 메서드 같은 생성자 이외의 초기화 동작)
- 예외 발생
- 파일에 쓰기 등의 I/O 동작 수행
함수형 프로그래밍의 주요 특성
1. 불변(Immutable) 객체를 사용한다
- 원본은 변하지 않기 때문에, 객체의 상태를 바꿀 수 없으므로 thread safe하다.
2. 참조 투명성(Referential Transparency) // Pure Function
- 동시에 함수를 처리해도 사이드이펙트 없이 동일한 값 출력시킬 수 있어야한다
- 외부의 값을 참조하지 않고 있어야한다.
3. lazy evaluation
- 실제로 필요로 해지는 경우에 연산을 시작
4. 코드를 간결하게 해준다.
5. 불변과 에러가 없기 때문에 병렬처리하기가 좋다.
람다(lambda)
- 람다(lambda)라는 용어는 람다 미적분학 학계에서 개발한 시스템에서 유래했다.
- 메서드를 하나의 ‘식(expression)’으로 표현한 것, 람다식은 함수를 간략하면서도 명확한 식으로 표현할 수 있게 해준다.
- 익명 클래스처럼 이름이 없는 함수면서 메서드를 인수로 전달할 수 있다.
한마디로 정리하는 사용하는 이유?
- 익명클래스를 함수의 파라미터로 전달하게 되면 불필요한 클래스를 선언하게 되므로 함수형 인터페이스를 이용해서 구현하면 코드가 간결해지기 때문
특징
- 익명
- 보통의 메서드와 달리 이름이 없으므로 익명이라 표현
- 함수
- 특정 클래스에 종속되지 않으므로 함수라고 부른다. 하지만 메서드처럼 파라미터 리스트, 바디, 반환 형식, 가능한 예외 리스트를 포함
- 전달
- 람다 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 있다.
- 간결성
- 익명 클래스처럼 복잡하지 않고 간단하다.
메서드와 함수의 차이?
- 메서드는 함수와 같은 의미이지만 특정 클래스에 반드시 속해야 한다는 제약이 있기 때문에 기존 함수와 같은 의미의 다른 용어를 선택해서 사용
- 그런 람다식을 통해 메서드가 하나의 독립적인 기능을 하기 때문에 함수라는 용어를 사용
샘플 예제 - Sample Car List
List<Car> carList = Arrays.asList(
new Car("k3", 1800, GASOLINE, true),
new Car("avante", 2300, GASOLINE, true),
new Car("avanteN", 3100, GASOLINE, true),
new Car("sonata", 2700, HYBRID, true),
new Car("gv70", 6000, GASOLINE, true),
new Car("gv70E", 6000, ELECTRIC, true),
new Car("gv60", 7000, ELECTRIC, true),
new Car("benzC", 6400, DIESEL, false),
new Car("benzC", 6700, GASOLINE, false),
new Car("g80", 8000, GASOLINE, true),
new Car("benzE", 8700, GASOLINE, false)
);
차량 정보를 담고있는 리스트가 있을 때 가격순으로 정렬을 한다면?
1. 기존의 방법
Collections.sort(carList, new Comparator<Car>() {
@Override
public int compare(Car car1, Car car2) {
return car1.getPrice() - car2.getPrice();
}
});
2. 람다식 사용
Collections.sort(carList, (car1, car2) -> car1.getPrice() - car2.getPrice());
람다의 구조
- 파라미터 리스트
- Comparator의 compare 메서드 파라미터 (차량 두개)
- 화살표
- 화살표 ( → ) 는 람다의 파라미터 리스트와 바디를 구분
- 람다 바디
- 두 차량의 가격을 비교, 람다의 반환값에 해당하는 표현식
람다의 장점!
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> result = new ArrayList<>();
for (T e : list) {
if (p.test(e)) {
result.add(e);
}
}
return result;
}
- 람다와 동작 파라미터화로 반복되는 코드를 추상화하고 유연하고 간결한 코드를 구현할 수 있다.
//차량 리스트에서 모델이 GV70 이면 리스트에 담음.
List<Car> carGV70 = filter(carList, car -> car.getModel() == Model.GV70);
//차량 리스트에서 모델이 avante 이면 리스트에 담음.
List<Car> carGV70 = filter(carList, car -> car.getModel() == Model.avante);
//차량 리스트에서 모델이 sonata 이면 리스트에 담음.
List<Car> carGV70 = filter(carList, car -> car.getModel() == Model.sonata);
인터페이스를 사용하지 않는다면 각각의 모델마다 filterGV70, filterAvante . . . 반복되는 메서드를 구현해야 한다.
주의사항
- 함수형 인터페이스라는 문맥에서 람다 표현식을 사용할 수 있다.
- 함수형 인터페이스 라는 것은 단 하나의 추상 메서드만 존재해야 한다.
- @FunctionalInterface 어노테이션을 사용하자.
- 함수형 인터페이스 인지 컴파일 체크를 해준다.
- 누군가 실수로 메서드를 추가하지 못하게 막아준다.
- **자유 변수를 사용(람다 캡쳐링)**할 때 final, 혹은 실질적으로 final로 선언된 변수와 똑같이 사용되어야 한다.
- 자유 변수 = 파라미터로 넘겨진 변수가 아닌 외부에서 정의된 변수
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
//문제 없는 코드
public class Example {
public static void main(String[] args) {
int num = 300; //지역변수
Runnable r = () -> System.out.println(num);
r.run();
}
}
//에러 발생
public class Example {
public static void main(String[] args) {
int num = 300; //지역변수
Runnable r = () -> System.out.println(num);
r.run();
num++; //<-- 지역변수가 바뀐다.
}
}
자유 변수의 제약이 생긴 이유?
- 인스턴스 변수(힙 영역)와 지역 변수(스택 영역)는 메모리에 저장되는 위치부터 다름
람다에서 지역 변수에 바로 접근할 수 있다는 가정하에 람다가 스레드에서 실행이 되면 변수를 할당한 스레드가 사라져서 변수 할당이 해제되었는데도 람다가 실행하는 스레드에서는 해당 변수에 접근하려 할 수 있습니다. 따라서 자바 구현에서는 원래 변수에 접근을 허용하는 것이 아니라 자유 지역 변수의 복사본을 제공합니다.
따라서 복사본의 값이 바뀌지 않아야 하므로 지역 변수에는 한 번만 값을 할당해야 한다는 제약이 생겼습니다.
- 람다는 이름이 없고 문서화도 못하기 때문에 코드 자체로 동작이 명확히 설명되지 않거나 코드 줄 수가 많아지면 람다를 쓰지 말아야 한다.
- 람다식이 세 줄을 넘어가면 가독성이 심하게 나빠진다.
[참고]
https://www.youtube.com/watch?v=V1u3aqV-qXg&ab_channel=SKplanetTacademy
'자바 (ref. 자바의정석)' 카테고리의 다른 글
Object 클래스 - JAVA (0) | 2022.04.11 |
---|---|
제어자(modifier) , 추상클래스 - JAVA (0) | 2022.04.06 |
기본형 매개변수와 참조형 매개변수 - JAVA (0) | 2022.04.04 |
기본형(Primtive Type) - JAVA (0) | 2022.03.28 |
람다식(Lambda expression) - JAVA (0) | 2022.03.17 |