함수형 인터페이스
지난번 포스팅에서는 람다(Lambda) 표현식이라는 간단하게 함수를 정의하는 개념에 대해 알아보았습니다.
이번 포스팅에서는 JAVA8 에서 이러한 람다를 지원할 수 있겠금 등장한 함수형 인터페이스에 대해 알아보려 합니다.
JAVA8에서는 새로운 어노테이션이 생겼습니다!!!!
@FunctionalInterface
어노테이션의 이름에서 다들 눈치를 체셨을 겁니다.
네, 바로 함수형 인터페이스를 선언을 위해 등장한 어노테이션 입니다. (종류는 Built in annotation)
그렇다고 모든 interface에 저 어노테이션을 붙일 수는 없습니다.
저 어노테이션이 오직 한 개의 추상메소드가 있는 인터페이스만 선언할 수 있습니다. 다음 아래와 같이 말이죠.
1 2 3 4 | @FunctionalInterface public interface CreateObjInterface { People createObject(int age, String name); } | cs |
왜 꼭 한개의 추상메소드를 가진 인터페이스만 선언이 가능하게 만들었을까요? 제가 읽고 있는 JAVA 8 IN ACTION 에서는 함수 형식을 표현하기 위해서라는 이유와 이미 많은 JAVA 프로그래머들이 추상 메소드 한개를 갖는 인터페이스에 익숙하기 때문이라고 합니다.
인터페이스에 여러 메소드가 정의되어야 한다면, 차라리 클래스로써, concrete 하게 가지고 있는게 더 좋겠죠. 이 것에 대한 내용은 아래 포스팅에 조금 더 자세히 적었습니다.
우리는 함수형 인터페이스를 사용하여 람다표현식을 사용할 수 있게 되었습니다. 앞서, 정의한 CreateObjInterface를 사용하여 다음과 같이 코드를 작성할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /** * 사람의 정보를 저장하는 Vo 클래스 * * @author Doohyun * */ public class People { private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } } // 객체 생성을 하는 방식의 람다식을 사용하여, 인스턴스를 생성 CreateObjInterface obj = (int age, String name) -> { People ret = new People(); ret.setAge(age); ret.setName(name); return ret; }; People doohyun = obj.createObject(27, "남두현"); | cs |
여기에서 주목할 점은 람다표현식이 CreateObjInterface 의 인스턴스로 취급이 된다는 것입니다. (객체지향적으로 봤을 때는 interface를 구현(implement)하는 concrete 클래스를 만든 것이라고 할 수 있습니다.)
즉 이러한 특성을 이용해 함수를 인수로 가지고 있으며, 코드로 넘길 수 있는 것이 가능해졌다는 것을 알 수 있습니다.
이렇게, 람다를 사용하기 위해 함수형 인터페이스를 정의해야하는 이유는 사용할 람다의 시그니처가 되기 때문입니다. 시그니처란 파라미터 형식, 반환값 등을 말할 수 있는데요. (객체지향에서는 Access 수준, 메소드명, final 정도가 더있을 것 같습니다.) 람다가 어떻게 쓰일 것이다라는 규칙을 명시한 것이라 볼 수 있을 것 같습니다. 함수형 프로그래밍에서는 이것을 함수 디스크럽터 라고 합니다.
즉 람다가 익명의 추상 메소드라는 점에서, 기존 메소드 정의해야할 대부분을 똑같이 따라가줘야함을 알 수 있습니다. 그렇기 때문에 기존 익명 클래스 제작 시, 생겼던 규칙 역시 그대로 안고 갑니다.
1. checked exception
람다 내부에서 checked exception이 발생할 수 있는 로직의 경우, 해당 예외를 어떻게 처리를 할 것인지 명시해주어야합니다. 먼저 상위로 throw 하기 위해서는 아래와 같이 interface에 넘길 exception 명시를 해주어야 합니다.
1 2 3 4 5 6 | @FunctionalInterface public interface CreateObjInterface throws NameEmptyException { People createObject(int age, String name); } | cs |
물론 람다 내에서 try-catch 문으로 처리가 가능합니다.
1 2 3 4 5 6 7 8 9 10 | (int age, String name) -> { People ret = new People(); try { ret.setName(name); ret.setAge(age); } catch (NameEmptyException e) { e.printStackTrace(); } return ret; }; | cs |
2. 변수 접근
기존 익명 클래스를 만들어 인터페이스를 제작했던 것 처럼 람다 역시 외부에 정의된 자유 변수(파라미터로 넘어가지 않는 변수)를 사용할 수 있습니다. 이와 같은 동작을 람다 캡처링이라고 부른다고 합니다.
이러한 변수는 static 변수나 인스턴스 변수의 값을 자유롭게 변경할 수 있지만 지역변수 (함수 내부에 선언된 변수) 는 final인 상태로 밖에 가지고 있지 못합니다.
기존 자바에서는 익명 클래스 내부 구현 메소드에서 지역변수의 final을 강제화했지만 람다는 그렇지 않습니다. 람다의 경우 final 를 강제화 하지는 않지만 지역변수의 값을 변경 시 문법오류를 일으키게 됩니다.
이 것에 대한 자세한 포스팅은 람다와 함수형 인터페이스간의 해석 방법에 대한 포스팅에서 자세히 다루도록 하겠습니다.
|
'개발이야기 > 함수형 프로그래밍' 카테고리의 다른 글
원시타입을 위한 함수형 인터페이스 (0) | 2016.07.28 |
---|---|
JAVA8 에 정의된 함수형 인터페이스 (0) | 2016.07.28 |
람다 표현식 (0) | 2016.07.27 |
동작 파라미터화와 함수형 프로그래밍 (2) (0) | 2016.07.25 |
동작 파라미터화와 함수형 프로그래밍 (1) (0) | 2016.07.25 |