우선 람다를 알아보기전에 oracle 공식 홈페이지로 잠깐 이동해볼까요?
https://www.oracle.com/index.html
홈페이지에 들어가셔서 Resources를 눌러주세요
Documentation으로 갑니다!
Java를 눌러주시구요!!
Java SE Technical Documentation을 눌러주세요
Java SE로 들어가주시면요
람다는 JDK8버전부터 나왔다고합니다 JDK 8을 눌러주세요!
Java Tutorials Learning Paths로 이동합니다.
드디어 람다식을 찾았네요!!
람다에 대한 설명이 나와있습니다~
Lamda Expression이라고하네요 Expression 즉, 식입니다.
표현식(Expression)과 구문(Statement)이라는 것의 차이가 있습니다.
구문은 제어문을 생각하시면 됩니다! 제어문은 조건문과 반복문 즉, if문 for문 while문 switch문이 있죠. 이러한 문은 행동입니다. 그런데 식은 값이라고 생각할수 있습니다.
삼항연산식(삼항연산자)를 생각하면 (조건)?T:F 의형태로 값이 리턴이 됩니다.
람다도 식이기 때문에 값이 리턴될거라고 예상할수 있겠죠??
람다 표현식을 간단하게 말하면 메소드를 하나의 식으로 표현하는 것입니다.
int min(int x, int y){
return x < y ? x : y;
}
예를 들어 이러한 메소드가 있다고 가정해봅니다. 이러한 메소드를 람다식으로 표현하면
(x, y) -> x < y ? x : y;
이렇게 표현할수 있게됩니다.
예제처럼 메소드를 람다 표현식으로 표현하면, 클래스를 작성하고 객체를 생성하지 않아도 메소드를 사용할 수 있게됩니다!
자바에서는 클래스의 선언과 동시에 객체를 생성하게 되는데요.
하나의 객체만을 생성ㅎ라 수 있는 클래스를 익명 클래스라고합니다.
그렇기 떄문에 자바에서 람다식은 익명클래스와 같다고 할 수 있겠네요.
new Object(){
int min(int x, int y){
return x < y ? x : y;
}
}
@FunctionalInterface
람다 표현식을 사용할 때는 람다 표현식을 저장하기 위한 참조 변수의 타입을 결정해야 합니다.
참조변수타입 참조변수이름 = 람다표현식
이런 문법처럼 람다 표현식을 하나의 변수에 대입할때 사용하는 참조 변수의 타입을 함수형 인터페이스라고 합니다.
함수형 인터페이스는 추상클래스와는 달리 하나의 추상메소드만 가져야합니다.
@FunctionalInterface 어노테이션을 사용해서 함수형 인터페이스임을 명시하여 컴파일러는 해당 인터페이스를 함수형 인터페이스라고 인식하게 됩니다.
자바 컴파일러는 @FunctionalInterface가 명시된 함수형 인터페이스에 두개 이상의 메소드가 선언되면 오류를 발생 시킵니다.
이제 람다식의 여러가지 형태를 알아보도록 하겠습니다.
1. () -> {code};
package com.test01;
@FunctionalInterface
public interface Test01 {
public void prn();
}
해당 인터페이스가 있다고 가정했을때 이 인터페이스를 상속받는 클래스가 있다고 생각해보겠습니다.
package com.test01;
public class MTest {
public static void main(String[] args) {
Test01 test01_1 = new Test01() {
@Override
public void prn() {
System.out.println("Interface 구현!");
}
};
test01_1.prn();
// () -> {code};
Test01 test01_2 = () -> {System.out.println("Lamda!");};
test01_2.prn();
// () -> code; // 바디({})안에 명령이 하나만 있을때는 { } 생략 가능
Test01 test01_3 = () -> System.out.println("Lamda! 재밌다!");
test01_3.prn();
}
}
위의 클래스를 실행하면 이런 결과가 나옵니다.
원래라면 익명클래스를 사용하여
Test01 test01_1 = new Test01() {
@Override
public void prn() {
System.out.println("Interface 구현!");
}
};
이런 방식을 사용하여 인터페이스를 구현하는 방법이 있습니다.
하지만 이 방법을 람다식으로 표현 할 수 있습니다.
Test01 test01_2 = () -> {System.out.println("Lamda!");};
test01_2.prn();
이런 방식이 있는데요! 자바스크립트 ES6 문법의 Arrow Function이랑 비슷하죠??
또한 바디안에 즉 중괄호({ })안에 명령이 하나만 있다면 중괄호도 생략할수 있습니다.
Test01 test01_3 = () -> System.out.println("Lamda! 재밌다!");
test01_3.prn();
2. (parameter) -> {};
package com.test01;
@FunctionalInterface
public interface Test02 {
void prn(int i);
}
아규먼트가 int i가 들어간 추상메소드가 선언된 인터페이스가 생성되어있습니다.
package com.test01;
public class MTest {
public static void main(String[] args) {
// (parameter) -> {};
Test02 test02_1 = (int i) -> {System.out.println("input : " + i);};
test02_1.prn(10);
Test02 test02_2 = (i) -> {System.out.println("input : " + i);};
test02_2.prn(20);
// parameter -> {};
Test02 test02_3 = i -> System.out.println("input : " + i);
test02_3.prn(30);
}
}
위와 같은 방법으로 표현이 가능한데요
Test02 test02_1 = (int i) -> {System.out.println("input : " + i);};
test02_1.prn(10);
이런 방식으로 (parameter) -> {}로 표현 할 수 있습니다.
이 식을 출력할때 10이라는 값을 파라미터로 주었네요.
Test02 test02_2 = (i) -> {System.out.println("input : " + i);};
test02_2.prn(20);
(int i) 부분에서 타입형을 생략 하여 표현 할 수도 있습니다.
Test02 test02_3 = i -> System.out.println("input : " + i);
test02_3.prn(30);
타입 뿐만 아니라 () 도 생략이 가능합니다.
이렇게 parameter -> {} 로 표현 할 수 있습니다.
3. parameter -> {return code};
package com.test01;
// @FunctionalInterface
public interface Test03 {
public int prn(int i);
}
@FunctionalInterface 어노테이션은 생략 할 수 있습니다.
package com.test01;
public class MTest {
public static void main(String[] args) {
// parameter -> {return code;};
Test03 test03_1 = i -> {return i + 10;};
System.out.println(test03_1.prn(10));
// parameter -> code; // return 생략
Test03 test03_2 = i -> i + 20;
System.out.println(test03_2.prn(10));
}
}
Test03 test03_1 = i -> {return i + 10;};
System.out.println(test03_1.prn(10));
이처럼 명령만 들어가는 것이 아닌 return값이 있을수가 있는데요.
인터페이스로 구현한 Test03 인터페이스 안에 prn메소드의 리턴타입을 int로 주었습니다.
Test03 test03_2 = i -> i + 20;
System.out.println(test03_2.prn(10));
이 경우에도 명령이 하나이기 때문에 중괄호 생략이 가능하고 return 또한 생략 할 수 있습니다.
4. (param1, parma2) -> {return code;};
package com.test01;
public interface Test04 {
public int prn(int i, int j);
}
이번엔 파라미터가 2개가 들어갔네요!
package com.test01;
public class MTest {
public static void main(String[] args) {
// (param1, parma2) -> {return code;};
Test04 test04_1 = (n, m) -> {return n + m;};
System.out.println(test04_1.prn(10, 20));
// (param1, param2) -> code;
Test04 test04_2 = (n, m) -> n * m;
System.out.println(test04_2.prn(10, 20));
// (param1, param2) -> {code; return code;};
Test04 test04_3 = (n, m) -> {
System.out.printf("%d * %d = ", n, m);
return n * m;
};
System.out.println(test04_3.prn(30, 40));
}
}
Test04 test04_1 = (n, m) -> {return n + m;};
System.out.println(test04_1.prn(10, 20));
아규먼트가 2개이기 때문에 파라미터도 2개입니다.
Test04 test04_2 = (n, m) -> n * m;
System.out.println(test04_2.prn(10, 20));
마찬가지로 return이 생략 될 수 있습니다.
Test04 test04_3 = (n, m) -> {
System.out.printf("%d * %d = ", n, m);
return n * m;
};
System.out.println(test04_3.prn(30, 40));
이전까지는 () -> {code} 이거나 () -> {return code} 였는데 이번에는 () -> {code; return code;} 형태네요!
자 그러면 람다 표현식이 뭔지는 알겠는데 왜 이렇게 쓰냐구요??
간단한 예제를 하나 보여드리겠습니다.
package com.test02;
public interface MYProc {
double calc(double i, double j);
}
투개의 숫자가 아규먼트인 메소드를 가진 인터페이스가 있습니다.
package com.test02;
import java.util.Scanner;
public class MTest {
public static void main(String[] args) {
MYProc sum = (a, b) -> a + b;
MYProc sub = (a, b) -> a - b;
MYProc mul = (a, b) -> a * b;
MYProc div = (a, b) -> a / b;
MYProc mod = (a, b) -> a % b;
Scanner sc = new Scanner(System.in);
System.out.println("i 입력 : ");
int i = sc.nextInt();
System.out.println("j 입력 : ");
int j = sc.nextInt();
System.out.printf("%d + %d = %.0f\n", i, j, sum.calc(i, j));
System.out.printf("%d - %d = %.0f\n", i, j, sub.calc(i, j));
System.out.printf("%d * %d = %.0f\n", i, j, mul.calc(i, j));
System.out.printf("%d / %d = %.2f\n", i, j, div.calc(i, j));
System.out.printf("%d %% %d = %.0f\n", i, j, mod.calc(i, j));
}
}
해당 클래스에서는 두개의 숫자가 필요한 람다식 5개를 만들었습니다!
람다를 이용하면 메소드 하나하나 구현하는것보다 훨씬 쉽게 구현할수 있고 코드도 간결합니다!
'Java 관련 > Java' 카테고리의 다른 글
[Java] 익명(anonymous) 클래스 (0) | 2022.07.27 |
---|---|
[Java] 자바의 탄생과 특징 (0) | 2022.07.26 |
[Java] 쓰레드(Thread) (0) | 2021.11.20 |
[Java] 입출력(IO - InputOutput) (0) | 2021.11.19 |
[Java] 예외 처리(Exception) (0) | 2021.11.18 |