함수형 프로그래밍 기법을 지원하는 자바의 문법요소이다.
메서드를 하나의 '식(expression)' 으로 표현한 것으로, 코드를 매우 간결하면서 명확하게 표현할 수 있다는 큰 장점이 있다.
매개변수의 타입을 추론할 수 있는 경우 타입 생략 가능하다.
매개변수가 하나인 경우에는 소괄호() 생략 가능하다.
함수의 바디가 한줄로만 이루어진 경우에는 중괄호{} 및 return문 세미콜론; 생략이 가능하다.
함수의 바디가 return 문 한줄로만 이루어진 경우에는 중괄호{}를 생략할 수 없고
중괄호가 있으면 각 줄마다 세미콜론; 을 생략할 수 없다.
(int x) -> {return x;}; // 생략전
x -> x; // 매개변수가 하나이기 때문에 소괄호 생략, 바디가 한줄로만 이루어지 있기 때문에 중괄호 및 return문 생략
(x, y) -> { // 매개변수가 하나 이상이기 때문에 소괄호 생략 불가, 바디가 여러 줄로 작성되어 중괄호, 세미콜론, return문 생략 불가
x = 30;
y = 40;
return x * y;
};
추상 메소드를 하나만 가지고 있는 인터페이스를 뜻한다. 그리고 람다식은 이러한 함수형 인터페이스를 기반으로만 작성이 될 수 있다. 즉, 함수형 인터페이스를 사용하는 이유는 람다식은 함수형 인터페이스로만 접근이 가능하기 때문에 사용한다.
@FunctionalInterface 애너테이션을 인터페이스에 붙혀주면 더 명시적으로 구분할 수 있고 함수형 인터페이스 규칙을 위반하면 컴파일시 에러가 발생하게 된다.
아래의 코드는 자바에서 제공하는 기본 함수형 인터페이스 이다. 자바에서는 자주 사용할만한 함수형 인터페이스를 이미 정의 해두었기 때문에 특별한 경우가 아니면 해당 인터페이스를 사용하면 된다.
함수형 인터페이스를 보면 제네릭을 사용하여 Function<Integer, Integer> 에서 매개변수 타입과 반환타입이 정해진다. main 메소드 내부에서 함수형 인터페이스 참조 변수를 선언하고 람다식을 대입하면 자동으로 함수형 인터페이스의 단 1개뿐인 추상메서드 apply 메소드가 정의되면서 객체를 생성한다.
람다식의 형태는 매개 변수를 가진 코드 블록이므로 마치 자바의 메소드를 선언하는 것처럼 보인다. 하지만, 자바는 메소드를 단독으로 선언할 수 없고 항상 클래스의 구성 멤버로 선언하기 때문에 람다식은 단순히 메소드를 선언하는 것이 아니라 이 메소드를 가지고 있는 객체를 생성해 낸다. 그리고 이 객체의 타입은 다름 아닌 인터페이스이다. "인터페이스 변수 = 람다식;"과 같이 선언하는 것이다. 즉, 람다식은 인터페이스의 익명 구현 객체를 생성한다는 말이 된다.
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
public class Main {
public static void main(String[] args) {
Function<Integer, Integer> function = n -> n * 10;
System.out.println(function.apply(20)); // 200이 출력된다.
}
}
Function<T, R>
BiFunction<T, U, R>
Consumer<T>
Supplier<T>
Predicate<T>
UnaryOperator<T> extends Function<T, T>
BinaryOperator<T> extends BiFunction<T,T,T>
메소드 참조 방법
스태틱 메소드 | 참조 클래스::메소드 |
인스턴스 메소드 | 객체 참조변수::메소드 |
생성자 | 참조 클래스::new |
class Member{
private String name;
private int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public static String hi(String name){
return "hi " + name;
}
}
class Example {
public void test(){
UnaryOperator<String> unaryOperator = Member::hi; // 스태틱 메소드 참조사용
System.out.println(unaryOperator.apply("Json")); // hi Json 출력
Member member = new Member("Json",30);
Supplier supplier = member::getName; //인스턴스 메소드참조 사용
System.out.println(supplier.get()); //Json 출력
BiFunction<String,Integer,Member> biFunction = Member::new; // 생성자 참조 사용
Member member1 = biFunction.apply("Json",30);
System.out.println(member1.getName()); // Json 출력
System.out.println(member1.getAge()); // 30 출력
}
}
메소드참조를 사용할 때 호출하는 메소드의 매개변수 개수와 함수형인터페이스의 추상 메소드 매개변수의 개수를 일치하게 잘 고려하여 호출해야 한다.
인스턴스 메소드 getName은 매개변수가 없고 Supplier 인터페이스의 추상 메소드 get 도 매개변수가 없기 때문에 컴파일 에러가 없지만 만약 Consumer 함수형 인터페이스로 변경할 경우 Consumer의 추상메소드 accept 는 매개변수를 하나 필요로 하기에 컴파일 에러를 발생시킨다.