java8

자바8의 새롭게 바뀐 부분이 많지만 그 중 내가 자주 쓰는걸 정리 해보겠다.

@FunctionalInterface

첫 번째로 @FunctionalInterface 인터페이스다.
람다를 쓰긴 위한 애노테이션이다. 만약 저 애노테이션이 붙은거라면 람다 표현식을 사용 할 수 있다.
하지만 명시적으로 지정 하지 않더라도 abstract 메소드가 한개라면 람바 표현식을 사용 할 수 있다.
만약 저 애노테이션을 사용한다면 abstract 메소드가 2개 이상 있을경우 컴파일 타임에 에러가 난다.
자바 기본 패키지에 있는 Function이라는 인터페이스다.

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);

    //...
}

한번 커스텀한 interface를 만들어보자.
두개의 같은 타입 파라미터를 넘겨서 두개를 더하는 인터페이스다.

@FunctionalInterface
interface AddFunction<T> {

    T apply(T t, T t1);
}

사용해 보자

AddFunction<Integer> add = (i, j) -> i + j;
System.out.println(add.apply(10, 20));

AddFunction<String> add1 = (i, j) -> i + j;
System.out.println(add1.apply("hello", " world"));

결과는 다음과 같을 것이다.

30
hello world

lambda

두 번째론 람다표현식이다. 자바 개발자 한테는 조금 생소한 표현식 이다. 하지만 다른 언어들은 일찌감치 람다 표현식을 사용하고 있다.
스칼라, 파이썬 하스켈, c#등 여러가지 언어에서 사용 하고 있다.
한번 살펴보자.
우리가 쓰레드를 사용 할때 이용하는 인터페이스중 Runnable가 있다. 이 것도 java8에선 FunctionalInterface로 구현되었다.

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

우리가 java8 이전에는 이렇게 사용하곤 했다. 우리가 흔히 말하는 익명 클래스다. 예를 든 코드다 실질적으로 이렇게 사용하진 않는다.

Runnable runnable = new Runnable() {
     @Override
     public void run() {
         System.out.println("run 호출");
     }
};
runnable.run();

그런데 람바를 쓰면 아주 간단하게 바뀐다. 한번보자.

Runnable runnable = () -> System.out.println("run 호출");
runnable.run();

6줄을 단 한줄로 바꿔버렸다. 대단하다.
그럼 어떻게 바뀌는지 한번 보자.
() 이거는 파라미터다 Runnableabstract 메소드 run은 받는 파라미터가 한개도 없다. 그래서 괄호만 해주는거다.
-> 애로우?라 부른다? 람다? 인가? 흠 그리고 다음 나오는게 메소드 바디이다. 간단하다.
그럼 다음 예제도 살펴보자
이번엔 아까 위에서 봤던 Function 인터페이스다. 이 인터페이스를 아마 자주 사용할 듯 하다.
일단 람다를 쓰지말고 한번 해보자.

Function<Integer,String> function = new Function<Integer, String>() {
    @Override
    public String apply(Integer integer) {
        return String.valueOf(integer);
    }
};
String str = function.apply(1010);
System.out.println(str);

일단 Function 인터페이스는 제네릭타입이다. 첫 번째는 파라미터타입 두 번째는 리턴 타입이다.
한마디로 어떤 타입의 파라미터로 넘기면 다른 타입을 반환하는걸 의미한다. 같은 타입을 지정해도 상관없다. 이런걸 identity라 부른다.
위의 코드는 int를 String으로 변환하는 코드다.
이제 람다로 바꿔보자!

Function<Integer,String> function = x -> String.valueOf(x);
String str = function.apply(1010);
System.out.println(str);

이것도 아주 깔끔하고 짧게 바뀌었다.
다시 한번 살펴보자. apply 메소드는 파라미터가 한개다.그래서 x라는 변수 한개를 선언했다.(파라미터가 한개일땐 ()로 감싸주지 않아도 된다.)
그리고 그 x로 String.valuOf를 이용하여 변환 하였다.
그런데 String.valueOf를 호출하기전에 그 파라미터가 어떤건지 한번 Print로 출력하고 싶다.
그럼 메소드 바디를 감싸주면 된다. 컬리브레이스로 감싸자 해보자.

Function<Integer,String> function = x -> {
    System.out.println(x);
    return String.valueOf(x);
};

이외에도 자주 쓰는 인터페이스는
Consumer<T> 어떤 타입의 파라미터로 받는다 리턴은 없다.
Predicate<T> 어떤 타입의 파라미터로 받는다 리턴은 Boolean 이다.
Supplier<T> 어떤 타입으로 리턴한다. 파라미터는 없다.

이 외에도 java.util.function 패키지에 가보면 function 인터페이스가 있다. 한번씩 보자!
다음 시간에 Stream을 공부해보겠다.