[github 코드]
- 함수형인터페이스 직접 만들어본 코드는 아래에 나머지 샘플 코드들은 깃헙에 푸쉬해놓음
- https://github.com/imjinbro/javaBasic/tree/master/src/com/jinbro/source/fp
[FP를 OOP에서 왜?]
- FP가 필요한 부분이 있으니깐 사용하겠다
1) 동시성 side effect 없애기 : 멀티쓰레딩 공유자원 안전
- 객체 상태 변화에 민감한 부분에서 순수함수형프로그래밍이 좋다 : 같은 input -> 같은 output
- 변경 개념이 아니라 복사되고 복사된 것이 함수를 거쳐 결과값으로 : side effect 없앰
2) 함수에만 신경쓰면 됨 : 메서드에만 집중가능함
- 객체지향설계 메서드 single responsibility principle과 비슷함, side effect를 생각해야하는가 차이가 남
- 쪼개서 생각하기
- 간결해짐 : 퍼즐 맞추기 같은 느낌이랄까
[Functional Programming]
- 함수로 프로그래밍 하는 것
1) 함수
- 명령들의 집합이 아니고 X
- 수학의 함수 : input -> output
- 표현식(expression) : input -> output, 반드시 결과값이 있음
2) 함수를 만들어내는 방법 중 하나인 람다식
- 익명함수 표현식 : 이름없지만 동작하는 함수를 만들어냄
- 자바에서는 1개의 abstract 메서드를 가지는 인터페이스 익명클래스 구현하는 것
- 표현이 간결해짐 : 추상화된 표현이 가능함, 람다식을 사용하면 그 인터페이스 타입의 메서드를 구현하는구나 컴파일러가 추론
- 파라미터 개수(제네릭을 사용한다는 가정하에)만 맞으면 다양한 함수를 만들어 낼 수 있음
[자바에서 FP를 어떻게하나 - 제공되는 API 함수형 인터페이스를 사용하면서 알아보기]
- 패턴은 이렇다
1) 1개 abstract 메서드를 가진 interface를 선언함
2) 특정 객체의 메서드의 파라미터 타입이 위에서 만든 인터페이스 타입
3) 특정 객체의 메서드를 호출할 때 원래라면 인터페이스를 구현하는 new 인터페이스 긴 코드를 작성해야하지만
람다식을 사용하면 컴파일러가 알아서 인터페이스를 구현하는구나 추론하고 1개의 abstract 메서드와 바인딩함
0) 특정 객체의 메서드 내부적으로는 인터페이스.abstract로 선언된 메서드()를 하는 것임
[대표적인 함수형 인터페이스를 사용하면서 위의 패턴 익혀보기] : 설명이랑 활용은 위 깃헙
- java.util.function
- @FunctionalInterface : 단 1개의 abstract 메서드만을 갖도록 강제함
1) Function<T,P> : <T> -> <P>
2) Consumer<T> : <T> -> Void
3) Predicate<T> : <T> -> Boolean
4) Supplier<T> : lazy evaluation
[직접 함수형인터페이스를 만들어서 패턴 익혀보기 : 람다식의 장점을 느껴보자]
1) abstract method를 1개만 가지는 인터페이스 : 객체로 생성되지만 1개의 함수처럼
2) @FunctionalInterface 어노테이션을 붙여놓으면 abstract 메서드가 1개인지 컴파일 타임 체크
- 단 default, static 메서드는 체크X : java8부터 추가가능
3) 람다식을 사용할 수 있음 : 익명메서드, 추론이 가능하기때문에(그자리가 어떤 객체 타입의 메서드인지)
- 내부적으로는 익명클래스구현과 같다고함
4) 람다식을 사용하면 그떄마다 필요한 함수를 갈아끼우는 효과
- 파라미터 개수만 맞다면 : 타입파라미터를 사용하면 타입은 생각안해도되니깐
import java.math.BigDecimal;
import java.util.function.Supplier;
@FunctionalInterface
public interface MFuncInterface<T, R> {
R apply(T data);
//R print(T data); : 2개의 abstract 메서드가 있으면 람다식을 사용할 수 없음 : 어떤 메서드인지 구분X
}
@FunctionalInterface
interface BigDecimalToCurrency{
/* NONE-Generic : 명확한 경우에는 <T> 타입파라미터를 받지않고 명시적으로 지정해놓음 : 람다식을 생성하는 쪽에서 쓰면 추론 */
String toCurrency(BigDecimal value);
}
class MFuncInterfaceUse {
public static void main(String[] args) {
/*
method("Jinhyung", new MFuncInterface<String, String>() {
@Override
public String apply(String data) {
return "Hello! " + data;
}
});
*/
//개수만 같지 전혀 다른 메서드
method("Jinbro", s -> "Hello! " + s);
method("박진형", s -> s + "님 저희 지점을 방문해주셔서 감사합니다");
method(3, s -> s + " 2 1 0 땡!");
method(3, s -> s + "^2 = " + s*s);
hello(true, () -> "Jinbro");
hello(false, () -> "Jinbro");
BigDecimalToCurrency bigDecimalToCurrency = bd -> "$" + bd.toString();
System.out.println(bigDecimalToCurrency.toCurrency(new BigDecimal(120.00)));
}
static <T> void method(T data, MFuncInterface<T, String> func){
//T는 타입파라미터를 받고(추론), R은 String 고정, 내부적으로 func의 apply 메서드 사용
System.out.println(func.apply(data));
}
//내부적으로는 어떻게 사용되나 테스트해봄 : Supplier의 get()
static void hello(boolean isMember, Supplier<String> getName){
if(isMember){
System.out.println(getName.get() + "님 환영합니다");
} else {
System.out.println("회원가입이나 로그인을 해야합니다");
}
}
}
[제약사항]
1) 람다식을 쓴다면 최소한 인터페이스 타입 객체가 생성될 때 타입파라미터가 있어야함
- 아무런 정보없이 람다식을 사용하면 타입추론이 어려워서 컴파일 단계에서 에러남
- 함수형 인터페이스의 메서드가 제네릭 메서드인 경우 : 호출될 때 비로소 타입을.....
- 클래스 타입파라미터 사용하기
@FunctionalInterface
public interface InvaildFuncInterface {
<T> String print(T value);
}
class InvalidFuncInterfaceUse{
public static void main(String[] args) {
/* 호출할 때 value가 비로소 어떤 타입인지 알 수 있음 : 추론 불가 */
//getPrint(1, s -> s.toString());
}
public static <T> void getPrint(T value, InvaildFuncInterface invalidFuncInterfaceUse){
System.out.println(invalidFuncInterfaceUse.print(value));
}
}
[함수형인터페이스 - 람다식 이럴 때 쓴다, 이렇게 쓴다] : 코드는 위 깃헙에...
1) 람다식을 사용할 수 있는 여지를 만들어준다 : 함수형인터페이스 메서드 동작까지 구현
- 예를 들어 Function<T, R> 이라면 function.apply(T t) 해놓으면 호출부에서 람다식으로 구현
- 실제 함수몸통은 람다식, 이럴 때 호출된다는 것은 그 인터페이스 타입 객체가 구현된 곳에
2) 4가지 API 함수형 인터페이스 적절하게 사용
- Function<T, R> : 작업으로 타입 변환할 때
- Consumer<T> : 작업은 하되 딱히 리턴되는 것이 없을 때
- Predicate<T> : 작업하면서 true, false 작업이 필요할 때
- Supplier<T> : 작업을 지연시켜야할 때 혹은 특정 시점에만 작업될 수 있도록 할 때
[추가적인 내용]
1) Predicate<? super T>
- T가 ?의 super 타입이면 됨
- 작업에서 T가 가진 메서드를 사용하면 될 때
'java' 카테고리의 다른 글
[Java] 람다식 - 단순히 syntactic sugar 아니네요 (0) | 2017.11.14 |
---|---|
[Java] 자료구조 API - 컬렉션프레임워크 (0) | 2017.11.08 |
[Java] 제네릭, 와일드카드 - 제네릭, 와일드카드는 깊게 알수록 복잡하다 (0) | 2017.10.28 |
[Java] 쓰레드 - 쓰레드, 멀티쓰레드(우선순위, 쓰레드 그룹, 쓰레드풀) 등 (0) | 2017.10.25 |
[Java] 표준 API #5 - Calendar, Date, Format, java.time 패키지 (0) | 2017.10.19 |
댓글