티스토리 뷰
스칼라, 파이썬, 클로저, 자바스크립트 등등 수많은 유행하는 언어들이 함수형 언어이거나 또는 함수형 프로그래밍을 지원할 수 있는 요소들을 가지고 있습니다.
함수형 코딩에 장점을 자바도 더 이상외면할 수 없기에 자바 역시 8에 와서는 드디어 함수적 코딩을 할 수 있는 요소들을 도입하게 되었습니다. (사실 함수라는 가면을쓴 인터페이스 입니다. 아마 이부분은 태생적 한계일거라고 생각합니다. 이하는 의미적 함수로 칭하겠습니다.)
8이 나오기 이전에 트렌드(?) 뒤처져 약간은 외면받았던 자바에 인기를 다시 끌어올릴 만큼 획기적인 변화라고 볼 수 있습니다.
자바에서 사용하는 함수적 표현 기법 즉 람다식에 대해서 알아보겠습니다.
요즘 프로그래밍을 한다면 "람다" 어디 선가 한번쯤은 들어보셨을 겁니다. 사전적 용어를 검색하면 많이 나오는데 복잡합니다.
그런데 프로그래밍에서 말하는 람다란
"람다 == 익명함수"
쉽게 이렇게 생각하시면 됩니다. 결국 이름 없는 함수입니다.
용어가 어려우면 괜히 복잡합니다. 실제 간단한 예제로 사용법에 대해서 알아보겠습니다.
1. Java 1.7 이하
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package com.example.demo; import java.util.concurrent.TimeUnit; public class LamdaTest { public static void main(String[] args) { //10 초를 세다 Runnable r = new Runnable() { @Override public void run() { int i = 0; while(i < 10) { try { TimeUnit.SECONDS.sleep(1); i++; System.out.println(i + " sec"); } catch (InterruptedException e) { e.printStackTrace(); } } } }; r.run(); } } | cs |
1.8 이전에서는 어떤 인터페이스의 행위를 하기 위해서는 그 인터페이스를 구현한 인스턴스가 필요했습니다.
가장 흔하게 접할 수 있는 부분이 Runnable 인터페이스 사용할때 일 것입니다.위에는1.8 이전에 Runnable 을 사용하는 소스입니다.
위에는 10초간 숫자 count를 위한 스레드를 만들기 위해서 Runnable 을 구현하고 있는 익명 클래스를 이용한 예제입니다.
실제 구현 로직은 run이라는 메서드 안에 있고 사실 필요한 건 run이라는메서드만 있으면 되지만 1.8 이전에서는 함수를 선언할 수 없기 때문에(함수와 메서드는 다릅니다.) Runnable 을 구현하고 있는 익명 클래스를 전체를 선언해서 사용하고 있습니다.
2. Java 1.8 이상
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.example.demo; import java.util.concurrent.TimeUnit; public class LamdaTest { public static void main(String[] args) { Runnable r = () -> { int i = 0; while(i < 10) { try { TimeUnit.SECONDS.sleep(1); i++; System.out.println(i + " sec"); } catch (InterruptedException e) { e.printStackTrace(); } } }; r.run(); } } | cs |
차이점이 보이시나요? 간단한 로직이지만 거추장스러우니까 로직 부분을 생략해보면 결국 남는 건 아래와 같은 부분입니다.
1 | Runnable r = () -> {}; | cs |
나누어서 설명해 보면 ( ) 괄호 부분은 함수 파라메터 부분이고 { } 줄 괄호는 함수 body 부분입니다.
위에 부분이 람다식에 기본 형태입니다. 이처럼 익명 클래스에 선언 없이 익명 함수만을 선언해서 기존에 인터페이스를 대체 가능하게 됐습니다.
이해를 돕기 위한 간단한 다른 예를 들면
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package com.example.demo; import java.util.function.Consumer; public class LamdaTest { public static void main(String[] args) { Consumer<String> sayName = (String name) -> { System.out.printf("Hello %s !!", name); }; sayName.accept("무명소졸"); } } | cs |
결과
Hello 무명소졸 !!
Consumer 부분은 우선 잊고 넘어가셔도 됩니다. 지금은 람다식 표현 방법에 집중하시면 됩니다.
다시 본론으로 돌아가서 위에 Runnable 인터페이스가 저런 람다식 표현이 가능하게 된 이유는 1.8에서 추가된 @FunctionalInterface 때문입니다. 실제로 Runnable 인터페이스 안으로 들어가면 아래와 같이 @FunctionalInterface 가 선언돼 있는 것을 확인할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package java.lang; @FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); } | cs |
하지만 @FunctionalInterface 는 모든 인터페이스에서 사용가능한것은 아니고 제약이 하나 있습니다.
그건 인터페이스 안에 하나의 메서드만 존재해야만 됩니다. (사실 하위호환을 위해 어떤 인터페이스경우 default 메서드가 존재하기는 합니다.)
위에 추가 예제에서 사용한 Consumer<T> 인터페이스도 함수형 인터페이스이고 자바 8에서 이미java.util.function 패키지에 자주 쓰일 수 있는 많은 유형의 함수형 인터페이스를 구현해 놓았습니다.
하지만 익명 클래스보다 람다 표현식이나 간단해진것 같지만 어떻게 보면 코딩양이 획기적으로 줄지는 않았습니다.
익명 클래스를 써도 크게 불편하지 않을 수도 있습니다. 하지만 이건 코드량 줄이는 것 이상의 의미가 있습니다.
자바에서도 일급함수를 사용할 수 있게 됐다는 것입니다. 일급함수란 함수를 파라메터, 리턴값등처럼 일급시민으로 사용 가능하다는 이야기 입니다.
이게 무슨의미이고 무슨장점이 있는지는 다음 포스트에 예제를 들어 설명 하겠습니다.
'Java' 카테고리의 다른 글
[자바] 자바에서 Call By Value 인가? Call By Reference? (0) | 2018.01.23 |
---|---|
[자바] 자바8 에서 하위디렉토리 전부 삭제하기 (0) | 2018.01.17 |
[자바] 자바에서 유니크ID, Key 값 만들기 (0) | 2018.01.16 |
[자바] Currying In Java 8 (0) | 2017.12.06 |
[자바] 자바에서 강제로 Out of memory 발생시키기 (0) | 2013.07.29 |
- Total
- Today
- Yesterday