본문 바로가기
Programming/Java, Spring

자바의 여러가지 functional interface + comparator

by Renechoi 2023. 6. 3.

1. Supplier 

 

@FunctionalInterface
public interface Supplier<T> {
 T get();
}

 

사용 예시 

public static void main(String[] args) {
Supplier<String> myStringSupplier = () -> {return "hello world!";};
   myStringSupplier.get();


   Supplier<Double> myRandomDoubleSupplier = () -> Math.random();
   System.out.println("myRandomDoubleSupplier.get() = " + myRandomDoubleSupplier.get());


   printRandomDoubles(myRandomDoubleSupplier, 5);
}

public static void printRandomDoubles(Supplier<Double> randomSupplier, int count){
   for (int i=0; i<count; i++){
      System.out.println("randomSupplier = " + randomSupplier.get());
   }
}

 

 

 

2. Consumer 

 

@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

 

안에서 프로세스를 하고 리턴하는 것은 없다. 

 

스트링을 받아서 아무것도 하지 않고 print만 하고 끝낸다. 

 

 

public static void main(String[] args) {
   Consumer<String> myStringConsumer = (String string) ->{
      System.out.println("string = " + string);
   };
   
   myStringConsumer.accept("hello");
}

 

 

여러 가지 consumer를 패스해서 다양한 Integer를 프로세스 해보자. 

 

 

public static void main(String[] args) {
   Consumer<String> myStringConsumer = (String string) -> System.out.println("string = " + string);

   myStringConsumer.accept("hello");

   List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);

   Consumer<Integer> myIntegerProcessor = (Integer x) -> System.out.println("process integer: " + x);
   Consumer<Integer> myIntegerProcessor2 = (Integer x) -> System.out.println("process in different way: " + x);
}

public static void process(List<Integer> inputs, Consumer<Integer> processor){
   for (Integer input : inputs) {
      processor.accept(input);
   }
}


public static <T> void process2(List<T> inputs, Consumer<T> processor){
   for (T input : inputs) {
      processor.accept(input);
   }
}

 

 

3. BiConsumer

 

@FunctionalInterface
public interface BiConsumer<T, U> {

    /**
     * Performs this operation on the given arguments.
     *
     * @param t the first input argument
     * @param u the second input argument
     */
    void accept(T t, U u);

    /**
     * Returns a composed {@code BiConsumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code BiConsumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
        Objects.requireNonNull(after);

        return (l, r) -> {
            accept(l, r);
            after.accept(l, r);
        };
    }

 

 

2개의 타입을 받아서 consume한다. 

 

사용 예시 

public static void main(String[] args) {
    BiConsumer<String, Integer> printValues = (name, age) -> {
        System.out.println("Name: " + name);
        System.out.println("Age: " + age);
    };

    printValues.accept("kim", 25);

    BiConsumer<Integer, Integer> performOperation = (a, b) -> {
        int sum = a + b;
        int difference = a - b;
        int product = a * b;

        System.out.println("Sum: " + sum);
        System.out.println("Difference: " + difference);
        System.out.println("Product: " + product);
    };

    performOperation.accept(10, 5);
}

 

 

4. Predicate 

 

@FunctionalInterface
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);
    }

   
    @SuppressWarnings("unchecked")
    static <T> Predicate<T> not(Predicate<? super T> target) {
        Objects.requireNonNull(target);
        return (Predicate<T>)target.negate();
    }
}

 

 

value를 테스트 하고 boolean을 리턴하는 메서드를 구현한다. 

 

사용 예시 

 


public class PredicateExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -2, 0, -5);

        Predicate<Integer> isEven = number -> number % 2 == 0;

        // numbers 리스트에서 짝수만 필터링하여 출력
        System.out.println("Even numbers:");
        numbers.stream()
               .filter(isEven)
               .forEach(System.out::println);

        // 양수를 판별하는 Predicate
        Predicate<Integer> isPositive = number -> number > 0;

        // numbers 리스트에서 양수만 필터링하여 출력
        System.out.println("Positive numbers: " + filter(numbers, isPositive));
        System.out.println("Non-Positive numbers: " + filter(numbers, isPositive.negate()));
        
    }
    
    public static <T> List<T> filter(List<T> inputs, Predicate<T> predicate){
        List<T> output = new ArrayList<>();
        for (T input : inputs) {
            if (predicate.test(input)){
                output.add(input);
            }
        }
        return output;
    }
}

 

 

 

5. Comparator 

 

java.util에 Comparator Interface를 알아보자. 

 

FunctionalInterface
public interface Comparator<T> {
    
    int compare(T o1, T o2);

   
    boolean equals(Object obj);

  
    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }

   
    default Comparator<T> thenComparing(Comparator<? super T> other) {
        Objects.requireNonNull(other);
        return (Comparator<T> & Serializable) (c1, c2) -> {
            int res = compare(c1, c2);
            return (res != 0) ? res : other.compare(c1, c2);
        };
    }
    
    .
    .
    .
    .
    .
    .
}

 

 

Comparator를 사용하여 비교하는 예시 코드를 작성해보자. 

 

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

   public Person(String name, int age) {
      this.name = name;
      this.age = age;
   }

   public String getName() {
      return name;
   }

   public int getAge() {
      return age;
   }
}
public static void main(String[] args) {
    List<Person> people = new ArrayList<>();
    people.add(new Person("Alice", 25));
    people.add(new Person("Bob", 30));
    people.add(new Person("Charlie", 20));

    // 이름을 기준으로 오름차순으로 정렬하는 Comparator
    Comparator<Person> nameComparator = (person1, person2) -> person1.getName().compareTo(person2.getName());
    Comparator<Person> nameComparator2 = Comparator.comparing(Person::getName);

    // 나이를 기준으로 내림차순으로 정렬하는 Comparator
    Comparator<Person> ageComparator = (person1, person2) -> person2.getAge() - person1.getAge();
    Comparator<Person> ageComparator2 = Comparator.comparingInt(Person::getAge).reversed();

    // 이름을 기준으로 오름차순으로 정렬
    people.sort(nameComparator);
    System.out.println("Sorted by name (ascending):");
    for (Person person : people) {
        System.out.println(person.getName() + " - " + person.getAge());
    }

    // 나이를 기준으로 내림차순으로 정렬
    people.sort(ageComparator);
    System.out.println("Sorted by age (descending):");
    for (Person person : people) {
        System.out.println(person.getName() + " - " + person.getAge());
    }
}

 

Person 객체는 name과 age라는 속성을 갖고 있다. Comparator<Person>를 사용하여 name과 age를 기준으로 정렬하는 nameComparator와 ageComparator를 정의할 수 있다.

people 리스트에 여러 Person 객체를 추가한 후, nameComparator를 사용하여 이름을 기준으로 오름차순으로 정렬하고 출력한다. 그 후 ageComparator를 사용하여 나이를 기준으로 내림차순으로 정렬하고 출력한다.

이처럼 Comparator를 사용하면 정렬 기준을 유연하게 변경할 수 있으며, 다양한 정렬 방식을 구현할 수 있다.

반응형