오늘 코드 리뷰 시간에 함수형 인터페이스 사용을 배웠다. 코드를 줄일 수 있는 새로운 방법을 배웠다!
근본적 질문
왜 함수형 프로그래밍이 주목받는가?
2000년대 초반까지는 CPU의 클럭 증가, 실행시간 최적화, cache 크기 증가를 통해 소프트웨어의 싱글 프로게스, 싱글 스레드의 속도가 증가했다. 그러나 전력 소모 및 발열 문제 등으로 단일코어를 통한 CPU 클럭 증가는 현실적으로 향상시키기 어려운 상황에 돌입하게 되었고, 이를 대안으로 코어 수를 늘려서 CPU 성능을 향상하게 되었다.
함수형 프로그래밍이 좋은 이유
1. 불변이라는 것 = 교착상태에 빠지지 않는다
- 함수형 프로그램은 대입문이 없기 때문에 기본적으로 한 번 값이 변수에 할당 되고 나면 이후에 값이 변경되지 않는다.
- 부수효과가 발생하지 않기 때문에 표현 식을 어느 때 실행하더라도 문제가 발생하지 않아 참조 투명성을 갖제 된다.
- 참조 투명성은 멀티코어 프로세스에서 교착상태에 빠지지 않는다는 장점이 있다.
2. 명확하다는 것
- 함수가 하는 일은 명확하게 정의되기 때문에 코드를 읽을 때 이해하기 쉽다.
- 함수의 결합을 통한 고차 함수를 사용할 때도 간결하게 표현할 수 있다.
- 함수를 갑으로 사용할 수도 있다.
- 익명 함수를 사용할 수도 있다.
함수형 인터페이스 Functional Interface
- 추상 메소드
abstract method
를 딱 하나만 가지고 있는 인터페이스 @FunctionalInterface
어노테이션을 붙이면 명시적으로 사용할 수 있다.- 자바에서 함수형 프로그래밍을 하면 함수를 일급 객체
First class object
로 사용할 수 있다.
1급 객체는 뭔가?
1급 객체의 조건
- 변수나 데이터에 할당할 수 있어야 한다.
- 객체의 파라미터로 넘길 수 있어야 한다.
- 객체의 리턴값으로 리턴할 수 있어야 한다.
자바스크립트의 일급객체와 다른 점은?
자바스크립트의 1급 객체 first class citizen
- 1급 객체 : 다른 객체들에게 적용 가능한 연산을 모두 지원하는 객체
- 예를 들어, (1) 매개변수로 전달되는 것
- (2) 함수에서 반환되는 것
- (3) 변수에 할당되는 것 등등
- 자바스크립트의 함수는 1급 객체이기 때문에 파라미터로 전달될 수 있다.
자바의 1급 객체
자바에서는 함수가 1급 객체에 해당되지 않아서, 변수에 함수를 할당하고 사용할 수 없다.
그러나 자바의 lambda를 사용하면 함수를 1급 객체로 취급하지 않는 자바를 어느정도 보완할 수 있다.
메소드가 1개만 존재하는 인터페이스 또는 클래스를 통해 마치 함수를 전달하는 것 처럼 사용할 수 있기 때문이다.
1급 컬렉션은 또 뭔가?
- Collection을 Wrapping하면서, 그 외 다른 멤버 변수가 없는 상태
예를 들면,
1
2
3
4
5
6
7
8
9
10
11
public class Person {
private String name;
private List<Car> cars;
// ...
}
public class Car {
private String name;
private String oil;
// ...
}
위의 객체의 콜렉션을 아래와 같이 wrapping 하는 것
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Person {
private String name;
private Cars cars;
// ...
}
// List<Car> cars를 Wrapping
// 일급 컬렉션
public class Cars {
// 멤버변수가 하나 밖에 없다!!
private List<Car> cars;
// ...
}
public class Car {
private String name;
private String oil;
// ...
}
1급 컬렉션의 장점
- 하나의 인스턴스에서 비즈니스 로직을 관리할 수 있다.
일급 컬렉션이 없다면, list로 묶어서 처리해야한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void getMoreThanThree() {
Car aCar = new Car();
Car bCar = new Car();
Car cCar = new Car();
List<Car> cars = new ArrayList<>();
cars.add(aCar);
cars.add(bCar);
cars.add(cCar);
cars.stream()
.filter(car -> car.getPosition() > 3)
.forEach(System.out::println);
}
일급 컬렉션을 만들어 사용한다면 아래와 같이 처리할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Cars {
private List<Car> cars;
public Cars(List<Cars> cars) {
this.cars = cars;
}
public List<Cars> getCars() {
return cars;
}
public List<Cars> getCarsOverPosition(int position) {
return cars.stream()
.filter(car.getPosition() > position)
.collect(Collectors.toList());
}
}
- 처음에
Car
객체를 생성해서 바로 불변 클래스Cars
로 관리하면 더 이상Car
가 추가되거나 삭제되지 않으니 빼먹는 실수가 없다 - 비즈니스 로직도 따로 서비스를 빼서 분리하지 않고
Cars
도메인에서 관리할 수 있다.
- 리스트 내의 객체의 상태를 동일하게 관리할 수 있다.
같은 비즈니스 로직에 따라 함께 관리할 수 있다.
reference
Java 8 에서 왜 함수형 프로그래밍이 도입되었을까? 함수형 인터페이스와 자바
1급 객체(First-class citizen) 란?
일급 컬렉션 (First Class Collection)의 소개와 써야할 이유
일급 컬렉션을 사용하는 이유
First Class Collection(일급 컬렉션)