모던 자바( 자바8) 못다한 이야기를 보고 정리한 것입니다.

Functional Interface로 java.util.function 패키지 안에 포함되어있다.

람다식의 타입과 형변환

인터페이스인데 abstract 메소드가 하나만 존재하는 경우

Functional interface 4종류

  1. Function 타입
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

apply 메소드 하나만 가지고 있음

입력값과 리턴값이 있어야한다.

functional interface 는 anonymous 클래스 필요없이 사용이 가능하다.

function type : function패키지 안에 들어있다.

리턴타입 하나의 타입이 다른 타입으로 리턴 된것.

같은 타입을 리턴하는 것을 identity라고 한다.

같은 타입 T -> R은 일반적인 함수

T- >T 는 identity 함수가 된다 . 그 타입을 받아서 그타입으로 바로 넘기게 되는것

//Parameter : T , Return Type : R
//Function <R,T> 하나의 메소드를 호출
Function<String, Integer> toInt = new Function<String, Integer>() {
  @Override
  public Integer apply(String value) {
    return Integer.parseInt(value);
  }
};

//lambda로 표현하면 ?
Function<String, Integer> toIntLamda = (value)->Integer.parseInt(value);


Integer number = toInt.apply("200");
System.out.println(number);
  1. Consumer
@FunctionalInterface
public interface Consumer<T> {

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

Accept 메소드 하나만 있음.

void 로 리턴값을 가지고 있음 값은 있고 리턴은 없다.

Ex) 리턴값이 없을경우, 출력

//Basic
//Consumer<T>
//Parameter  : T , Return : void
Consumer<String> consumer = new Consumer<String>() {
  @Override
  public void accept(String s) {
    System.out.println(s);
  }
};

//lambda
Consumer<String> consumer2 = (value)->System.out.println(value);

consumer.accept("20");
consumer2.accept("20");
  1. Predicate
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

결과 값은 항상 boolean 값

function 인데 파라미터 값은 T이고 리턴 타입은항상 boolean 으로 출력

//Basic
Predicate<Integer> isPositive = new Predicate<Integer>() {
  @Override
  public boolean test(Integer integer) {
    return integer >0;
  }
};

System.out.println("is Positive ? "+isPositive.test(-1));
System.out.println("is Positive ? "+isPositive.test(2));

//응용
List<Integer> numbers = Arrays.asList(-5,-4,-3,-2,-1,0,1,2,3,4,5);

//조건
Predicate<Integer> isPositive2 = value -> value > 0;

List<Integer> positiveNumbers = new ArrayList<>();
for(Integer num : numbers){
  if(isPositive2.test(num)){
    positiveNumbers.add(num);
  }
}

아래와 같이 자주쓰이는 filter 메서드를 만들어서 condition만 변경 해주는 방식으로도 사용이 가능하다.

public static <T> List<T> filter(List<T> list, Predicate<T> condition){
  List<T> result = new ArrayList<>();
  for(T element: list){
    if(condition.test(element)){
      result.add(element);
    }
  }
  return result;
}
  1. Supplier

인자 값은 없고 return type만 존재한다.

일종의 lazy Evaliation 할 수 있다.

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

expensiveValue를 구하는 메서드를 일반 코드로 나타내면 아래와 같은 코드가 된다. 총 소요시간은 총 9초 이다.

 long start = System.currentTimeMillis();
        printIfValidIndex( 0, getVeryExpensiveValue());
        printIfValidIndex( -1, getVeryExpensiveValue());
        printIfValidIndex( -2, getVeryExpensiveValue());

        System.out.println("Take Time : "+((System.currentTimeMillis()-start)/1000));

메서드 :

    private static String getVeryExpensiveValue(){
        try{
            TimeUnit.SECONDS.sleep(3);
                        System.out.println("throw this area");
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        return "Morris";
    }

    private static void printIfValidIndex( int number, String valueSupplier){
        if(number >=0){
            System.out.println("The value is "+ valueSupplier + ".");
        }else {
            System.out.println("InValid");
        }
    }

결과 값

getVeryExpensiveValue 메서드가 항상 실행 된다.

그래서 비싼 비용의 메서드가 계속 실행된다는 단점이 있다.

실행 순서는 getVeryExpensiveValue () -> printIfValidIndex -> condition

throw this area
The value is Morris.
throw this area
InValid
throw this area
InValid
Take Time : 9

lazy 하게 실행 할수 있는 supplier로 수정을 해보자

long start = System.currentTimeMillis();
        printIfValidIndex( 0, () -> getVeryExpensiveValue());
        printIfValidIndex( -1, () -> getVeryExpensiveValue());
        printIfValidIndex( -2, () -> getVeryExpensiveValue());

        System.out.println("Take Time : "+((System.currentTimeMillis()-start)/1000));
    }

메서드

일반 코드에서 printIfValidIndex의 매개변수를 string에서 Supplier으로 변경을 하였다.

    private static String getVeryExpensiveValue(){
        try{
            TimeUnit.SECONDS.sleep(3);
              System.out.println("throw this area");
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        return "Morris";
    }

    private static void printIfValidIndex( int number, Supplier<String> valueSupplier){
        if(number >=0){
            System.out.println("The value is "+ valueSupplier.get() + ".");
        }else {
            System.out.println("InValid");
        }
    }

결과 값

throw this area
The value is Morris.
InValid
InValid
Take Time : 3

결과 값을 보면 3초 밖에 나오지 않는다 .

printIfValidIndex -> condition -> get을 하면 불러온다.

이렇게 알수 있는 것은 supplier를 사용하면 매개변수.get을 사용하는 시점에서 해당 메서드가 실행 되는 것을 알수 있다. 이렇게 하면 컴퓨팅 성능과 리소스를 줄일수 있을 것 같다.

'ProgramLanguage > Java' 카테고리의 다른 글

Java8- Default 메서드(Abstract, Interface)  (0) 2020.03.18
JVM 튜닝  (0) 2020.02.10
객체 지향 설계 5원칙 - SOLID  (0) 2019.12.26
Java 와 객체 지향  (0) 2019.12.03
Java jar파일 만들기  (0) 2019.08.04

+ Recent posts