목차
0. 람다 표현식의 예시
- 람다 표현식에 들어가기 앞서 람다가 어떻게 생겼는지 맛보기로 살펴봅시다.
0.1 List에 들어있는 데이터 출력하기
final List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
// for
for(int i=0;i<list.size();i++) {
System.out.println(list.get(i));
}
// foreach
for(int num : list) {
System.out.println(num);
}
// lambda
list.forEach(num -> System.out.println(num));
0.2 List에 들어있는 데이터 중 짝수만 출력하기
final List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
// foreach
for(int num : list) {
if(num % 2 == 0) {
System.out.println(num);
}
}
// lambda
list.stream()
.filter(num -> num % 2 == 0)
.forEach(evenNum -> System.out.println(evenNum));
0.3 약간 복잡한 예시
- 람다식을 사용하면 편하고 가독성이 좋다는 것으로 알고 있는데 별로 잘 모르겠다면 약간 더 복잡한 예제를 해 보겠습니다.
- 주식 티커명을 담고 있는 배열이 존재하고 그 배열에서 "a"로 시작하는 값을 대문자로 바꿔서 ArrayList로 반환하세요.
[람다 미사용]
import java.util.ArrayList;
import java.util.List;
public class Exe {
public static List<String> change(String[] stockTicker) {
List<String> tempList = new ArrayList<>();
for (String stock : stockTicker) {
if(stock.startsWith("a")) {
tempList.add(stock.toUpperCase());
}
}
return tempList;
}
public static void main(String[] args) {
final String[] stockTicker = {"aapl", "msft", "googl", "amzn"};
List<String> list = change(stockTicker);
// 결과출력
System.out.println(list.getClass()); // class java.util.ArrayList
for (String stock : list) {
System.out.println(stock); // AAPL, AMZN
}
}
}
[람다 사용]
- 람다를 사용할 경우 메소드를 따로 만들 필요도 없고(= 익명메소드), 가독성도 높아지고 간결해집니다.
- 자바 8에서 등장한 컬렉션이나 배열을 다루기 쉽게 도와주는 강력한 도구인 Stream API를 사용하기 위해서는 람다를 알고 있어야 합니다.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Exe {
public static void main(String[] args) {
final String[] stockTicker = {"aapl", "msft", "googl", "amzn"};
// lambda
List<String> stockList = Arrays.stream(stockTicker)
.filter(stock -> stock.startsWith("a"))
.map(stock -> stock.toUpperCase())
.collect(Collectors.toList());
// 결과출력
System.out.println(stockList.getClass()); // class java.util.ArrayList
stockList.forEach(stock -> System.out.println(stock)); // AAPL, AMZN
}
}
1. 람다 표현식이란?
- 람다, 또는 람다 함수는 프로그래밍 언어에서 사용되는 개념으로 익명 함수(Anonymous functions)를 지칭하는 용어입니다.
- 익명 함수(Anonymous Function)란 위의 예시에서도 봤듯이 함수의 이름이 없는 함수입니다.
- 익명 함수들은 모두 일급 객체(First-Class Object)입니다.
- 일급 객체의 특징으로는 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 가리킵니다.
- 보통 함수에 매개변수로 넘기기, 수정하기, 변수에 대입하기와 같은 연산을 지원할 때 일급 객체라고 합니다.
2. 람다 표현식의 장점
- 코드의 간결성
- 좋은 가독성
- 생산성 상승 (따로 메소드를 만드는 과정을 생략)
- 병렬 처리에 유리합니다. (parallelStream() 사용 시 쉽게 병렬 처리할 수 있습니다.)
3. 람다 표현식의 단점
- 모든 원소를 전부 순회하는 경우는 람다식이 성능이 조금 느립니다.
- 람다식을 남용하면 오히려 코드를 이해하기 어려울 수 있습니다.
- 디버깅 시 함수 콜 스택 추적이 극도로 어렵습니다.
따라서 결국 무조건 람다가 좋다는 보장은 없고 상황에 따라 필요에 맞는 방법을 사용하는 것이 중요합니다.
4. 자바 8에서 람다 표현식을 알아야 하는 이유
- 자바 8에서 등장한 강력한 API인 Stream API를 사용하기 위해서는 람다를 알고 있어야 합니다.
- Stream API란 컬렉션, 배열등의 저장 요소를 하나씩 참조하며 함수형 인터페이스(람다식)를 적용하며 반복적으로 처리할 수 있도록 해주는 강력하고 매우 편한 기능입니다.
- 이러한 Stream API는 매개변수로 함수형 인터페이스를 받고 있고, 람다 표현식은 반환 값으로 함수형 인터페이스를 반환하고 있습니다. 따라서 Stream API를 이해하기 위해서는 람다식과 함수형 인터페이스를 반드시 알고 있어야 합니다.
- Stream API (함수형 인터페이스); // 매개변수로 함수형 인터페이스
- 함수형 인터페이스 = 람다 표현식 { return 함수형 인터페이스 }; // 반환 값으로 함수형 인터페이스
- Stream API (람다 표현식);
4.1 Stream API
- Stream 인터페이스 메소드의 매개변수를 보면 자바에서 기본적으로 제공하는 함수형 인터페이스를 받고 있는 것을 알 수 있습니다.
- 자바의 함수형 인터페이스에 대해 잘 모르겠다면 다음 링크를 참조해주세요
public interface Stream<T> extends BaseStream<T, Stream<T>> {
Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
void forEach(Consumer<? super T> action);
<R, A> R collect(Collector<? super T, A, R> collector);
// (...) 생략
}
4.2 자바 8에서 제공하는 함수형 인터페이스와 람다 표현식
[Function<T, R>]
- Function<T, R> 는 자바 8부터 기본적으로 제공하는 함수형 인터페이스입니다.
- T타입을 받아서 R타입을 리턴하는 함수 인터페이스
- 자바의 함수형 인터페이스에 대해 잘 모르겠다면 다음 링크를 참조해주세요
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
[함수형 인터페이스 구현]
- 함수형 인터페이스를 구현하는 방법으로는 익명 클래스와 람다 표현식이 있습니다.
- 람다식이 훨씬 간단하므로 람다식을 권장합니다.
- 람다식은 반환 값으로 함수형 인터페이스를 반환합니다.
import java.util.function.Function;
public class Exe {
public static void main(String[] args) {
// 익명 클래스를 이용한 함수형 인터페이스 구현
Function<Integer, String> plus10AndString = new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return Integer.toString(integer + 10);
}
};
String result = plus10AndString.apply(10);
System.out.println(result);
// 람다식은 함수형 인터페이스를 반환
Function<Integer, String> plus10AndStringLambda = num -> Integer.toString(num + 10);
String resultLambda = plus10AndStringLambda.apply(10);
System.out.println(resultLambda); // result = 20
}
}
5. 람다 표현식의 형태
[람다 형식]
(인자 리스트) -> {바디}
[인자 리스트 예시]
1. 인자가 없을 경우
: ()
2. 인자가 한 개일 경우
: (인자) 또는 인자
3. 인자가 여러 개일 경우
: (인자1, 인자2, ... )
4. 인자의 타입은 일반적으로 생략하지만 명시할 수도 있습니다. 대신 인자가 하나이더라도 괄호를 생략할 수 없습니다.
그리고 인자가 여러 개일 경우 타입 선언 시 인자 모두 선언해줘야 합니다.
: (Interger 인자) 또는 (Interger 인자1, Interger 인자2, ... )
5. 자바 10 이상에서는 데이터 타입으로 var로 선언도 가능합니다. 자바스크립트의 변수 선언과 비슷하다고 보면 됩니다.
: (var 인자) 또는 (var 인자1, var 인자2, ...)
[바디 예시]
1. 명령어가 한 줄인 경우 {} 생략 가능합니다.
: () -> { 명령어; };
: () -> 명령어;
2. 명령어가 여러 줄인 경우 {}를 사용해서 묶어 줍니다.
: () -> {
명령어1;
명령어2;
}
3. 명령어가 한 줄인 경우 return 생략 가능합니다.
() -> 1
() -> {return 1;} // 단 return을 사용할 경우 {}를 생략할 수 없습니다.
6. 람다 표현식의 예시
(인자 리스트)
- 인자가 없을 경우
- 인자가 한 개일 경우
- 인자가 여러 개일 경우
- 인자의 타입은 일반적으로 생략하지만 명시할 수도 있습니다.
- 자바 10 이상에서는 데이터 타입으로 var로 선언도 가능합니다.
// 함수형 인터페이스
@FunctionalInterface
public interface TestInterface {
void test();
}
public class Exe {
public static void main(String[] args) {
//1. 인자가 없는 경우
TestInterface testInterface1 = () -> System.out.println("Hello Lambda");
}
}
public class Exe {
public static void main(String[] args) {
// 2. 인자가 한 개인 경우
Function<Integer, Boolean> oddCheck1 = num -> num%2 == 1;
Function<Integer, Boolean> oddCheck2 = (num) -> num%2 == 1;
// 3. 인자가 여러 개인 경우
BiFunction<Integer, Integer, Integer> add1 = (num1, num2) -> num1 + num2;
// 4. 인자의 타입 선언
BiFunction<Integer, Integer, Integer> add2 = (Integer num1, Integer num2) -> num1 + num2;
// 5. 인자의 타입 선언 var (자바 10)
BiFunction<Integer, Integer, Integer> add3 = (var num1, var num2) -> num1 + num2;
}
}
{바디}
- 명령어가 한 줄인 경우
- 명령어가 여러 줄인 경우
- 명령어가 한 줄인 경우 return 생략 가능합니다.
public class Exe {
public static void main(String[] args) {
// 1. 명령어가 한 줄인 경우
Consumer<String> consumer4 = name -> System.out.println("Hello " + name);
Consumer<String> consumer5 = name -> {
System.out.println("Hello " + name);
};
// 2. 명령어가 여러 줄인 경우
Consumer<String> consumer6 = name -> {
System.out.println("Hello " + name);
System.out.println("Bye " + name);
};
// 3. 명령어가 한 줄인 경우 return
Supplier<Integer> get10 = () -> 10;
Supplier<Integer> get10_2 = () -> { return 10; };
}
}
'Backend > Java' 카테고리의 다른 글
[Java] 자바 8 인터페이스의 Default 메소드와 Static 메소드 (0) | 2021.12.17 |
---|---|
[java] 자바 8 메소드 레퍼런스 (Method Reference) (0) | 2021.12.15 |
[java] 자바 8 표준 함수형 인터페이스 (java.util.function) (1) | 2021.12.07 |
[java] 자바8 함수형 인터페이스 (@FunctionalInterface) (0) | 2021.12.06 |
[JAVA] 자바 AES 암호화 하기 (AES-128, AES-192, AES-256) (0) | 2021.08.28 |