함수형 인터페이스(Functional interface)는 1개의 추상 메소드를 갖고 있는 인터페이스를 말합니다. Single Abstract Method(SAM)라고 불리기도 합니다.
예를들어, 아래와 같은 인터페이스를 함수형 인터페이스라고 합니다.
public interface FunctionalInterface {
public abstract void doSomething(String text);
}
이 글에서는 함수형 인터페이스의 기본적인 사용 방법 및 예제에 대해서 알아보겠습니다.
1. 함수형 인터페이스를 사용하는 이유?
함수형 인터페이스를 사용하는 이유는 자바의 람다식은 함수형 인터페이스로만 접근이 되기 때문입니다.
예를 들어, 아래 코드에서 변수 func는 람다식으로 생성한 객체를 가리키고 있습니다.
doSomething()
에 인자로 문자열을 전달하면 람다식에 정의된 것처럼 로그로 출력을 합니다.
public interface FunctionalInterface {
public abstract void doSomething(String text);
}
FunctionalInterface func = text -> System.out.println(text);
func.doSomething("do something");
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
이 외에도 다양한 것들이 있습니다. 자바의 java.util.function 패키지에 정의되어있으니 더 많은 것을 확인하고 싶으시면 이곳을 확인해주세요.
이제, 예제와 함께 대표적인 함수형 인터페이스들을 어떻게 활용할 수 있는지 알아보겠습니다.
3. Runnable
Runnable
은 인자를 받지 않고 리턴값도 없는 인터페이스입니다.
public interface Runnable {
public abstract void run();
}
아래 코드처럼 사용할 수 있습니다.
Runnable runnable = () -> System.out.println("run anything!");
runnable.run();
Output:
run anything!
Runnable은 run()
을 호출해야 합니다. 함수형 인터페이스마다 run()
과 같은 실행 메소드 이름이 다릅니다.
인터페이스 종류마다 만들어진 목적이 다르고, 그 목적에 맞는 이름을 실행 메소드 이름으로 정하였기 때문입니다.
4. Supplier
Supplier
public interface Supplier<T> {
T get();
}
아래 코드처럼 사용할 수 있습니다. get()
메소드를 호출해야 합니다.
Supplier<String> getString = () -> "Happy new year!";
String str = getString.get();
System.out.println(str);
Output:
Happy new year!
5. Consumer
Consumer<T>
는 T 타입의 객체를 인자로 받고 리턴 값은 없습니다.
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
아래 코드처럼 사용할 수 있습니다. accept()
메소드를 사용합니다.
Consumer<String> printString = text -> System.out.println("Miss " + text + "?");
printString.accept("me");
Output:
Miss me?
또한, andThen()
을 사용하면 두개 이상의 Consumer를 연속적으로 실행할 수 있습니다.
Consumer<String> printString = text -> System.out.println("Miss " + text + "?");
Consumer<String> printString2 = text -> System.out.println("--> Yes");
printString.andThen(printString2).accept("me");
Output:
Miss me?
--> Yes
6. Function
Function<T, R>
는 T타입의 인자를 받고, R타입의 객체를 리턴합니다.
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(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;
}
}
다음과 같이 사용할 수 있습니다. apply()
메소드를 사용합니다.
Function<Integer, Integer> multiply = (value) -> value * 2;
Integer result = multiply.apply(3);
System.out.println(result);
Output:
6
compose()
는 두개의 Function을 조합하여 새로운 Function 객체를 만들어주는 메소드입니다.
주의할 점은 andThen()
과는 실행 순서가 반대입니다. compose()
에 인자로 전달되는 Function이 먼저 수행되고 그 이후에 호출하는 객체의 Function이 수행됩니다.
예를들어, 다음과 같이 compose를 사용하여 새로운 Function을 만들 수 있습니다. apply를 호출하면 add 먼저 수행되고 그 이후에 multiply가 수행됩니다.
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);
Output:
12
7. Predicate
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 Predicate<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);
}
}
다음과 같이 사용할 수 있습니다. test()
메소드를 사용합니다.
Predicate<Integer> isBiggerThanFive = num -> num > 5;
System.out.println("10 is bigger than 5? -> " + isBiggerThanFive.test(10));
Output:
10 is bigger than 5? -> true
and()
와 or()
는 다른 Predicate와 함께 사용됩니다.
직관적으로 and()
는 두개의 Predicate가 true일 때 true를 리턴하며 or()
는 두개 중에 하나만 true이면 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));
Output:
false
true
isEqual()
은 static 메소드로, 인자로 전달되는 객체와 같은지 체크하는 Predicate 객체를 만들어 줍니다.
다음과 같이 사용할 수 있습니다.
Predicate<String> isEquals = Predicate.isEqual("Google");
isEquals.test("Google");
Output:
true
8. References
Related Posts
- Java - Unsupported class file major version 61 에러
- Java - String.matches()로 문자열 패턴 확인 및 다양한 예제 소개
- Java - 문자열 공백제거 (trim, replace)
- Java - replace()와 replaceAll()의 차이점
- Java - ArrayList 초기화, 4가지 방법
- Java - 배열 정렬(Sorting) (오름차순, 내림차순)
- Java - 문자열(String)을 비교하는 방법 (==, equals, compare)
- Java - StringBuilder 사용 방법, 예제
- Java - 로그 출력, 파일 저장 방법 (Logger 라이브러리)
- Java IllegalArgumentException 의미, 발생 이유
- Java - NullPointerException 원인, 해결 방법
- Seleninum의 ConnectionFailedException: Unable to establish websocket connection 해결
- Java - compareTo(), 객체 크기 비교
- Java - BufferedWriter로 파일 쓰기
- Java - BufferedReader로 파일 읽기
- Java charAt() 함수 알아보기
- Java - BigInteger 범위, 비교, 연산, 형변환
- Java contains()로 문자(대소문자 X) 포함 확인
- Java - Set(HashSet)를 배열로 변환
- Java - 문자열 첫번째 문자, 마지막 문자 확인
- Java - 문자열 한글자씩 자르기
- Java - 문자열 단어 개수 가져오기
- Java - 1초마다 반복 실행
- Java - 배열을 Set(HashSet)로 변환
- Java - 여러 Set(HashSet) 합치기
- Java - 명령행 인자 입력 받기
- Java - 리스트 역순으로 순회, 3가지 방법
- Java - 특정 조건으로 리스트 필터링, 3가지 방법
- Java - HashMap 모든 요소들의 합계, 평균 계산
- Java - 특정 조건으로 HashMap 필터링
- Java - 싱글톤(Singleton) 패턴 구현
- Java - 숫자 왼쪽에 0으로 채우기
- Java - String 배열 초기화 방법
- Java - 정렬된 순서로 Map(HashMap) 순회
- Java - HashMap에서 key, value 가져오기