함수형 프로그래밍 기법을 지원하는 자바의 문법요소이다.
//기존 메서드 표현 방식
void sayhello() {
System.out.println("HELLO!")
}
//위의 코드를 람다식으로 표현한 식
() -> System.out.println("HELLO!")
람다 식에서는 기본적으로 반환타입과 이름을 생략할 수 있다.
익명함수(annoymous function)이라고 부르기도 한다.
int sum(int num1, int num2) {
return num1 + num2;
}
이런 식을
(int num1, int num2) -> { // 반환타입과 메서드명 제거 + 화살표 추가
return num1 + num2;
}
이렇게 가능하다.
예시
((((((1))))))
// 기존 방식
void example1() {
System.out.println(5);
}
// 람다식
() -> {System.out.println(5);}
---
((((((2))))))
// 기존 방식
int example2() {
return 10;
}
// 람다식
() -> {return 10;}
---
((((((3))))))
// 기존 방식
void example3(String str) {
System.out.println(str);
}
// 람다식
(String str) -> { System.out.println(str);}
특정 조건이 충족되면 람다식을 더욱 축약해서 표현할 수 있다.
// 기존 방식
int sum(int num1, int num2) {
return num1 + num2;
}
// 람다식
(int num1, int num2) -> {
num1 + num2
}
이랬던 것을
(int num1, int num2) -> num1 + num2
이렇게 가능하다.
매개변수의 타입을 유추할 수 있는 경우 생략할 수 있다.
(num1, num2) -> num1 + num2
람다식 또한 객체이다.
더 정확히는 이름이 없기 때문에 익명객체라고 할 수 있다.
익명클래스란 객체의 선언과 생성을 동시에 하여 오직 하나의 객체를 생성하고, 단 한번만 사용되는 일회용 클래스이다.
// sum 메서드 람다식
(num1, num2) -> num1 + num2
이런 메서드를
new Object() {
int sum(int num1, int num2) {
return num1 + num1;
}
}
이렇게 표현할 수 있다.
public class LamdaExample1 {
public static void main(String[] args) {
// 람다식 Object obj = (num1, num2) -> num1 + num2; 로 대체 가능
Object obj = new Object() {
int sum(int num1, int num2) {
return num1 + num1;
}
};
obj.sum(1, 2);
}
}
출력 결과
java: cannot find symbol
symbol: method sum(int,int)
location: variable obj of type java.lang.Object
하지만 Ojbect
클래스에는 sum
이라는 메서드가 없기 때문에, Object
타입의 참조변수에 담는다고 해도 sum
메서드를 사용할 수 없다.
이를 해결하기 위해 함수형 인터페이스(Function Interface)를 사용할 수 있다.
즉, 자바에서 함수형 프로그래밍을 하기 위한 새로운 문법 요소를 도입하는 대신, 기존의 인터페이스 문법을 활용하여 람다식을 다루는 것이라고 할 수 있다.
함수형 인터페이스는 단 하나의 추상 메서드만 선언될 수 있는데, 이는 람다식과 인터페이스의 메서드가 1:1로 매칭되어야 하기 때문이다.
public class LamdaExample1 {
public static void main(String[] args) {
/* Object obj = new Object() {
int sum(int num1, int num2) {
return num1 + num1;
}
};
*/
ExampleFunction exampleFunction = (num1, num2) -> num1 + num2;
System.out.println(exampleFunction.sum(10,15));
}
@FunctionalInterface // 컴파일러가 인터페이스가 바르게 정의되었는지 확인하도록 합니다.
interface ExampleFunction {
int sum(int num1, int num2);
}
// 출력값
25
@FunctionalInterface
public interface MyFunctionalInterface {
void accept();
}
이 인터페이스를 타겟 타입으로 갖는 람다식은 아래와 같은 형태로 작성해야한다. 람다식에서 매개변수가 없는 이유는 accept()
가 매개변수를 가지지 않기 때문이다.
MyFunctionalInterface example = () -> { ... };
// example.accept();
@FunctionalInterface
interface MyFunctionalInterface {
void accept();
}
public class MyFunctionalInterfaceExample {
public static void main(String[] args) throws Exception {
MyFunctionalInterface example = () -> System.out.println("accept() 호출");
example.accept();
}
}
// 출력값
accept() 호출
@FunctionalInterface
public interface MyFunctionalInterface {
void accept(int x);
}
//예제
public class MyFunctionalInterfaceExample {
public static void main(String[] args) throws Exception {
MyFunctionalInterface example;
example = (x) -> {
int result = x * 5;
System.out.println(result);
};
example.accept(2);
example = (x) -> System.out.println(x * 5);
example.accept(2);
}
}
// 출력값
10
10
이런식으로 매개변수가 있는 람다식을 표현할 수 있다.
@FunctionalInterface
public interface MyFunctionalInterface {
int accept(int x, int y);
}
//예제
public class MyFunctionalInterfaceExample {
public static void main(String[] args) throws Exception {
MyFunctionalInterface example;
example = (x, y) -> {
int result = x + y;
return result;
};
int result1 = example.accept(2, 5);
System.out.println(result1);
example = (x, y) -> { return x + y; };
int result2 = example.accept(2, 5);
System.out.println(result2);
example = (x, y) -> x + y;
//return문만 있을 경우, 중괄호 {}와 return문 생략가능
int result3 = example.accept(2, 5);
System.out.println(result3);
example = (x, y) -> sum(x, y);
//return문만 있을 경우, 중괄호 {}와 return문 생략가능
int result4 = example.accept(2, 5);
System.out.println(result4);
}
public static int sum(int x, int y){
return x + y;
}
}
//출력값
7
7
7
7
람다식에서 불필요한 매개변수를 제거할 때 주로 사용한다.
더욱 더 간편하게 사용 가능
(left, right) -> Math.max(left, right)
left와 right 중 더 큰 수를 리턴하는 식이다.
IntBinaryOperator operato = Math :: max; //메서드 참조
이런 식으로 간단하게 사용 가능하다.
정적 매서드를 사용할 때에는 클래스 이름 뒤에 ::
기호를 붙이고 정적 메서드 이름을 기술하면 된다.
클래스::메서드
인스턴스 메서드의 경우
참조 변수::메서드
//예제
//Calculator.java
public class Calculator {
public static int staticMethod(int x, int y) {
return x + y;
}
public int instanceMethod(int x, int y) {
return x * y;
}
}
import java.util.function.IntBinaryOperator;
public class MethodReferences {
public static void main(String[] args) throws Exception {
IntBinaryOperator operator;
/*정적 메서드
클래스이름::메서드이름
*/
operator = Calculator::staticMethod;
System.out.println("정적메서드 결과 : " + operator.applyAsInt(3, 5));
/*인스턴스 메서드
인스턴스명::메서드명
*/
Calculator calculator = new Calculator();
operator = calculator::instanceMethod;
System.out.println("인스턴스 메서드 결과 : "+ operator.applyAsInt(3, 5));
}
}
/*
정적메서드 결과 : 8
인스턴스 메서드 결과 : 15
*/
(a,b) -> new 클래스(a,b)
클래스 뒤에 ::
기호를 붙이고 new 연산자를 기술하면 된다.
클래스::new
생성자가 오버로딩 되어 여러 개가 있을 경우 컴파일러는 함수형 인터페이스의 추상 메서드와 동일한 매개 변수 타입과 개수를 가지고 있는 생성자를 찾아 실행한다.