람다식 - (2)

59154 단어 JavaJava

표준 API

Java 8에서부터 표준 API로 제공하는 함수적 인터페이스가 있는데, Consumer, Supplier, Function, Operator, Predicate가 있다.

Consumer 함수적 인터페이스

이름에 Consumer가 포함된 함수형 인터페이스들을 말한다. 모두 accept() 라는 추상 메서드를 가지는데, 메서드들의 시그니쳐는 다르지만, 모두 반환값이 없다. 매개변수를 받아서 소비만 하는 것이다. 어떤 타입의 매개변수를 받아서 소비하느냐에 따라 사용하는 인터페이스가 달라진다.

인터페이스추상 메서드
Consumer<T>void accept(T t)
BiConsumer<T, U>void accept(T t, U u)
DoubleConsumervoid accept(double value)
IntConsumervoid accept(int value)
LongConsumervoid accept(long value)
ObjDoubleConsumer<T>void accept(T t, double double)
ObjIntConsumer<T>void accept(T t, int value)
ObjLongConsumer<T>void accept(T t, long value)

사용 예시

Consumer<String> consumer = str -> {
    System.out.println(str);
};

consumer.accept("소비자 인터페이스");

BiConsumer<String, String> biConsumer = (t, u) -> {
    System.out.println(t);
    System.out.println(u);
};

biConsumer.accept("첫번째", "두번째");

consumer를 선언하는 명령문을 다음과 같이 줄일수도 있다. 메서드를 한번 호출하는 것이 메서드 본문의 전부이기 때문이다.

Consumer<String> consumer = System.out::println;

Supplier 함수적 인터페이스

이름에 Supplier가 포함된 함수형 인터페이스들을 말한다. getXXX()라는 이름의 추상 메서드를 가지는데, 모두 매개변수는 없고 반환값만 있다. 반환값의 타입에 따라 사용할 인터페이스가 달라진다.

인터페이스추상 메서드
Supplier<T>T get()
BooleanSupplierboolean getAsBoolean()
DoubleSupplierdouble getAsDouble()
IntSupplierint getAsInt()
LongSupplierlong getAsLong()

사용 예시

Supplier<String> supplier = () -> "text";
System.out.println(supplier.get());

DoubleSupplier supplier2 = () -> 1.52;
System.out.println(supplier2.getAsDouble());

Function 함수적 인터페이스

모두 applyXXX()라는 메서드를 가지며, 매개변수가 반환값이 둘 다 있다. 주로 매개값을 반환값으로 매핑할 경우에 사용한다.

객체 -> 객체

인터페이스추상 메서드
Function<T, R>R apply(T t)
BiFunction<T, U, R>R apply(T t, U u)

기본형 -> 객체

인터페이스추상 메서드
DoubleFunction<R>R apply(double value)
IntFunction<R>R apply(int value)

기본형 -> 기본형

인터페이스추상 메서드
IntToDoubleFunctiondouble applyAsDouble(int value)
IntToLongFunctionlong applyAsLong(int value)
LongToDoubleFunctiondouble applyAsDouble(long value)
LongToIntFunctionint applyAsInt(long value)

객체 -> 기본형

인터페이스추상 메서드
ToDoubleBiFunction<T, U>double applyAsDouble(T t, U u)
ToDoubleFunction<T>double applyAsDouble(T value)
ToIntBiFunction<T, U>int applyAsInt(T t, U u)
ToIntFunction<T>int applyAsInt(T value)
ToLongBiFunction<T, U>long applyAsLong(T t, U u)
ToLongFunction<T>long applyAsLong(T value)

사용 예시

public class Student {
    private String name;
    
    public Student(String name, int englishScore, int mathScore) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class FunctionEx {
    private static List<Student> list = Arrays.asList(
            new Student("신은혁", 100, 95),
            new Student("김연아", 90, 98),
            new Student("차주혁", 80, 92),
            new Student("신주아", 81, 99)
    );

    public static void main(String[] args) {
        Function<Student, String> function = t -> t.getName();
        // 우변을 Student::getName으로 대체 가능
        printString(function);
    }

    public static void printString(Function<Student, String> function){
        for(Student student : list){
            String str = function.apply(student);
            System.out.println("이름 : " + str);
            // 리스트에 있는 각 학생의 이름 출력
        }
        System.out.println();
    }
}

Operator 함수적 인터페이스

Function과 같이 매개변수와 반환값이 모두 있는 applyXXX() 메서드를 가지만, Function과는 달리 매개변수를 반환값으로 매핑하기 보다, 연산을 수행한 후 매개변수와 동일한 타입으로 반환값을 제공한다. 연산을 수행하는 타입과, 피연산자의 개수에 따라 다른 인터페이스를 사용한다.

인터페이스추상 메서드
BinaryOperator<T>BiFunction<T, T, T>의 하위 인터페이스
UnaryOperator<T>Function<T, T>의 하위 인터페이스
DoubleBinaryOperatordouble applyAsDouble(double, double)
DoubleUnaryOperatordouble applyasDouble(double)
IntBinaryOperatorint applyAsInt(int, int)
IntUnaryOperatorint applyAsInt(int)
LongBinaryOperatorlong applyAsLong(long, long)
LongUnaryOperatorlong applyAsLong(long)

사용 예시

import java.util.function.IntBinaryOperator;

public class OperatorEx {
    public static int[] scores = {15, 55, 10, 100, 92, 35, 87};

    public static void main(String[] args) {
        IntBinaryOperator operatorMax = Math::max;
        System.out.println(maxOrMin(operatorMax)); // 100

        IntBinaryOperator operatorMin = Math::min;
        System.out.println(maxOrMin(operatorMin)); // 10
    }

    public static int maxOrMin(IntBinaryOperator operator){
        int result = scores[0];
        for(int score: scores){
            result = operator.applyAsInt(result, score);
        }

        return result;
    }
}

Predicate 함수적 인터페이스

매개변수와 boolean형 반환값을 가지는 testXXX() 메서드가 있다. 매개변수의 값이 조건에 맞는지 조사하여 true나 false를 반환하는 것이다.

인터페이스추상 메서드
Predicate<T>boolean test(T t)
BiPredicate<T, U>boolean test(T t, U u)
DoublePredicateboolean test(double value)
IntPredicateboolean test(int value)
LongPredicateboolean test(long value)
import java.util.ArrayList;
import java.util.function.Predicate;

public class PredicateEx {
    public static ArrayList<Student> arrayList = new ArrayList<>();

    public static void main(String[] args) {
        arrayList.add(new Student("김동년", "여자", 20));
        arrayList.add(new Student("이민기", "남자", 60));
        arrayList.add(new Student("김연아", "여자", 100));
        arrayList.add(new Student("신은혁", "남자", 100));
        arrayList.add(new Student("홍길동", "남자", 80));

        Predicate<Student> predicateMale = student ->
                                   student.getGender().equals("남자");
        Predicate<Student> predicateFemale = student -> 
                                   student.getGender().equals("여자");

        System.out.println(avg(predicateMale));
        System.out.println(avg(predicateFemale));

    }

    public static double avg(Predicate<Student> predicate){
        int count = 0;
        int sum = 0;
        for(Student student: arrayList){
            if(predicate.test(student)){
                count++;
                sum += student.getScore();
            }
        }

        return (double)sum / count;
    }
}

디폴트 메서드

함수적 인터페이스는 andThen()과 compose()라는 디폴트 메서드를 가진다.

andThen()

두 개의 함수적 인터페이스를 순차적으로 연결해서 실행한다. 또한 첫 번째 반환값을 두번째 메서드의 매개변수로 사용하여 최종 결과값을 반환한다.

인터페이스AB = 인터페이스A.andThen(인터페이스B);
// 인터페이스A에서의 결과값을 인터페이스B에서의 매개변수로 사용
최종 결과 = 인터페이스AB.method();
// 최종 결과값을 받음

compose()

이어주는 순서가 andThen()과 반대인 메서드이다. 인터페이스B의 결과값을 인터페이스A의 매개변수로 사용하여 최종 결과값을 반환한다.

인터페이스AB = 인터페이스A.compose(인터페이스B);
// 인터페이스B에서의 결과값을 인터페이스A에서의 매개변수로 사용
최종 결과 = 인터페이스AB.method();

Consumer

Consumer 인터페이스는 반환값이 없기 때문에 디폴트 메서드는 각 인터페이스의 호출 순서만 정한다. accept에 넣은 매개변수가 두 메서드에서 공통적으로 사용된다.

Consumer<Person> consumerA = person -> 
    System.out.println("ConsumerA : " + person.getName());
Consumer<Person> consumerB = person -> 
    System.out.println("ConsumerB : " + person.getId());

Consumer<Person> consumerAB = consumerA.andThen(consumerB);
consumerAB.accept(new Person("김남건", "Kim", null));

Function

먼저 실행한 메서드의 결과값을 다음 메서드의 매개변수로 넘겨줘서 최종적값을 반환한다.

andThen이든 compose든 두번째로 호출되는 메서드의 매개변수 타입이 첫번째 호출되는 메서드의 반환값의 타입과 일치해야 하는 것을 유의하자.

Function<Person, Address> functionA = p -> p.getAddress();
Function<Address, String> functionB = a -> a.getCountry();

Function<Person, String> functionAB = functionA.andThen(functionB);
String country = functionAB.apply(new Person("김남건", "kim", 
    new Address("한국", "제주")));
System.out.println(country);

인터페이스별 제공 메서드

Predicate의 디폴트 메서드

Predicate의 경우 boolean형 값을 반환하기 때문에 and(), or(), negate() 세 개의 디폴트 메서드를 가진다. 모든 Predicate 인터페이스가 이 세 개의 메서드를 지원한다. 그리고 정적 메서드인 isEqual()도 지원한다.

and()

두 Predicate가 모두 true를 반환하면 최종적으로 true를 반환한다.

predicateAB = predicateA.and(predicateB);

or()

둘 중 하나라도 true이면 최종적으로 true를 반환한다.

predicateAB = predicateA.or(predicateB);

negate()

Predicate의 결과값의 반대값을 반환한다.

predicateB = predicateA.negate();

예시

IntPredicate predicateA = x -> x % 2 == 0;
IntPredicate predicateB = x -> x % 3 == 0;
IntPredicate predicateAB = predicateA.and(predicateB);
System.out.println("36은 6의 배수인가? : " + predicateAB.test(36));

IntPredicate predicateC = predicateA.negate();
System.out.println("21은 홀수인가? : " + predicateC.test(21));

Predicate.isEqual()

기존의 Object.equals() 메서드의 기능에 null 값을 비교하는 기능도 추가하였다. 반환하는 값이 Predicate이기 때문에 test()까지 호출해야 한다.

sourceObjecttargetObject반환값
nullnulltrue
not nullnullfalse
nullnot nulltrue
not nullnot nullsourceObject.equals(targetObject)
Predicate<String> predicate = Predicate.isEqual("문자열1");
boolean result1 = predicate.test("문자열1");
boolean result2 = predicate.test(null);
System.out.println(result1); // true
System.out.println(result2); // false

BinaryOperaator의 디폴트 메서드

minBy(), maxBy()

Comparator를 이용해 최대 T와 최소 T를 얻는 BinaryOperator<T>를 반환한다. 둘 다 정적 메서드이다.

반환 타입메서드
BinaryOperator<T>minBy(Comparator<? super T> comparator)
BinaryOperator<T>maxBy(Comparator<? super T> comparator)

사용 예시

BinaryOperator<Fruit> binaryOperator = 
    BinaryOperator.minBy((f1, f2) -> f1.getPrice() - f2.getPrice());

Fruit fruit = binaryOperator.apply(new Fruit("황도", 5000),
    new Fruit("복숭아", 4000));
System.out.println(fruit.getName()); // 복숭아

메서드 참조(Method Reference)

종종 람다식은 기존 메서드를 단순히 호출만 하는 경우가 있다. 이때 코드를 더욱 간결하게 만들기 위해 도입된 것이 메서드 참조이다. 람다식에 매개변수나 메서드 본문을 작성하는 대신 단순히 메서드 이름만 적음으로써 메서드 호출을 대신할 수가 있다.

IntBinaryOperator operator = (a, b) -> Math.max(a, b);
IntBinaryOperator operator = Math::max;

정적 메서드

정적 메서드의 경우 (클래스 이름)::(메서드) 형태로 호출한다.

IntBinaryOperator operator = (a, b) -> Math.max(a, b);
IntBinaryOperator operator = Math::max;

인스턴스 메서드

(참조변수)::(메서드) 형태로 호출한다.

public class Calculator{
    public int method(int x, int y){
        return x + y;
    }
}

Calculator cal = new Calculator();
Operator operator = cal::method;
System.out.println(operator.applyAsInt(7, 8));

생성자 참조

(클래스)::new로 참조한다. 인터페이스의 메서드를 호출할 때 매개변수의 개수, 타입에 따라 다른 생성자가 호출된다.

public class Person {
    private String name;
    private int age;

    public Person(){
        System.out.println("기본생성자 호출");
    }

    public Person(String name){
        System.out.println("String name 받는 생성자 호출");
        this.name = name;
    }

    public Person(String name, int age) {
        System.out.println("매개변수 두개 받는 생성자 호출");
        this.name = name;
        this.age = age;
    }
}

public class PersonEx {
    public static void main(String[] args) {
        Function<String, Person> function1 = Person::new;
        Person person1 = function1.apply("신은혁");

        BiFunction<String, Integer, Person> function2= Person::new;
        Person person2 = function2.apply("김남건", 25);
    }
}

좋은 웹페이지 즐겨찾기