Java - 표준 API의 함수적 인터페이스
표준 API의 함수적 인터페이스
Java8부터는 java.util.function 표준 API 패키지를 통해 빈번하게 사용되는 함수적 인터페이스가 제공된다. 이 패키지 안에 있는 함수적 인터페이스들을 크게 Consumer, Supplier, Function, Operator, Predicate로 구분할 수 있다.
Consumer 함수적 인터페이스
인터페이스명 | 추상 메서드 | 설명 |
Consumer<T> | void accept(T t) | 객체를 T를 받아 소비 |
BiConsumer<T, U> | void accept(T t, U u) | 객체 T, U를 받아 소비 |
DoubleConsumer | void accept(double value) | double 값을 받아 소비 |
intConsumer | void accept(int value) | int 값을 받아 소비 |
LongConsumer | void accept(long value) | long 값을 받아 소비 |
ObjDoubleConsumer<T> | void accpet(T t, double value) | 객체 T, double 값을 받아 소비 |
ObjIntConsumer<T> | void accept(T t, int value) | 객체 T, int 값을 받아 소비 |
ObjLongConsumer<T> | void accept(T t, long value) | 객체 T, long 값을 받아 소비 |
위 표에서 알 수 있듯이, Consumer 인터페이스는 특정 객체를 소비한다. 소비한다라는 것은 매개 변수를 사용만 하고 리턴하지 않는다는 뜻이다. 그래서 추상 메서드의 리턴 형태가 void가 된다.
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
Consumer 인터페이스의 추상 메서드를 사용하기 위한 객체 Student를 위 코드와 같이 작성한다.
public class Main {
public static void main(String[] args) {
Consumer<String> c01 = value -> System.out.println("입력값 : " + value);
BiConsumer<String, Integer> c02 = (value01, value02) -> System.out.println("입력값01 : " + value01 + ", 입력값02 : " + value02);
IntConsumer c03 = value -> System.out.println("입력값 : " + value);
DoubleConsumer c04 = value -> System.out.println("입력값 : " + value);
LongConsumer c05 = value -> System.out.println("입력값 : " + value);
ObjIntConsumer<Student> c06 = (student, value) -> System.out.println("이름 : " + student.getName() + ", 숫자 : " + value);
ObjDoubleConsumer<Student> c07 = (student, value) -> System.out.println("이름 : " + student.getName() + ", 숫자 : " + value);
ObjLongConsumer<Student> c08 = (student, value) -> System.out.println("이름 : " + student.getName() + ", 숫자 : " + value);
c01.accept("홍길동"); // "입력값 : 홍길동" 출력
c02.accept("홍길동", 20); // "입력값01 : 홍길동, 입력값02 : 20" 출력
c03.accept(30); // "입력값 : 30" 출력
c04.accept(3.14); // "입력값 : 3.14" 출력
c05.accept(123456); // "입력값 : 123456" 출력
Student student = new Student("이순신");
c06.accept(student, 123456); // "이름 : 이순신, 숫자 : 123456" 출력
c07.accept(student, 3.14); // "이름 : 이순신, 숫자 : 3.14" 출력
c08.accept(student, 123456); // "이름 : 이순신, 숫자 : 123456" 출력
}
}
위에는 Consumer 인터페이스를 사용한 예시 코드이다.
입력값 : 홍길동
입력값01 : 홍길동, 입력값02 : 20
입력값 : 30
입력값 : 3.14
입력값 : 123456
이름 : 이순신, 숫자 : 123456
이름 : 이순신, 숫자 : 3.14
이름 : 이순신, 숫자 : 123456
실행 결과는 위와 같다.
Supplier 함수적 인터페이스
인터페이스명 | 추상 메서드 | 설명 |
Supplier<T> | T get() | T 객체를 리턴 |
BooleanSupplier | boolean getAsBoolean() | boolean 값을 리턴 |
DoubleSupplier | double getAsDouble() | double 값을 리턴 |
IntSupplier | int getAsInt() | int 값을 리턴 |
LongSupplier | long getAsLong() | long 값을 리턴 |
위 표에서 알 수 있듯이, Supplier 인터페이스는 특정 값을 리턴한다. 이때, 주의해야 할 점은 이 인터페이스는 매개변수를 갖지 않는다는 것이다. 따라서, 람다 바디 내에서 변수를 정의한 후 값을 리턴하도록 설계해야 한다.
public class Main {
public static void main(String[] args) {
String stringValue = "HelloWorld";
boolean booleanValue = true;
double doubleValue = 3.14;
int intValue = 123;
int longValue = 123456;
Supplier<String> supplier = () -> stringValue;
BooleanSupplier booleanSupplier = () -> booleanValue;
DoubleSupplier doubleSupplier = () -> doubleValue;
IntSupplier intSupplier = () -> intValue;
LongSupplier longSupplier = () -> longValue;
String s = supplier.get();
System.out.println("String 값 : " + s); // "String 값 : HelloWorld" 출력
boolean b = booleanSupplier.getAsBoolean();
System.out.println("boolean 값 : " + b); // "boolean 값 : true" 출력
double d = doubleSupplier.getAsDouble();
System.out.println("String 값 : " + d); // "String 값 : 3.14" 출력
int i = intSupplier.getAsInt();
System.out.println("int 값 : " + i); // "int 값 : 123" 출력
long l = longSupplier.getAsLong();
System.out.println("long 값 : " + l); // "long 값 : 123456" 출력
}
}
위에는 Supplier 인터페이스를 사용한 예시 코드이다.
String 값 : HelloWorld
boolean 값 : true
String 값 : 3.14
int 값 : 123
long 값 : 123456
실행 결과는 위와 같다.
Function 함수적 인터페이스
인터페이스명 | 추상 메서드 | 설명 |
Function<T, R> | R apply(T t) | 객체 T를 R로 매핑 |
BiFunction<T, U, R> | R apply(T t, U u) | 객체 T, U를 객체 R로 매핑 |
DoubleFunction<R> | R apply(double value) | double를 객체 R로 매핑 |
IntFunction<R> | R apply(int value) | int를 객체 R로 매핑 |
IntToDoubleFunction | double applyAsDouble(int value) | int를 double로 매핑 |
IntToLongFunction | long applyAsLong(int value) | int를 long으로 매핑 |
LongToDoubleFunction | double applyAsDouble(long value) | long을 double로 매핑 |
LongToIntFunction | int applyAsInt(long value) | long을 double로 매핑 |
toDoubleBiFunction<T, U> | double applyAsDouble(T t, U u) | 객체 T, U를 double로 매핑 |
위 표에서 알 수 있듯이, Function 인터페이스는 매개변수를 리턴 값으로 매핑(타입 변환)한다.
public class Student {
private int number;
private String name;
private int mathGrade;
private int engGrade;
public Student(int number, String name, int mathGrade, int engGrade) {
this.number = number;
this.name = name;
this.mathGrade = mathGrade;
this.engGrade = engGrade;
}
public int getNumber() {
return number;
}
public String getName() {
return name;
}
public int getMathGrade() {
return mathGrade;
}
public int getEngGrade() {
return engGrade;
}
}
Function 인터페이스의 추상 메서드를 사용하기 위한 객체 Student를 위 코드와 같이 작성한다.
public class Main {
public static void main(String[] args) {
Student student = new Student(1, "홍길동", 80, 100);
// 매핑 : Student 객체 -> Student의 Integer 값
Function<Student, Integer> function = s -> s.getNumber();
int result01 = function.apply(student);
System.out.println("Student 번호 : " + result01);
// 매핑 : 두 Integer 값 -> Double 값
BiFunction<Integer, Integer, Double> biFunction = (math, eng) -> (double) (math + eng) / 2;
double result02 = biFunction.apply(student.getMathGrade(), student.getEngGrade());
System.out.println("수학과 영어 성적의 평균 : " + result02);
// 매핑 : Double 값 -> Integer 값
DoubleFunction<Integer> doubleFunction = x -> {
Double d = Math.floor(x);
return d.intValue();
};
int result03 = doubleFunction.apply(3.14);
System.out.println("소수점 버리기 : " + result03);
// 매핑 : 두 Integer 값 -> Double 값
ToDoubleBiFunction<Integer, Integer> toDoubleBiFunction = (math, eng) -> (double) (math + eng) / 2;
double result04 = toDoubleBiFunction.applyAsDouble(student.getMathGrade(), student.getEngGrade());
System.out.println("수학과 영어 성적의 평균 : " + result04);
}
}
위 소스코드는 Function 인터페이스를 사용한 예시 코드이다.
Student 번호 : 1
수학과 영어 성적의 평균 : 90.0
소수점 버리기 : 3
수학과 영어 성적의 평균 : 90.0
실행결과는 위와 같다.
Operator 함수적 인터페이스
인터페이스명 | 추상 메서드 | 설명 |
BinaryOperator<T> | T apply(T t, T t) | T와 T를 연산한 후 T 리턴 |
UnaryOperator<T> | T apply(T t) | T를 연산한 후 T 리턴 |
DoubleBinaryOperator | double applyAsDouble(double value, double value) | 두 개의 double 값을 연산한 후 double 값 반환 |
DoubleUnaryOperator | double applyAsDouble(double value) | 한 개의 double 값을 연산한 후 double 값 반환 |
IntBinaryOperator | int applyAsInt(int value, int value) | 두 개의 int 값을 연산한 후 int 값 반환 |
IntUnaryOperator | int applyAsInt(int value) | 한 개의 int 값을 연산한 후 int 값 반환 |
LongBinaryOperator | long applyAsLong(long value, long value) | 두 개의 long 값을 연산한 후 long 값 반환 |
LongUnaryOperator | long applyAsLong(long value) | 한 개의 long 값을 연산한 후 long 값 반환 |
Operator 인터페이스는 Function과 동일하게 매개 변수와 리턴 값이 있는 applyXXX() 메서드를 가지고 있다. 하지만, 이 메서드들은 매개 변수를 리턴 값으로 매핑하는 역할보다는 매개 변수를 이용해서 연산을 수행한 후 동일한 타입으로 리턴 값을 제공하는 역할을 수행한다.
public class Main {
static private int[] numbers = {3, 7, 2, 5, 1, 9};
static private double[] celsiusDegrees = {25, 37, 41, 2};
public static void main(String[] args) {
// 연산식 설정 - 최댓값 구하기
System.out.println("===== 최댓값 =====");
int maxValue = getMax(
(x, y) -> {
int number = x;
if (x < y) {
number = y;
}
return number;
}
);
System.out.println(maxValue);
// 연산식 설정 - 제곱값
System.out.println("===== 제곱값 =====");
int[] squareArr = getSquareValue(x -> x * x);
for (int squareValue : squareArr) {
System.out.println(squareValue);
}
// 연산식 설정 - 화씨 온도 바꾸기
System.out.println("===== 화씨 온도 =====");
double[] temperatureArr = getTemperature(x -> x * 9 / 5 + 32);
for (double temperature : temperatureArr) {
System.out.println(temperature);
}
}
// 함수적 인터페이스를 매개 변수 받아 최댓값을 구하는 메서드
static public int getMax(IntBinaryOperator operator) {
int result = numbers[0];
for (int number : numbers) {
result = operator.applyAsInt(result, number);
}
return result;
}
// 함수적 인터페이스를 매개 변수로 받아 제곱값을 구하는 메서드
static int [] getSquareValue(IntUnaryOperator operator) {
int[] intArr = new int[numbers.length];
for (int i = 0; i < numbers.length; i++) {
intArr[i] = operator.applyAsInt(numbers[i]);
}
return intArr;
}
// 함수적 인터페이스를 받아 섭씨 온도를 화씨 온도로 바꾸는 메서드
static double[] getTemperature(DoubleUnaryOperator operator) {
double[] temperatureArr = new double[celsiusDegrees.length];
for (int i = 0; i < celsiusDegrees.length; i++) {
temperatureArr[i] = operator.applyAsDouble(celsiusDegrees[i]);
}
return temperatureArr;
}
}
Operator 인터페이스를 활용해 최댓값, 제곱 값, 온도 단위를 화씨로 바꾸기 연산을 수행한다.
===== 최댓값 =====
9
===== 제곱값 =====
9
49
4
25
1
81
===== 화씨 온도 =====
77.0
98.6
105.8
35.6
실행결과는 위와 같다.
Predicate 함수적 인터페이스
인터페이스명 | 추상 메서드 | 설명 |
Predicate<T> | boolean test(T t) | 객체 T를 조사 |
BiPredicate<T, U> | boolean test(T t, U u) | 객체 T와 U를 비교 조사 |
DoublePredicate | boolean test(double value) | double 값을 조사 |
IntPredicate | boolean test(int value) | int 값을 조사 |
LongPredicate | boolean test(long value) | long 값을 조사 |
Predicate 인터페이스는 매개 변수와 boolean 리턴 값이 있는 test() 추상 메서드를 가지고 있다. 해당 메서드는 매개 변수를 조사해서 true 또는 false를 반환하는 역할을 한다.
public class Main {
static private List<Student> students;
public static void main(String[] args) {
students = Arrays.asList(
new Student("홍길동", Gender.MALE, 90),
new Student("이순신", Gender.MALE, 100),
new Student("임꺽정", Gender.FEMALE, 85),
new Student("이강인", Gender.MALE, 70)
);
// 남성 평균점수 구하기
System.out.println("===== 남성 평균점수 =====");
double avgGradeOfMale = getAvg(x -> x.getGender().equals(Gender.MALE));
System.out.println(avgGradeOfMale);
// 90점 이상일 경우 평균점수 구하기
System.out.println("===== 90점 이상일 경우 평균점수 =====");
double avgOver80 = getAvg(x -> x.getScore() >= 80);
System.out.println(avgOver80);
}
static private double getAvg(Predicate<Student> predicate) {
int count = 0;
int sum = 0;
for (Student student : students) {
if (predicate.test(student)) {
count++;
sum += student.getScore();
}
}
return (double) sum / count;
}
}
위 소스 코드는 특정 조건에 부합하는 점수만 가져와서 평균으로 구하는 코드이다.
===== 남성 평균점수 =====
86.66666666666667
===== 90점 이상일 경우 평균점수 =====
91.66666666666667
실행결과는 위와 같다.
출처
ㆍ https://steady-coding.tistory.com/308