JAVA/JAVA8 관련

[더 자바 8] 함수형 인터페이스와 람다 표현식

JUMP개발자 2022. 2. 4. 00:48

1. 함수형 인터페이스

- 추상메서드가 하나만 존재하는 인터페이스

- 다른 Static / default 메서드는 있어도 됨.

- @FunctionalInterface를 선언하여 함수형 인터페이스를 견고하게 관리 가능하다.

(함수형 인터페이스 아닐 시에 컴파일 에러를 냄)

 

// 함수형 인터페이스
// -> 추상 메서드를 인터페이스당 1개만 사용가능
@FunctionalInterface
public interface RunSomething { 
    
    void doIt();
    
   // void doitAgain();
}

2. 익명 내부 클래스

public class Foo {

    public static void main(String[] args) {
        // 자바 8 이전에 사용하던 방식
        // 익명 내부 클래스 - anoymous inner class
        RunSomething runSomething = new RunSomething() {
            @Override
            public void doIt() {
                System.out.println("do it!!");
            }
        };
        // 자바 8에서 새로 지원하는 방식
        RunSomething runSomething2 = () -> {System.out.println("do it!!");};
    }
}

3. 순수함수

- 함수 밖에 있는 값 변경 X

- 입력 받은 값이 동일한 경우, 결과값이 같아야 한다. ( 이를 보장하지 못하면 함수형 프로그래밍이 아님)

 

위를 보장 받지 못하는 경우 :

 - 함수 외부의 변수를 사용하는 경우

 - 외부에 있는 값을 변경하는 경우 

// 인터페이스
  @FunctionalInterface
  public interface RunSomthing {
  	int doIt(int number);
  }
  
// 클래스 
public class Foo {
    
    public static void main(String[] args) {
      RunSomthing runSomthing1 = new RunSomthing() {
      int baseNumber = 10;
        @Override
        public int doIt(int number) {
          baseNumber++; // 2 : 외부에 있는 값을 변경
          return number + baseNumber; // 1 : 함수 외부의 값을 사용
        }
      };
    }
  }

4. 고차함수

함수가 함수를 매개변수로 받고. 리턴할 수 있음.

 

5. 자주 사용하는 함수 인터페이스

- 위와같이 직접 함수 인터페이스를 만드는 경우보다 JAVA에서 제공하는 함수 인터페이스를 사용하는 경우가 많음.

 

  • Function<T, R> : T를 받아서 R을 리턴
    • apply, andThen, compose 등을 사용함.
    • 입력값과 결과값이 같으면 UnaryOperator를 사용할 수 있다
      ex:) Function<Integer, Integer> == UnaryOperator<Integer>
// 방법1 : 클래스로 구현해서 사용
   
    public class Plus10 implements Function<Integer, Integer> {
        @Override
        public Integer apply(Integer number) {
            return number + 10;
        }
    }
// 방법 1 사용
public class Foo {

    public static void main(String[] args) {
		Plus10 plus10 = new Plus10();
         System.out.println(plus10.apply(1));
    }

}    
// 방법2 : 람다식을 사용하는 방식
 public class Foo {
        public static void main(String[] args) {
            Function<Integer, Integer> plus10 = (i) -> i + 10;
            System.out.println(plus10.apply(2));
            Function<Integer,Integer> multiply2 = (i) ->i*2;
        	System.out.println(multiply2.apply(2)); //4
        }
}

 

andThen : 함수를 먼저 실행하고, 입력값을 실행 (실행함수 우선 실행)

compose : 입력값을 실행한후, 함수를 실행. (입력함수 우선 실행)

public class Foo {

    public static void main(String[] args) {

        Function<Integer,Integer> plus10 = (i) ->i+10;
        System.out.println(plus10.apply(2)); //12
        Function<Integer,Integer> multiply2 = (i) ->i*2;
        System.out.println(multiply2.apply(2)); //4
        // 1. andThen
        Function<Integer, Integer> plus10Andmultiply2 = plus10.andThen(multiply2);
        System.out.println(plus10Andmultiply2.apply(2)); // (2 + 10) * 2 = 24
        // 2. compose
        Function<Integer,Integer> multiply2AndPlus10 = plus10.compose(multiply2);
        System.out.println(multiply2AndPlus10.apply(2)); // (2*2)+10= 14
        
    }

}
  • Consumer<T> : T타입을 받아서 아무값도 리턴하지 않는 함수 인터페이스
public class Foo {

    public static void main(String[] args) {
        Consumer<Integer> printT = (i) ->System.out.println(i);
        printT.accept(10);
    }

}
  • Supplier<T> : T타입의 값을 제공하는 함수 인터페이스
public class Foo {

    public static void main(String[] args) {
        Supplier<Integer> get10 = () ->10;
        System.out.println(get10.get()); // 10
    }
}
  • Predicate<T> : T타입을 받아서 boolean을 리턴하는 함수 인터페이스