💎 함수형 인터페이스 (Funtaional Interface)
public interface FunctionalInterface {
public abstract void doSomething(String text);
}
- 함수형 인터페이스는 1개의 추상 메소드를 갖고 있는 인터페이스를 말한다
- Single Abstract Method(SAM) 이라고 불리기도 한다
1) 함수형 인터페이스를 사용하는 이유
함수형 인터페이스를 사용하는 이유는 자바의 람다식은 함수형 인터페이스로만 접근이 되기 때문이다
예시를 통해 살펴보자!
public interface FuntionalInterface {
public abstract void doSomething(String text);
}
FunctionalInterface func = text -> Systsem.out.println(text);
func.doSomething("do something");
위의 코드에서 변수 func는 람다식으로 생성한 객체를 가리키고 있다
doSomething() 에 인자로 문자열을 전달하면 람다식에 정의된 것처럼 로그로 출력한다
Output:
do something
이번엔 위의 코드를 익명 클래스를 사용하여 리팩토링 해보자
함수형 인터페이스와 람다식으로 익명 클래스를 간단하게 표현했다고 생각할 수 있다
FunctionalInterface func = new FunctionalInterface() {
@Override
public void doSomething(String text) {
System.out.println(text);
}
};
func.doSomething("do Something");
정리하면! 함수형 인터페이스를 사용하는 것은 람다식으로 만든 객체에 접근하기 위해서이다
위의 예시처럼 람다식을 사용할 때마다 함수형 인터페이스를 매번 정의하기에는 불편하기 때문에 자바에서 라이브러리로 제공하는 것들이 있다
2) 기본 함수형 인터페이스
자바에서 기본적으로 제공하는 함수형 인터페이스는 다음과 같은 것들이 있다
- Runnable
- Supplier
- Consumer
- Function<T,R>
- Predicate
이 외에도 더 많은 인터페이스들은 자바 공식문서에 가면 확인할 수 있다!
https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
3) Runnable
public interface Runnable {
public abstract void run();
}
Runnable은 인자를 받지 않고 리턴값도 없는 인터페이스이다
Runnable runnable = () -> System.out.println("run anything");
runnable.run();
위의 코드처럼 사용할 수 있다
Output:
run anything!
Runnable은 run() 을 호출해야 한다
함수형 인터페이스마다 run() 과 같은 실행 메소드 이름이 다른데, 이는 인터페이스 종류마다 만들어진 목적이 다르고, 그 목적에 맞는 이름을 실행 메소드 이름으로 정했기 떄문이다
4) Supplier
public interface Supplier<T> {
T get();
}
Supplier<T>는 인자를 받지 않고 T 타입의 객체를 리턴한다
Supplier<String> getString = () -> "Happy new yaer!";
String str = getString.get();
System.out.println(str);
위의 코드처럼 사용할 수 있다
Output:
Happy new year!
5) Consumer
public interface Consumner<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
Consumer<T>는 T 타입의 객체를 인자로 받고 리턴 값은 없다
Consumer<String> printString = text -> System.out.println("Miss " + text +"?");
printString.accept("me");
위의 코드처럼 accept() 메소드를 이용하여 사용하면 된다
Output:
Miss me?
Consumer<String> printString = text -> System.out.println("Miss " + text "?");
Consumer<String> pringString2 = text -> System.out.println("--> Yes");
printString.andThen(printString2).accept("me);
또한, 위와 같이 andThen()을 사용하면 두 개 이상의 Consumer를 연속적으로 실행할 수 있다
Output:
Miss me?
--> Yes
6) Function
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requiredNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
Function<T, R>는 T 타입의 인자를 받고, R 타입의 객체를 리턴한다
Function<Integer, Integer> multiply = (value) -> value * 2;
Integer result = multiply.apply(3);
System.out.println(result);
위와 같이 apply() 메소드를 이용하여 사용할 수 있다
Output:
6
compose() 는 두 개의 Function을 조합하여 새로운 Function 객체를 만들어주는 메소드이다
주의할 점은 andThen()과는 실행 순서가 반대로 compose()에 인자로 전달되는 Function이 먼저 수행되고 그 이후에 호출되는 객체의 Function이 수행된다는 것이다!
Function<Integer, Integer> multiply = (value) -> value * 2;
Function<Integer, Integer> add = (value) -> value + 3;
Function<Integer, Integer> addThenMultiply = multiply.compose(add);
Integer result1 = addThenMultiply.apply(3);
System.out.println(result1);
예를 들어, 위와 같이 compose를 사용하여 새로운 Function을 만들 수 있다
apply를 호출하면 add 먼저 수행되고 그 이후에 multiply가 수행된다
Output:
12
7) Predicate
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicaet<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
Prediate<T> 는 T타입 인자를 받고 결과로 boolean을 리턴한다
Predicate<Integer> isBiggerThanFive = num -> num > 5;
System.out.println("10 is bigger than 5? -> " + isBiggerThanFive.test(10));
위와 같이 test() 메소드를 이용하여 사용할 수 있다
Output:
10 is bigger than 5? -> true
Predicate<Integer> isBiggerThanFive = num -> num > 5;
Predicate<Integer> isLowerThanSix = num -> num < 6;
System.out.println(isBiggerThanFive.and(isLowerThanSix).test(10));
System.out.println(isBiggerThanFive.or(isLowerThanSix).test(10));
위와 같이 and()와 or() sms ekfms Predicate와 함께 사용된다
직관적으로 and()는 두 개의 Predicate가 true일 때 true를 반환하고, or() 는 두 개 중 하나만 true이면 true를 반환한다
Output:
false
true
Predicate<String> isEquals = Predicate.isEqual("Google");
isEqulas.test("Google");
isEqula() 은 static 메소드로, 인자로 전달되는 객체와 같은지 체크하는 Predicate 객체를 만들어준다
Output:
true
참고)
https://codechacha.com/ko/java8-functional-interface/
'야미스터디 > Java' 카테고리의 다른 글
[Java] 익명 클래스 vs 람다식 (0) | 2022.12.11 |
---|---|
[Java - Stream] 04. mapping (0) | 2022.12.09 |
[Java - Stream] 03. filtering (0) | 2022.12.07 |
[Java - Stream] 02. 컬렉션과 Stream의 구현방식의 차이 (0) | 2022.12.07 |
[Java - Stream] 01. 스트림을 학습해야 하는 이유 (0) | 2022.12.07 |
댓글