유래
λ-calculus(람다대수)에서 유래했다. 어것은 1930년경 논리학등에 사용하기 위해 제안된 수학적 형식인데, 예를 들면 아래와 같다. 의미는 "매개변수를 받아 1을 더해서 반환한다" 이다.
λx.x+1
'λ'다음에 매개변수 'x'가 선언되고, '.'뒤에 함수의 내용이 정의된다. 이 형식은 2가지 중요한 의미를 가진다. 하나는 '함수를 정의' 한다는 것이고 다른 하나는 함수의 이름이 없다는 것이다. 따라서 λ는 Anonymous function을 의미한다.
왜 λ(람다)를 쓰게 되었을까?
현대의 프로그래밍 언어는 대부분 객체지향이거나 적어도 이 방식에 근거를 한다. 대표적인 언어가 JAVA인데, 객체지향적인 언어의 규약이 가끔은 비효율적인 경우가 있다. λ(람다)는 이를 해결하기 위해 도입이 되었다. 단순히 코드의 길이를 단축하는 것 만은 아니다.
아래는 λ(람다)함수와 클래스의 주요한 차이점이다. 이 것은 좋다-나쁘다로 보기보다는 하나의 특성으로 이해하면 되겠다. JAVA에서 '~Helper' 혹은 '~Util' 클래스에 static 함수를 선언하고 사용하는 것과 유사하다.
λ(람다) 함수 |
클래스 |
|
|
JAVA에서 λ(람다)를 쓸 수 있는 경우
SAM(Single Abstract Method)인 경우 λ(람다)를 이용할 수 있다. 예를 들면 'Runnable'같은 인터페이스를 SAM이라 한다. 즉, 하나의 함수를 갖는 인터페이스를 말한다. 이 SAM은 상속받은 구현객체 혹은 Anonymous class를 통해 사용될 수 있고, λ(람다)를 이용한 함수로 사용할 수 있다.
interface Runnable
{
public void run();
}
...
// anonymous class
Runnable r = new Runnable() {
public void run() {
...
}
};
// 람다.
Runnable r2 = () -> { ... };
'FunctionalInterface' 표식을 사용할 수도 있다. 이 것은 상속등의 수정작업이 있을 때, 컴파일 단계에서 실수를 막아준다. 아래는 앞으로 예로 사용할 인터페이스 선언이다.
@FunctionalInterface
interface Calculator
{
public int calc(int p1, int p2);
}
@FunctionalInterface
interface Calculator2
{
public int calc(int p1);
}
@FunctionalInterface
interface Calculator3
{
public void calc();
}
JAVA, λ(람다)함수의 기본형식
맨 위에 예를 든 λ(람다)의 형식 λx.x+1은 JAVA에서 표현하면 (x)->x + 1 로 된다.
- λ => ()
- . => ->
이 기본형식을 가지고 앞서 예를 든 SAM들에 대한 λ(람다)식을 나열해 본다. 나열된 표현은 모두 유효하다.
Calculator::calc
- (int x, int y) -> { return x + y; }
- (x, y) -> { return x + y; } // 데이터형은 암묵적으로 생략 가능하다.
- (x, y) -> x + y; // 예약어 'return' 생략 가능하며, '{}'도 단문이면 생략 가능하다.
Calculator2::calc
- x -> x * 2; // 매개변수가 하나일 때, '()'를 생략할 수 있다.
Calculator3::calc
- () -> System.out.println("Hello"); // 매개변수가 없을 때
λ(람다)함수의 고급 사용 - static method, 생성자 참조
@FunctionalInterface
interface Parser
{
public int parse(String s);
}
Parser p1 = (s) -> Integer.parseInt(s);
Parser p2 = Integer::parseInt
@FunctionalInterface
interface Factory
{
public String create();
}
Factory f1 = () -> new String();
Factory f2 = String::new;
static method와 마찬가지로 생성자에 대해서도 'f2'를 선언할 때와 같이 'class::new' 형식의 식으로 대체 가능하다. 여기서 하가지 의문이 드는데, 매개변수가 다르게 여러개의 생성자 혹은 같은 이름의 static method가 있을 경우에는 어떻게 될까?
아래의 코드가 해답이 될 것이다. 매개변수의 데이터형, 갯수가 모두 일치하는 것이 짝지워진다.
@FunctionalInterface
interface Factory
{
public String create(String maker);
}
class Car
{
public Car() { }
public Car(String maker) { ... }
...
}
Factory f1 = Car::new; // Car(String maker)와 짝지워짐
Car c1 = f1.create("bentz"); // OK
Car c2 = f1.create(); // Error
λ(람다)함수의 고급 사용 - 일반 method 참조
일반 메소드도 참조가 가능한데 인스턴스된 것의 함수인지 아니면 객체에 선언된 것을 사용하는지에 따라 다르다. 그리고 후자는 아직 JAVA에서 정리되지 않은 느낌이다.
String s = "Hello";
Greeting g1 = () -> s.toString();
Greeting g2 = s::toString;
마치며
'Learning' 카테고리의 다른 글
JAVA9 jshell(REPL) (0) | 2018.01.29 |
---|---|
My-SQL 8 RC2 흘끗보기 - JSON (0) | 2017.11.11 |
프로그램언어 GO 흘끗 보기 (0) | 2017.10.23 |
자바스크립트 프레임웤 vue.js 흘끗보기 (0) | 2017.09.30 |
JAVA 9 모듈방식(modularity) 코딩 힐끗보기 (0) | 2017.09.08 |