람다식(Lambda)란?
자바에서 함수형 프로그래밍(Functional Programming)을 구현하는 방식이다.
람다식은 클래스를 생성하지 않고 함수의 호출만으로 기능을 수행하며 함수형 인터페이스를 선언한다.
람다는 프로그래밍 언어에서 사용되는 개념으로 익명 함수(Anonymous functions)를 지칭하는 용어다.
현재 사용되고 있는 람다의 근간은 수학과 기초 컴퓨터과학 분야에서의 람다 대수다. 람다대수는 간단히 말하자면
수학에서 사용하는 함수를 보다 단순하게 표현하는 방법이다.
자바 8부터 지원되는 기능이다.
함수형 프로그래밍(Functional Programming)이란?
함수형 프로그램의 특징은 함수 베이스로 프로그래밍을 하는 것이다.
함수 기반의 프로그래밍을 하는데 매개변수를 받아서 그 매개변수를 이용한 프로그래밍을 하게 되면
외부변수들을 사용하지 않는다. 그것을 순수 함수형 프로그래밍이라고 한다.
매개변수만을 사용하도록 만든 함수로 외부자료에 부수적인 영향(Side Effect)가 발생하지 않도록 한다.
함수가 수행됨으로써 다른 변수값이 변한다거나 하는 경우가 발생하지 않기 때문에 병렬처리가 가능하다.
여러가지 효율성이 대두되면서 함수형 프로그래밍 방식이 많이 뜨고 있다고 한다.
특징
익명함수이므로 이름을 가질 필요가 없다.
두개 이상의 입력이 있는 함수는 최종적으로 1개의 입력만 받는 람다 대수로 단순화 될 수 있다.
익명함수(Anonymous Functions)란?
말 그대로 이름이 없는 함수다.
익명함수들은 공통으로 일급객체(First Class citizen)라는 특징을 갖고 있다.
일급 객체란 일반적으로 다른 객체들에 적용 가능한 연산을 모두 지원하는 개체를 가리킨다.
함수를 값으로 사용할 수도 있으며 Parameter로 전달 및 변수에 대입하기와 같은 연산들이 가능하다.
장단점
장점
람다를 사용하면 불필요한 반복문의 삭제가 가능하며 복잡한 식을 단순하게 표현하여 코드량이 줄어든다.
람다는 지연연상을 수행함으로써 불필요한 연산을 최소화한다.
멀티스레드를 활용하여 병렬처리를 사용할 수 있다.
단점
람다는 호출이 까다롭다.
람다 stream 사용 시 단순 for문 혹은 while문 사용 시 성능이 떨어진다.
불필요하게 너무 사용하게 되면 오히려 가독성을 떨어뜨릴 수 있다.
람다식 문법
매개변수가 하나인 경우 괄호 생략이 가능하지만 두개인 경우는 괄호를 생략할 수 없다.
str->{ System.out.println(str); }
중괄호 안의 구현부가 한 문장인 경우는 중괄호를 생략할 수 있다.
str->System.out.println(str);
중괄호 안의 구현부가 한 문장이라도 return 문이라면 중괄호를 생략할 수 없다.
str->return str.length(); //오류발생
중괄호 안의 구현부가 반환문의 하나라면 return 과 중괄호를 모두 생략할 수 있다.
(x, y) -> x + y //두 값을 더하여 반환
str -> str.length(); //문자열 길이를 반환
메서드 선언은 interface를 사용한다.
프로그램내에서 변수는
자료형에 기반하여 선언하고 int a;
매개변수로 전달하고 int add(int x, int y);
메서드의 반환값으로 사용한다. return num;
람다식은 프로그램내에서 변수처럼 사용할 수 있다.
예제코드
//Interface
public interface PrintString {
void show(String str);
}
//Test class
public class TestLambda {
public static void main(String[] args) {
PrintString lambdastr = s-> System.out.println(s);
lambdastr.show("hi");
showMyString(lambdastr);
PrintString test = returnString();
test.show("test");
}
public static void showMyString(PrintString p) {
p.show("Hello");
}
public static PrintString returnString() {
return s-> System.out.println(s + "!!!");
}
}
결과값은
hi
Hello
test!!!
이렇게 출력된다.
lambdastr.show라는 것은 lambdastr에서 구현되어있는 출력문에 s를 넣어서 넘겨준다는 것인데
결국에는 PrintString.show에 lambdastr이라는 함수가 생겨나면서 그 매개변수로 "hi"가 넘어가고
그럼 함수에 출력문이 구현되어있으니까 출력이 되는 형태다.
showMyString에서는 PrintString으로 받는다. 그럼 PrintString타입인 lambdastr을 매개변수로 넘겨준다면
showMyString에서는 p.show가 바로 위에 있는 코드와 마찬가지로 lambdastr.show("Hello"); 형태로
처리하는 것이다.
returnString의 경우는 함수의 구현부가 마치 변수처럼 returnString으로 반환이 되어 대입이 되고
그 메서드가 호출 될 수 있다.
test.show로 test를 넘겨줬고 returnString에서는 그럼 test를 s로 받아 s + !!! 의 형태인
test!!!를 출력하게 된다.
함수형 인터페이스 @FuntionalInterface
FunctionalInterface는 일반적으로 구현해야할 추상 메서드가 하나만 정의된 인터페이스를 가리킨다.
자바 컴파일러는 이렇게 명시된 함수형 인터페이스에 두개이상의 메서드가 선언되면 오류를 발생시킨다.
@FunctionalInterface
public interface StringConcat {
public void makeString(String s1, String s2);
}
//구현해야할 메서드가 두개이므로 Functional Interface가 아니기 때문에 오류발생
@FunctionalInterface
public interface StringConcat {
public void makeString(String s1, String s2);
public void make(String s1, String s2);
}
예제코드
@FunctionalInterface
public interface MyMaxNumber {
int getMaxNumber(int x, int y);
}
//Test class
public class TestMyMaxNumber {
public static void main(String[] args) {
MyMaxNumber max = (x, y)->(x >= y) ? x : y;
System.out.println(max.getMaxNumber(10, 20));
}
}
20이 결과값으로 출력된다.
x, y 두개의 매개변수가 넘어올건데 인터페이스의 메서드가 호출되면 익명이고 max라는 이름으로 구현된 것은
x와 y 중에 더 큰값을 반환하라고 했다.
반환되는 것이지만 return을 붙이면 중괄호가 있어야 하기 때문에 그냥 사용한 코드다.
만약 return을 붙여준다면
MyMaxNumber max = (x, y)->{return (x >= y) ? x : y; };
이렇게 작성하면 된다.
예제코드2
@FunctinalInterface
public interface StringConcat {
public void makeString(String s1, String s2);
}
//impl class
public class StringConImpl implements StringConcat {
@Override
public void makeString(String s1, String s2) {
System.out.println(s1 + ", " + s2);
}
}
//Test class
public class TestStringConcat {
public static void main(String[] args) {
StringConImpl impl = new StringConImpl();
impl.makeString("hello", "world");
StringConcat concat = (s, v)-> System.out.println(s + ", " + v);
concat.makeString("Hello", "World");
StringConcat concat2 = new StringConcat() {
@Override
public void makeString(String s1, String s2) {
System.out.println(s1 + ", " + s2);
}
};
concat2.makeString("hello, ", "world, ");
}
}
결과값은
hello, world
Hello, World
hello, , world,
이렇게 출력된다.
제일 처음 StringConImpl을 사용한 방법은 람다를 사용하지 않았을 때 원래 사용하던 인터페이스 방식이다.
concat의 코드를 보면 Impl 클래스를 거치지 않고 인터페이스 타입으로 바로 처리한다.
s와 v로 매개변수를 받고 받아왔다면 출력문에 s , v 형태로 출력하도록 한것이다.
그래서 Hello, World가 출력되게 되는 형태다.
마지막 concat2의 경우는 concat이 처리되는 방법이라고 볼 수 있다.
람다식으로 구현했지만 실행되면서 내부적으로 익명객체가 자동으로 생성되면서 처리되는 것이다.
이렇게 람다식을 사용하면 좀 더 간결한 코드로 처리가 가능하다.
여기까지는 기초적인 사용에 대한 것인것 같고 좀 더 깊이 들어가서 익숙해진다면 오히려 더 편하게 사용할 수 있지
않을까 싶다.
이건 시간을 갖고 이것저것 해보면서 더 공부가 필요할 것 같다.
레퍼런스
● 패스트캠퍼스 올인원 패키지 - 자바 객체지향프로그래밍
'JAVA' 카테고리의 다른 글
입출력스트림(IOStream) (0) | 2021.02.12 |
---|---|
스트림(Stream) (0) | 2021.02.11 |
제네릭프로그래밍(Generic Programming) (0) | 2021.02.09 |
컬렉션 프레임워크(Collection Framework) (0) | 2021.02.08 |
예외처리(Exception) (0) | 2021.02.07 |