[JAVA] 함수형 인터페이스 기본 개념

유알·2022년 12월 26일
0

[JAVA]

목록 보기
5/13

람다식 간단한 개념

자바에가 처음 등장한 이후 두번의 큰 변화가 있었는데, 한번은 JDK1.5부터 지네릭스가 추가된 것이고,
다른 하나가 JDK1.8부터 람다식이 추가된 것이다.

간단히 보면 메서드 명과 리턴 타입을 생략하면 람다식이 된다.
int max(int a, int b) { return a > b ? a: b;}
.
int max(int a, int b) { return a > b ? a: b;}
.
(int a, int b) -> { return a > b ? a: b;}

람다식은 어느 객체에 포함되는 것인가?

자바에서 모든 메서드는 클래스 내에 포함되어야 하는데 람다식은 어디에 포함되는 것일까?
사실 람다식은 익명클래스의 객체와 동등하다.

(int a, int b) -> a > b ? a : b
new Object(){
	int annonymousMethod(int a, int b) {
   	return a > b ? a : b;
   }
}

여기서 annonymousMethod라는 메서드 명이 있냐 없냐만 다르다.

그렇다면 이렇게 정의된 람다식을 어떻게 호출할 것인가? 부를 수 있는 이름이 없는데 말이다.
이는 간단하다. 항상 하던데로 참조변수를 붙여주면 된다.

타입 f = (int a, int b) -> a > b ? a : b ;

이렇게 해서 람다식을 호출 할 수 있다.
그렇다면 저 타입은 어떻게 지정해준단 말인가?

헷갈리지 말아야 할것이 저 타입은 리턴타입과는 다르다.
위에서 말햇듯 람다식은 익명 클래스와 같다고 하였다.

함수형 인터페이스

자 아래를 보자

interface MyFunction() {
	public abstract int max(int a, int b) ;
}

이를 익명 클래스로 구현하면?

MyFunction f = new MyFunction() {
	public int max (int a, int b){
  		return a > b ? a : b;
   }
}

이렇게 된다. 이를 정확히 람다식으로 대체한다고 생각하면 된다.
어떻게 대체 할 수 있을까?

MyFunction f = (int a, int b) -> a > b ? a: b ;

이렇게 하고 추후에 아래와 같이 사용하면 된다.

f.max(5,6);

즉 람다식과 익명클래스는 역할이 같다.
이렇게 람다식을 설계하면 기존의 자바의 규칙들을 어기지 않으면서도 자연스럽다.

그래서 자바에서는 인터페이스를 통해 람다식을 다루기로 하였으며, 람다식을 다루기 위한 인터페이스를 함수형 인터페이스라고 부르기로 했다.

함수형 인터페이스의 특징

  • 함수형 인터페이스에는 오직 하나의 추상메서드만이 존재해야한다.
  • 반면 static이나 default 메서드는 제약이 없다.

이렇게 하고 람다식을 받으면 하나뿐인 추상메서드가 람다식으로 구현이 되는 구조이다.

함수형 인터페이스에 @FunctionalInterface라는 어노테이션을 붙이면 컴파일러가 올바르게 구현했는지 체크해준다. 하지만 이건 필수도 아니고 이 어노테이션을 안붙였다고 함수형 인터페이스가 아닌 것도 아니다.

실제 사용 예

내가 이 글을 쓰게 된 이유도 이 예시 때문이다. 나는 분명히 람다식을 배웠었는데, 이 예제를 보고 고개가 갸우뚱 했다.

간단한 유저 객체

@Data
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    private String password;
    private String roles; //USER,ADMIN

    public List<String> getRoleList(){
        if(this.roles.length() > 0){
            return Arrays.asList(this.roles.split(","));
        }
        return new ArrayList<>();
    }
}

람다식을 사용한 부분

public class PrincipalDetails implements UserDetails {
    private User user;

    public PrincipalDetails(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        user.getRoleList().forEach(r->{
            authorities.add(() -> r); //이 부분을 보세요
        });
        return authorities;
    }

보면알겠지만, user.getRoleList()List<String>을 반환한다.
하지만 람다식을 쓴부분을 보면 GrantedAuthority에 직접 람다식을 이용해 넣는다.

나는 처음이라 굉장히 헷갈리고 잘못 생각했다. 어떻게 GrantedAuthority에 String을 넣지? 혹시 자동으로 Auto Casting 되는 건가?
하지만 그게 아니고 GrantedAuthority는 추상메서드가 하나 뿐인 인터페이스 였다.
비록 @FuntionalInterface가 붙지 않았지만 그것은 필수가 아니다. 즉 이는 아래와 같은 표현이다.

public class PrincipalDetails implements UserDetails {
    private User user;

    public PrincipalDetails(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        user.getRoleList().forEach( r -> {
        	new GrantedAuthority{
            	@Override
        		String getAuthority(){
            		return r;
                }
            }
        });
        return authorities;
    }

GrantedAuthority 클래스

public interface GrantedAuthority extends Serializable {
	String getAuthority();

}
profile
더 좋은 구조를 고민하는 개발자 입니다

0개의 댓글