앞서 읽던 이펙티브 자바에 함수형 프로그래밍
이 등장했다. 객체지향 프로그래밍과 같은 프로그래밍 방식의 한 종류인데, 자바에서는 이렇게 코드를 작성해본 적이 없기도 하고 개념도 잘 모르기 때문에 기록해보고자 한다.
함수형 프로그래밍의 개념은 사실 프로그래밍의 개념이 등장했을 때보다 더 빨리 등장했고, 람다 계산법은 1930년에 발명되었다.
(ex. 함수형 프로그래밍, 객체지향 프로그래밍, 구조지향 프로그래밍 밖에..)
= 프로그래밍을 하는 방법 => 따라서 언어에 독립적임
= 선언적인 방식으로 구현되어 흐름 제어를 명시적으로 기술하지 않고 프로그램 로직을 표현하는 프로그래밍
= 함수
단위로 개발을 하는 기법
1급 객체(함수)
자바에서는 함수형 인터페이스(추상메서드 only 하나)를 통해 구현함.
일급 객체의 조건!
1.전달인자로 전달 가능
2.동적 프로퍼티 할당이 가능
3.변수 or 데이터 구조 안에 담기 가능
4.return값으로 활용 가능
5.할당 시 (이름에 관계없이) 고유 객체로 구별 가능
순수 함수(pure function)
= 외부 요인에 영향을 받지 않는 함수
= 같은 입력 -> 같은 출력 반환
-> 부작용이 없는 함수 (다른 요인에 의한 결과 변화 X)
//ex.
public class MutateState {
public static void main(String[] args) {
List<String> names = Arrays.asList("Ajay", "Jaya", "Bruce");
List<String> result = new ArrayList<>();
names.stream()
.filter(name -> name.length() == 4)
.map(String::toUpperCase)
.forEach(nameInUpperCase -> result.add(nameInUpperCase));
System.out.println(result); //[AJAY, JAYA]
}
}
//람다 표현식에서 forEach를 활용해 마지막에 변하기 쉬운 리스트에 값을 대문자로 바꿔 넣어주고 있다
//이 점이 이 람다 표현식을 순수하지 못한 함수로 만든다 (결과 변화)
고차 함수
= 함수를 일급 객체로 취급한다고 하고, 함수를 인자로 받거나 결과로 반환하는 함수
-> 인자로 함수를 전달 O
-> return값으로 함수 사용 O
익명 함수 (람다식과 연관!)
= 이름이 없는 함수(람다식으로 표현!)
= 함수 호출 or 반환 시 상수 표현식으로 작성된 함수
메서드 체이닝 방식으로 함수 연결
= 말 그대로 메서드를 엮어서 계속해서 사용하는 방법
//ex.
public class ChainingStudent {
private int age;
private String name;
//기존의 setter와는 다르게 반환을 객체로 해준다
public int ChainingStudent setAge(int age) {
this.age = age;
return this;
}
public int ChainingStudent setName(String name) {
this.name = name;
return this;
}
public int getAge() {
return this.age;
}
}
@Test
public void testChaining() {
ChainingStudent st = new ChainingStudent();
st.setAge(24).setName("SK");
System.out.print(st.getAge()); //24
}
교착상태
를 막아줌 -> 정리!!!) = 익명 함수 형태로 구현
->
를 통해 매개변수를 함수 body로 전달그 쓰임에 대해 알아보자.
interface Calculator {
int sum(int a, int b);
}
public Class Cal {
public static void main(String[] args) {
Calculator c = (int a, int b) -> a+b; //이게 람다함수!!
//위를 Calculator c = (a, b) -> a+b; 로 바꿔도 됨
//Integer.sum()과 동일하므로 c = Integer::sum; 으로 바꿔도 됨
int res = c.sum(1,2);
}
}
//여기서 인터페이스의 메서드가 하나여야만 람다함수를 사용할 수 있다!
//그래서 @FunctionalInterface 어노테이션을 활용하면 좋음!
//(구현상의 오류를 방지하기 위함)
Bifunction
, BinaryOperator
)Bifunction<Integer, Integer, Integer>
은 순서대로 입력 2개, 출력 1개임을 의미해준다. 여기서 Bifunction
의 apply
메서드를 활용하면 (a, b)->a+b
가 실행된다.BinaryOperator
는 입출력의타입이 동일할 때 간단하게 표현하기 위해 사용된다.BinaryOperator<Integer> bo = (a,b)->a+b
+링크참조
= '흐름'이란 의미
= 함수형 프로그래밍에서 단계적으로 정의된 계산을 처리하기 위한 인터페이스
스트림생성().중간연산().최종연산()
=> 메서드 체이닝 형태로 구현!간단히 예를 하나 들어보자.
주어진 정수 배열 int[] data = {1,3,5,2,4,6,7,9}
가 있다.
이 배열에서 짝수만을 뽑아 역순으로 정렬하는 코드를 작성해보자.
원래와 같다면,
List<Integer> even = new ArrayList<>();
for(int i = 0; i < data.length; i++) {
if(data[i]%2==0) even.add(data[i]);
}
Collections.sort(even, Comparator.reverseOrder());
int[] res = new int[even.length];
for(int i = 0; i < res.length; i++) {
res[i] = even.get(i);
}
이만큼이나 긴 코드를 작성해야 한다.
그러나 stream을 사용하면,
int[] res = Arrays.stream(data).boxed().filter((a)->a%2==0).sorted(Comparator.reverseOrder()).mapToInt(Integer::intValue).toArray();
이렇게 메서드 체이닝 형태로 한방에 끝난다!
boxed()
= Intstream을 Integer의 Stream으로 변경한다.mapToInt(Integer::intValue)
로 Integer의 Stream을 IntStream으로 변경한다.anyMatch(e->e.contains("a"))
filter(e->e.contains("a"))
~.stream().map(e->Paths.get(e))
anyMatch
, allMatch
, noneMatch
List<Integer> num = Arrays.asList(1,2,3);
Integer reduceNum = num.stream().reduce(10, (a, b)->a+b);
List<String> res = list.stream().map(e->e.toUpperCase()).collect(Collectors.toList());
https://wikidocs.net/157858
https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/util/function/package-summary.html
https://dinfree.com/lecture/language/112_java_9.html
https://blog.knoldus.com/functional-java-understanding-pure-functions-with-java/
https://namu.wiki/w/%EA%B3%A0%EC%B0%A8%20%ED%95%A8%EC%88%98
https://dreamcoding.tistory.com/60
https://www.baeldung.com/java-8-streams-introduction
https://modimodi.tistory.com/24
https://wikidocs.net/167364