인터페이스 | 메소드 | 설명 |
Predicate<T> | boolean test(T t) | 데이터 한 개를 받아 조건을 판단하여 boolean으로 반환할 때 사용 |
Consumer<T> | void accept(T t) | 반환값은 없고 하나의 매개변수 필요 받는값(매개변수)만 있으므로 소비자(Consumer) |
Supplier<T> | T get() | 매개로 받는 데이터는 없고 반환값이 없으므로 공급자(Supplier) |
Function<T, R> | R apply(T t) | 일반적인 형태의 메소드 매개변수 한 개, 반환 값 한 개가 있다. |
1) Predicate 인터페이스 사용 예
TestLambda03.java
import java.util.function.Predicate;
public class TestLambda03 {
public static void main(String[] args) {
int x = 5;
Predicate<Integer> p1 = (a) -> a % 2 == 0? true: false;
System.out.printf("%d는 %s다.%n", x, p1.test(x)? "짝수" : "홀수");
int age = 24;
Predicate<Integer> p2 = (a) -> a > 19 ? true: false;
System.out.printf("%d세는 %s이다.%n", age, p2.test(age)? "성인" : "미성년");
String str = "Seoul";
Predicate<String> p3 = (a) -> a.isEmpty() ? true: false;
System.out.printf("%s 문자열이다.%n", p3.test(str) ? "비어있는" : "비어있지 않은");
}
}
2) Consumer 인터페이스의 사용 예
ConsumerTest.java
import java.util.function.Consumer;
public class ConsumerTest {
public static void main(String[] args) {
Consumer<Integer> c1 = (x) -> System.out.println(x + "를 두 배 하면 "+ x*2);
c1.accept(4);
Consumer<String> c2 = (x) -> System.out.println(x + "의 길이: "+ x.length());
c2.accept("\"무궁화 꽃이 피었습니다.\"");
}
}
3) Supplier 인터페이스의 사용 예
SupplierTest.java
import java.util.function.Supplier;
public class SupplierTest {
public static void main(String[] args) {
Supplier<Integer> s = () -> (int) (Math.random() * 100);
for(int i=0; i<10; ++i) System.out.print(s.get() + " ");
}
}
4) Function 인터페이스의 사용
아래의 코드는 cm에 해당하는 값을 전달한 후 inch로 변환, inch 값을 전달해서 cm로 변환하는 작업을 apply 메소드로 정의한 것과 섭씨 온도와 화씨 온도를 각각 전달하여 변환하는 apply 메소드를 정의하였다.
FunctionTest.java
import java.util.function.Function;
public class FunctionTest {
public static void main(String[] args) {
Function <Double, Double> cm2inch = (num) -> (num * 0.3937007874); // cm를 inch로 변환
Function <Double, Double> inch2cm = (num) -> (num * 2.54); // cm를 inch로 변환
double cm = 1.0;
double inch = 1.0;
System.out.printf("%.5fcm는 %.5finch입니다 %n", cm, cm2inch.apply(cm));
System.out.printf("%.5finch는 %.5fcm입니다 %n", inch, inch2cm.apply(inch));
// 섭씨를 화씨온도로 변환
Function <Double, String> c2f = (c) -> " 화씨 " + ((c * 9.0/5.0) + 32) + " ºF";
// 화씨를 섭씨온도로 변환
Function <Double, String> f2c = (f) -> " 섭씨 " + ((f - 32) * 5.0/9.0) + " ºC";
double celsius = 26.5;
double fahrenheit = 46.5;
String temp = null;
temp = c2f.apply(celsius);
System.out.println("섭씨 " + celsius + temp);
temp = f2c.apply(fahrenheit);
System.out.println("화씨 " + fahrenheit + temp);
}
}
인터페이스 | 메소드 | 설명 |
BiFunction<T, U, R> | R apply(T t, U u) | 두 개의 매개변수를 T, U를 전달받아 R을 반환한다. |
BiConsumer<T, U> | void accept(T t, U u) | 두 개의 매개변수를 전달받아 연산하고 반환 값은 없다. |
BiPredicate<T, U> | boolean test(T t, U u) | 두 개의 매개변수를 전달받고 결과를 Boolean으로 반환한다. |
이번에 살펴볼 함수형 인터페이스는 인터페이스에 Bi- 라는 접두사(?)가 붙어있는데, 이것은 인터페이스 내에 선언된 메소드의 매개변수가 2개(Binary)임을 나타낸다고 생각하면 된다.
먼저, BiFunction 인터페이스의 추상메소드 apply는 두 개의 매개변수 T, U를 전달받아 연산한 후 R 타입의 결과를 반환 할 때 사용한다.
예를 들여 키가 185.5cm, 몸무게 98.5 인 남성의 표준체중을 알고자 하면
BiFunction<Double, Double, Double> biFunc;
로 선언하고,
(185.5, 98.5) -> 표준체중구하는 연산식;
의 형태로 람다식을 정의한 후 그 결과를 Double형 데이터로 반환 받을 수 있다.
이때, BiFunction 인터페이스의 3번째 Generic 은 반환받고자 하는 타입이다.
만약 반환받고자 하는 데이터의 타입이 문자열이라면
BiFunction<Double, Double, String> biFunc;
위와 같이 지정할 수 있다.
예제 코드를 작성하기 위해 표준 체중을 구하는 일반적인 공식을 알아보자
남성 표준 체중(kg) = 키(cm) x 키(cm) x 22
여성 표준 체중(kg) = 키(cm) x 키(cm) x 21
BMI(체질량지수) = 몸무게(kg) / 키(m)의 제곱
그럼, 위의 공식과 BiFunction 인터페이스를 이용하여 남성의 표준체중을 구해보자
아래의 예는 Function 인터페이스와 BiFunction 인터페이스 두가지를 모두 활용하여 비교해 본 것이다.
Function 인터페이스에서는 표준 체중을, BiFunction 인터페이스로는 BMI 계산 결과를 반환하는 람다식이다.
BiFunctionTest.java
import java.util.function.BiFunction;
import java.util.function.Function;
public class BiFunctionTest {
public static void main(String[] args) {
String male = "남";
double height = 185.5;
double weight = 98.5;
double maleStdWeight = 0;
String bmiResult = null;
Function<Double, Double> malefunc = (h) -> ((h * h * 0.0001) * 22);
maleStdWeight = malefunc.apply(height);
System.out.printf("키 %.2fcm인 남성의 표준체중은 %.2f입니다.%n", height, maleStdWeight);
BiFunction<Double, Double, String> biFunc = (h, w) -> String.format("키 %.2fcm, 몸무게 %.2fcm인 사람의 BMI는 %.2f입니다.%n", h, w, (w / (h * h * 0.0001)));
bmiResult = biFunc.apply(height, weight);
System.out.println(bmiResult);
}
}
위의 코드에서 maleFunc 는 키를 전달하여 표준체중을 Double형으로 반환 받는 Function 인터페이스의 사용 예이고,
BiFunction<Double, Double, String> biFunc의 메소드 정의는 두 개의 Double 형 데이터인 키와 몸무게를 전달한 후 BMI 결과를 문자열의 형태로 반환 받았다.
위의 코드에서 BiFunction의 세 번째 Generic은 String으로 선언되었음을 확인할 수 있다. 즉, 2개의 Double 타입을 전달하여 String으로 그 결과를 반환 받는 형태임을 나타내는 것이다.
이번에는 BiPredicate 인터페이스를 포함한 3가지 예를 한꺼번에 살펴보도록 하자.
앞서 언급하였던 일급함수의 특성 중 메소드를 전달하는 Lambda 식의 예도 함께 포함되어 있다.
먼저, BiPredicate 인터페이스의 추상 메소드는 2개의 전달인자를 받아 연산을 한 후 boolean 으로 결과를 반환하는 test() 메소드가 있다.
boolean test(T t, U u)
아래 코드 중 첫 번째 예는 두 값을 전달받아 첫 번째 값이 두 번째 값보다 크거나 같으면 true, 아니면 false를 반환하는 BiPredicate.인터페이스의 추상메소드를 정의하였다.
BiPredicateTest.javaBiPredicateTest.java
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BiPredicate;
public class BiPredicateTest {
public static void main(String[] args) {
int a = 10, b = 15;
BiPredicate<Integer, Integer> biPredict = (x, y) -> (x >= y) ? true: false;
if(biPredict.test(a, b))
System.out.println(a +"는 " + b +"이상이다.");
else
System.out.println(a +"는 " + b +"미만이다.");
// 메소드의 전달인자로 사용되는 Lambda 식
predict((str1, str2) -> (str1.compareTo(str2)) < 0 ? true: false );
List<String> flowers = Arrays.asList("장미", "국화", "수선화", "목련", "진달래");
// Collections.sort() 메소드의 2번째 전달인자로 Lambda 식을 전달하면 List 내의 데이터를 오름차순으로 정렬하여 반환한다.
Collections.sort(flowers, (f1, f2) -> f1.compareTo(f2));
System.out.println(flowers);
}
private static void predict(BiPredicate<String, String> strCompare) {
boolean result = strCompare.test("장미", "국화");
System.out.println(result? "장미가 국화보다 작은 문자열이다." : "장미가 국화보다 큰 문자열이다.");
}
}
그리고 두 번째 예에서는 람다식이 메소드의 전달인자로 사용되는 예이다.
myPredicate 메소드 호출 부분에는 두개의 문자열을 전달한 후 앞의 문자열이 뒤의 문자열보다 작으면 true, 아니면 false를 반환하는 익명함수가 argument임을 확인할 수 있다.
이 함수를 받을 수 있는 인자는 두 개의 값을 받아 boolean을 반환하는 BiPredicate 인터페이스 내에 정의된 test() 메소드이다.
코드 아래 정의된 myPredicate 메소드의 매개변수의 타입이 BiPredicate 인터페이스라는 것을 확인할 수 있다.
마지막으로 세 번째의 예에서는 java.util.Collections 클래스에는 sort라는 이름의 static 메소드가 있는데 이 메소드는 첫 번째 전달인자로 List 를 두 번째 전달인자로 Comparator 인터페이스 타입의 전달 받는 형태로 선언되어 있다.
static <T> void sort(List<T> list, Comparator<? super T> c)
여기에서 주목할 것은 두번째 전달인자가 Comparator인터페이스 인데,
Comparator 인터페이스는 @FunctionInterface로 선언되어 있다.
때문에 두 번째 전달인자는 Comparator 인터페이스 내의 추상 메소드인 compare(T , T) 타입에 맞도록 데이터를 전달하면 된다.
int compare(T o1, T o2)
위의 예에서는 sort() 메소드의 두 번째 전달인자를 List에서 두개의 문자열을 전달하여 람다식 내부에서는 다시 두 개의 문자열의 비교의 결과값을 int로 반환하는 compareTo를 호출하는 형태로 작성되었다.
이 sort 메소드를 이용하면 리스트 내의 문자열을 정렬할 수 있다.
BiConsumer 인터페이스의 accept() 메소드는 직접 정의해 보자.
void accept(T t, U u)
이전글: 3. Lambda 사용을 위한 문법
다음 글: 5. 컬렉션에 정의된 함수형 인터페이스
[Java] 5. 컬렉션에 정의된 함수형 인터페이스 (0) | 2023.08.17 |
---|---|
[Java] 3. Lambda 사용을 위한 문법 (0) | 2023.08.04 |
[Java] 2. JDK 8에 추가된 주요 문법(Lambda) (0) | 2023.08.03 |
댓글 영역