Java8 부터 Java도 lamda
표현식을 사용할수 있게 되었습니다.
Java lamda
public class RunnableImpl implements Runnable{
int num = 0;
@Override
public void run() {
num++;
System.out.println("num : " + num);
}
}
Runnable r = new RunnableImpl();
Runnable r2 = new Runnable() {
@Override
public void run() {
System.out.println("run r2");
}
};
새로운 Runnable 객체를 생성하는 예제입니다.
Runnable
interface 를 구현한 방식과 익명 클래스 방식으로 구현해 보았습니다.
lamda
표현식의 특징은 클래스명이나 method 명을 알고있을 필요가 없다는 것입니다.
Runnable ex = () -> {
System.out.println("run")
};
lamda
를 사용하니 훨씬 간결해지고 명확해 졌습니다.
더 간단히 하면 이렇게 표현 할 수 있습니다.
Runnable ex = () -> System.out.println("run");
functional programming
앞에서 간단한 Runnable 예제를 통해서 lamda
를 맛보았습니다.
lamda
표현식을 좀더 알아가기 전에 functional programming
에 대해서 알아보겠습니다.
functional programming
은 함수의 입력만을 의존하여 출력을 만드는 구조로 외부에 상태 변경을 지양하는 패러다임을 말합니다.
- Pure Function
- Annonymous Function
- Higher-order Function(고계함수)
이러한 3가지 조건을 만족하는 것을 functional programming
이라고 말할수 있습니다.
Pure Function
은 side-effect 가 없는 함수로 , 함수의 실행이 외부에 영향을 끼치지 않는 함수를 말합니다.
Annonymous Function
은 이름없는 함수를 정의 할수 없어야 한다는 것 입니다. Higher-order Function
은 함수를 다루는 함수를 뜻합니다. functonal programming
에서는 함수 또한 값으로 여겨집니다. 그렇기 때문에 함수의 인자값과 리턴값이 함수가 될수 있는것을 Higher-order Function
이라고 합니다.
Pure Function
pure method
는 입력에 의해서만 출력이 결정되지만 nonPure method
는 oper
라는 외부 변수에 의해서 출력결과가 달라집니다. pure method
가 Pure Function이라고 할수 있습니다.
int oper = 2;
public int pure(int num){
return num * num;
}
public int nonPure(int num){
return num * num * oper;
}
Annonymous Function
lamda
를 사용해 함수 이름없이 사용할수 있습니다.
(int a, int b) -> return a * b;
Higher-order Function
map 함수의 parameter는 함수입니다. 이러한 특징이 Higher-order Function
입니다.
Stream.of(1,2,3)
.map((int a) -> return a + 1);
Functional Interface
Java 에서는 functional programming
을 구현하기 위해 Functional Inteface
개념을 도입하였습니다.
- 선언 method 가 하나인 Interface
- @FunctionalInterface
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
Java의 대표적인 @FunctionalInterface
입니다. lamda
표현식이 사용가능한 interface 입니다.
참고로 default
method는 여러개가 있어도 상관없습니다.
@FunctionalInterface
는 명시적인 것으로 이 어노테이션이 없어도 FunctionalInterface
조건을 만족한다면 lamda
표현식 사용이 가능합니다.
어노테이션을 추가해 놓고 abstract
method가 2개 이상이 될시에는 컴파일 에러가 발생합니다. FunctionalInterface
를 구현할 일이 있다면 어노테이션 사용이 유용해 보입니다.
FunctionInterface api
Java8에서 제공하는 몇가지 FunctionalInterface
api를 알아보겠습니다.
Functoin<T, R>
Function api 를 이용해서 입력받은 Integer
값을 String
type으로 변환해 보겠습니다.
static class FunctionImpl implements Function<Integer, String>{
@Override
public String apply(Integer intValue) {
return String.valueOf(intValue);
}
}
Function<Integer, String> f = FunctionImpl();
String result = f.apply(10);
Function<Integer, String> f2 = new Function<Integer, String>(){
@Override
public String apply(Integer t) {
return String.valueOf(t);
}
};
String result = f2.apply(100);
Function<Integer, String> f3 = (Integer i) -> {
return String.valueOf(i);
};
String result = f3.apply(100);
Function<Integer, String> f4 = i -> String.valueOf(i);
String result = f4.apply(100);
클래스 참조, 익명함수 사용 방법보다 lamda
가 얼마나 간결한지 눈에 확연히 들어옵니다.
BiFunctoin<T, U, R>
T, U 타입을 입력받아 R 타입으로 변환해주는 functional interface
입니다.
간단히 2 개의 Long
타입을 입력받고 더한 합을 String
type 으로 변환해보겠습니다.
BiFunction<Long, Long, String> bf = (v1, v2) -> "[" + (v1 + v2) + "]";
String result = bf.apply(10L , 30L);
기타
Function
이외에도 Predicate
, Consumer
, BiConsumer
, Supplier
등의 FunctionalInterface가 존재합니다.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u);
}
@FunctionalInterface
public interface Supplier<T> {
T get();
}
활용
lamda
표현식을 stream api로 활용해 보겠습니다.
Stream api
- Stream 생성
- 중개 연산 (filter, map, flatmap …)
- 종단 연산 (reduct, collect, forEach …)
Stream api의 구조는 3단계로 구성합니다.
IntStream.of(1, 3, 5, 4, 10, 16, 2)
.filter(s -> s > 4)
.sum();
Stream api에서 filter method는 IntPredicate
FuncctionalInterface 를 parameter 로 표현합니다.
lamda 식을 쓰지 않는다면 아래와 같이 표현할수 있습니다.
IntStream.of(1, 3, 5, 4, 10, 16, 2)
.filter(new IntPredicate() {
@Override
public boolean test(int value) {
boolean result = false;
if(value > 4){
result = true;
}
return result;
}
})
.sum()
정리
함수형 프로그래밍은 개발 생산성과 재사용성, 테스트, 간결한 분석등 여러가지 장점이 있다고 합니다.
이러한 장점은 둘째치고서라도 일단 코드의 양이 줄어 간결해진것만으로도 큰 장점이라고 생각합니다.
시간이 된다면 scala, haskell 함수형 언어에 대해서 공부해보고 포스팅 해보겠습니다.
참조
- https://skyoo2003.github.io/post/2016/11/09/java8-lambda-expression
- [https://ko.wikipedia.org/wiki/%ED%95%A8%EC%88%98%ED%98%95%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D](https://ko.wikipedia.org/wiki/%ED%95%A8%EC%88%98%ED%98%95%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D)
- https://skyoo2003.github.io/post/2016/11/09/java8-lambda-expression