ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 함수형 인터페이스
    프로그래밍 언어/JAVA 2022. 6. 26. 01:32

    함수형 인터페이스

    함수형 인터페이스는 java 8이전부터 있던 기능?(기능이라고 말할 것까지는 아니지만...)이지만 특정한 조건을 만족시키는 인터페이스를 함수형 인터페이스라고 한다.

    그 전에, 인터페이스가 무엇인지 간단하게 정리해보고 넘어가자.

    인터페이스(Interface)란, 다른 클래스를 작성할 때 기본이 되는 틀을 제공하면서, 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스를 의미한다.
    일반적인 부모 클래스와의 다른점은 인터페이스는 인터페이스의 메소드를 반드시 구현해야 하는 "강제성"을 갖는다.
    추상 클래스와의 다른점은 추상 클래스는 extends 키워드를 사용하여 상속하며, 다중 상속은 불가능합니다. 반면 인터페이스는 implements 키워드를 사용하여 상속하며, 다중 상속이 가능하다는 것이다.
    또한, 추상 클래스는 일반 변수, 생성자, 일반 메서드, 추상 메서드를 모두 가질 수 있는 반면 인터페이스는 상수와 추상 메서드만 가질 수 있고, 생성자와 일반 변수는 가질 수 없다.

     

    그러면 함수형 인터페이스는 어떻게 생겼는지 알아보자.

    public interface ExampleInterface {
    
        # 추상 메서드 선언 (abstract는 생략가능)
        void doIt();
        
        # java 8이후, 인터페이스에 추가된 기능으로 public static method선언 가능(public은 생략가능)
        # 인터페이스에 static 메소드를 선언함으로써, 인터페이스를 이용하여 간단한 기능을 가지는 유틸리티성 인터페이스를 만들 수 있게 됨.
        static void printName() {
        	System.out.println("Ratel");
        }
        
        # java 8이후, 인터페이스에 추가된 기능으로 defalut method 선언 가능
        # 인터페이스가 변경이 되면, 인터페이스를 구현하는 모든 클래스들이 해당 메소드를 구현해야 하는 문제가 있음.
        # 이러한 문제를 해결하기 위하여 인터페이스에 메소드 선언가능.
        default void printAddress(){
        	System.out.println("티스토리");
        }
    }

    함수형 인터페이스에서 중요한 점은 추상메서드가 인터페이스 내에  "단 한개"만 존재한다는 것이다.

    (static method, default method가 몇 개인지는 중요하지 않다.)

    자바에서 기본적으로 제공하는 함수형 인터페이스에 붙이는 @FunctionalInterface라는 어노테이션이 존재하고 이 어노테이션이 붙은 인터페이스에 추상메서드가 2개 이상 선언되면 컴파일에러가 발생해 미리 IDE에서 잡을 수 있다.

     

     

    이렇게 선언된 인터페이스의 사용법은 어떻게 될까?

    # java 8이전
    public class Foo {
    
    	public static void main(String[] args) {
        	# 익명내부클래스(anonymous inner class)
            ExampleInterface exampleInterface = new ExampleInterface() {
            	@Override
                public void doIt() {
                	System.out.println("hello world");
                }
            };
        }
    }
    
    # java 8이후
    public class Foo {
    
    	public static void main(String[] args) {
        	# 람다표현식을 사용하여 선언가능.
        	ExampleInterface exampleInterface = () -> System.out.println("hello world");
        }
    }

    함수형 인터페이스를 익명내부클래스로 선언하여 사용이 가능하고 이를 자바 8이후에는 람다표현식을 사용하여 코드를 더 간결하게 작성할 수 있다.

     

     

    자바에서는 기본적으로 제공하는 함수형 인터페이스가 존재한다.

    https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html

     

    java.util.function (Java Platform SE 8 )

    Interface Summary  Interface Description BiConsumer Represents an operation that accepts two input arguments and returns no result. BiFunction Represents a function that accepts two arguments and produces a result. BinaryOperator Represents an operation u

    docs.oracle.com

    많은 함수형 인터페이스를 제공하고 있고 이 모든 것을 정리하기는 힘들어 몇 개만 정리해보려고 한다.

    1) Interface Fuction<T, R>

    먼저, Function이라는 인터페이스에는 apply(T t)라는 추상메서드가 존재한다.

    해당 메서드는 타입이 T인 파라미터를 받아 R타입을 리턴하는 역할을 한다.

    Function인터페이스는 아래와 같이 사용할 수 있다.

    public class Foo {
    	
        public static void main(String[] args) {
        	# 람파표현식으로 apply함수 구현
            Function<Integer, Integer> plus5 = (i) -> i + 5;
            Function<Integer, Integer> multiply2 = (i) -> i * 2;
            
            # Function에서 default로 제공해주는 compose메서드
            # compose함수의 파라미터에 대한 반환값을 compose를 선언한 객체에 대입하여 계산
            # 즉, 곱하기 2후에 5를 더함.
            Function<Integer, Integer> multiply2AndPlus5 = plus5.compose(multiply2);
            
            # Function에서 default로 제공해주는 andThen메서드
            # andThen메서드를 선언한 객체의 apply실행 후에 파라미터의 apply실행
            # 즉, 5를 더한 후에 2를 곱함.
            Function<Integer, Integer> plus5AndMultiply2 = plus5.andThen(Multiply2);
        }
    }

    위 코드를 보면 Function에서 선언된 두 개의 타입이 Integer로 동일한데 이러한 경우에는 UnaryOperator라는 인터페이스를 사용해도 된다.

    UnaryOperator<T>는 Function<T, T>를 상속받고 있기 때문에 Function에서 선언된 default메서드도 사용할 수 있다.

     

    2) Interface BiFunction<T, U, R>

    BiFunction이라는 인터페이스에는 apply(T t, U u)라는 추상메서드가 존재하고 해당 메서드는 타입이 T와 타입이 U인 파라미터를 받아 R타입을 리턴하는 역할을 한다.

    Function 인터페이스는 하나의 파라미터를 받지만 BiFunction은 두 개의 파라미터를 받을 수 있고 BiFunction은 compose()라는 default메서드는 존재하지 않는다.

    BinFunction도 T, U, R이 같은 타입이라면 BinaryOperator<T>라는 인터페이스를 사용할 수 있다.

     

    3) Interface Consumer<T>, Supplier<T>

    Consumer와 Supplier는 회사코드에서도 흔히 봤는데 먼저 Consumer는 T타입을 파라미터로 받아서 아무런 값도 리턴하지 않는 void 추상 메서드인 accept를 가진 인터페이스이다.

    Supplier는 파라미터는 정의되어있지 않고 T타입을 반환시키는 추상 메서드인 get()을 가지고 있는 인터페이스이다.

    Consumer와 Supplier는 아래와 같이 사용할 수 있다.

    public class Foo {
    	
        public static void main(String[] args) {
        	Consumer<Integer> printArg = (i) -> System.out.println(i);
            printArg.accept(10);
            
            Supplier<Integer> get10 = () -> return 10;
            System.out.println(get10.get());
        }
    }

     

    4) Interface Predicate<T>

    predicate 인터페이스는 T타입인 파라미터를 받아 boolean을 리턴하는 추상 메서드인 test()를 가지고 있다.

    public class Foo {
    	
        public static void main(String[] args) {
        	Predicate<String> predicate = (str) -> return str.startsWith("ratel");
            System.out.println(predicate.test(ratelBlog);
        }
    }

     


    <참고자료>

    더 자바 Java 8, https://www.inflearn.com/course/the-java-java8/dashboard

    댓글

Designed by Tistory.