📌람다식 (Lambda expression)
객체 지향 프로그래밍이 나오기 전부터 Lisp 또는 Scheme와 같은 함수적 프로그래밍 언어들이 있었다. 현업에서는 호응을 얻지 못하였지만, 빅데이터가 각광받으면서 이를 프로그램적으로 다룰 방법이 필요해짐에 따라 멀티 코어를 활용한 분산처리와, 병렬화 기술이 필요하였다. 즉 이제 하나의 CPU안에 다수의 코어를 삽입하는 멀티 코어 프로세서들이 등장하면서 일반 프로그래머에게도 병렬화 프로그래밍에 대한 필요성이 생기기 시작했다.
이러한 추세에 대응하기 위해 Java 8에서는 병렬화를 위해 컬렉션(배열, List, Set, Map)을 강화했고, 이러한 컬렉션을 더 효율적으로 사용하기 위해 스트림(Stream)을 강화했다. 또 스트림을 효율적으로 사용하기 위해 함수형 프로그래밍이, 다시 함수형 프로그래밍을 위해 람다가, 또 람다를 위해 인터페이스의 변화가 수반됐다. 람다를 지원하기 위한 인터페이스를 함수형 인터페이스라고 한다.
람다는 한 마디로 코드 블록이다. 기존의 코드 블록은 반드시 매소드 내에 존재해야 했다. 그래서 코드 블록만 갖고 싶어도 기존에는 코드 블록을 위해 메소드를, 다시 메소드를 사용하기 위해 익명 객체를 만들거나 하는 식이 었지만, 자바 8부터는 람다식을 지원하면서 코드 블록을 만들기 위해 이러한 수고를 할 필요가 없어졌다. 또한 코드 블록인 람다를 메소드의 인자나 반환 값으로 사용할 수 있게 되었다. 이것의 의미는 코드 블록을 변수처럼 사용할 수 있다는 것이다.
람다식은 간단히 말해서 메서드를 하나의 '식'으로 표현한 것이다. 람다식은 함수를 간략하면서도 명확한 식으로 표현할 수 있게 해준다. 메서드를 람다식으로 표현하면 메서드의 이름과 반환값이 없어지므로, 람다식을 '익명 함수'이라고도 한다.
람다식 작성
EX)
//EX1)
int max(int a, int b){
return a>b?a:b;
}
(a,b) -> a > b ? a : b
//EX2)
void printVar(String name, int i){
System.out.println(name+"="+i);
}
(name,i) -> System.out.println(name+"="+i)
//EX3)
int square(int x){
return x*x;
}
x -> x * x
//EX4)
int roll(){
return (int) (Math.random()*6);
}
() -> (int)(Math.random()*6)
//EX5)
int sumArr(int[] arr){
int sum=0;
for(int i : arr)
sum+=i;
return sum;
}
(int[] arr) -> {
int sum=0;
for(int i : arr)
sum+=i;
return sum;
}
함수형 인터페이스
간단하게 설명하도록 하겠다. 추상 메소드를 하나만 갖는 인터페이스를 함수형 인터페이스라 한다. 이런 함수형 인터페이스만을 람다식으로 변경할 수 있다.
모든 인터페이스를 람다식의 타켓 타입으로 사용할 수 없다. 람다식이 하나의 메소드를 정의하기 때문에 두 개 이상의 추상 메소드가 선언된 인터페이스는 람다식을 이용해서 구현 객체를 생성할 수 없다. 하나의 추상 메소드가 선언된 인터페이스만이 람다식의 타켓 타입이 될 수 있는데, 이러한 인터페이스를 함수적 인터페이스라고 한다.
함수적 인터페이스를 작성할 때 두 개 이상의 추상 메소드가 선언되지 않도록 컴파일러가 체킹해주는 기능이 있는데, 인터페이스 선언 시 @FunctionalInterface 어노테이션을 붙이면 된다. 이 어노테이션은 두 개 이상의 추상 메소드가 선언되면 컴파일 오류를 발생시킨다. @FunctionalInterface 어노테이션은 선택사항이다. 이 어노테이션이 없더라도 하나의 추상 메소드만 있다면 모두 함수적 인터페이스이다. 이 어노테이션은 실수록 두 개 이상의 추상 메소드를 선언하는 것을 방지하기 위해서 붙여주는 것이다.
자바에서 제공되는 표준 API에서 한 개의 추상 메소드를 가지는 인터페이스들은 모두 람다식을 이용해서 익명 구현 객체로 표현이 가능하다. 예를 들어 스레드의 작업을 정의하는 Runnable 인터페이스는 매개 변수와 리턴값이 없는 run() 메소드만 존재하기 때문에 람다식을 이용해서 Runnable 인스턴스를 생성시킬 수 있다.
📌자바 8 API에서 제공하는 함수형 인터페이스
위에서 살펴본 코드들은 전부 사용자 정의 함수형 인터페이스로 만들어 사용한 것이다. 자바 8 API 설계자들은 개발자들이 많이 쓸 것이라고 예상되는 함수형 인터페이스를 java.util.function 패키지와 여러 패키지에서 제공하고 있다. java.util.function 패키지에서는 총 43개의 함수형 인터페이스를 제공하고 있는데 만약 필요로 함는 함수형 인터페이스가 존재하지 않는다면 역시 사용자 정의 함수형 인터페이스를 정의해서 사용하면 된다.
💡대표적인 함수형 인터페이스
함수형 인터페이스 | 추상 메소드 | 용도 |
Runnable | void run() | 실행할 수 있는 인터페이스 |
Supplier<T> | T get() | 제공할 수 있는 인터페이스 |
Consumer<T> | void accept(T t) | 소비할 수 있는 인터페이스 |
Function<T, R> | R apply(T t) | 입력을 받아서 출력할 수 있는 인터페이스 |
Predicate<T> | Boolean test(T t) | 입력을 받아 참/거짓을 단정할 수 있는 인터페이스 |
UnaryOperator<T> | T apply(T t) | 단항(Unary) 연산할 수 있는 인터페이스 |
BiConsumer<T, U> | void accpet(T t, U u) | 이항 소비자 인터페이스 |
BiFunction<T, U, R> | R apply(T t, U u) | 이항 함수 인터페이스 |
BiPredicate<T, U> | Boolean test(T t, U u) | 이항 단정 인터페이스 |
BinaryOperator<T, T> | T apply(T t, T t) | 이항 연산 인터페이스 |
📌컬렉션 스트림에서 람다 사용
//기존 코드
for(int i=0;i<ages.length;i++){
if(ages[i] < 20){
System.out.format("나이 %d살은 입장 불가능!", ages[i]);
}
}
//for each구문
for(int age : ages){
if(age < 20){
System.out.format("나이 %d살은 입장 불가능!", age);
}
}
//컬렉션 스트림 이용
Arrays.stream(ages)
.filter(age -> age < 20)
.forEach(age -> System.out.format("나이 %d살은 입장 불가능!", age));
기존 코드를 써도 무방하지만 변수의 초깃값을 잘못 주거나 등호를 잘못 작성하는듯 코드를 작성하는데 있어서 실수가 발생할 때가 있다. 개발자의 실수를 방지하고 편리하게 코드를 작성할 수 있도록 for each 구문이 나왔다.
for each 구문이 나옴으로 배열 첨자를 사용하지 않는 코드로 변경되고 코드가 간소화 되었다.
마지막 컬렉션 스트림을 이용하여 코드를 작성하면 코드가 더 간소화된다. 코드를 분석해보도록 하겠다.
Arrays.stream(ages)
기존 배열을 이용해 스트림을 얻으려면 Arrays 클래스의 stream() 정적 메소드를 사용한 것이다.
.filter(age -> age < 20 )
filter 메소드는 SQL 구문에서 where 절과 같은 역할을 한다. where과 마찬가지고 true/false를 반환하는 조건이 필요하다. 따라서 자바 8 API에서 제공하는 함수형 인터페이스 중 참/거짓을 단정할 수 있는 Predicate 함수형 인터페이스를 filter 메소드의 인자로 제공하였다.
.forEach(age -> System.out.format("나이 %d살은 불가능", age));
스트림 내부 반복을 실행하는 forEach 메소드를 사용한 모습이다.
'Java' 카테고리의 다른 글
[Java] TCP 네트워킹 - (4)스레드 병렬 처리 (0) | 2022.06.06 |
---|---|
[Java] IO 기반 입출력 - (1)입력 스트림과 출력 스트림 (0) | 2022.06.05 |
BufferedReader / BufferWriter (0) | 2022.04.01 |
[Java] 스트림(stream) (0) | 2021.08.07 |
[Java]Java.lang 패키지 (Object, System, Class, String, String, Pattern, Arrays, Math, Wrapper) (0) | 2021.08.04 |