람다식(Lambda Expression)
람다식
- Java1.8에서 추가된 함수형 프로그래밍 기법이다.
- 객체지향 프로그래밍과 다르게, 비즈니스 로직만을 빠르게 구현하는 특징.
- 객체지향 언어인 Java에서 메소드를 함수처럼 사용하는 형식.
함수적 프로그래밍
- y = f(x) 형태의 함수로 구성된 프로그래밍 기법
- x에 어떤 값을 주면 y가 얻어지는 함수형 기법
- 데이터를 매개값으로 전달하고 결과를 받는 코드들로 구성
- 객체 지향 프로그래밍 보다는 효율적인 경우
- 대용량 데이터의 처리시에 유리
객체를 생성후 처리하는 것보다, 데이터를 바로 처리하는 것이 속도에 유리
- 이벤트 지향 프로그래밍(이벤트가 발생하면 핸들러 함수 실행)에 적합
반복적인 이벤트 처리는 핸들러 객체보다는 핸들러 함수가 적합
- 현대적 프로그래밍 기법
자바에서 람다식을 수용한 이유
- 코드가 매우 간결해진다.
- 컬렉션 요소(대용량 데이터)를 필터링 또는 매핑해서 쉽게 집계할 수 있다.
자바는 람다식을 함수적 인터페이스의 익명 구현 객체로 취급한다.
- 람다식은 매개변수를 가진 코드블록이다. -> 익명 구현 객체
- 어떤 인터페이스를 구현할지는 대입되는 인터페이스에 달려있다.
Runnable runnable = new Runnable(){
public void run(){ ... }
}
new Runnable(){
public void run(){...}
}
함수적 인터페이스
- 하나의 추상 메소드로 선언된 인터페이스만 타켓 타입이 될 수 있다
- @FuntionalInterface 어노테이션
- 하나의 추상 메소드만을 가지는지 컴파일러가 체크하도록 한다.
- 두 개 이상의 추상 메소드가 선언되어 있으면 컴파일 오류가 발생한다.
람다식의 예
배열의 정렬
String [] strings =
{"fast","campus","java","backend","choigo","best","people"};
System.out.println(Arrays.toString(strings));
Arrays.sort(strings);
System.out.println(Arrays.toString(strings));
- Comparator 클래스를 만들고, 객체를 생성하여 전달하는 방식
class MyComparator implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
return o1.substring(1).compareTo(o2.substring(1));
}
}
Arrays.sort(strings, new MyComparator());
System.out.println(Arrays.toString(strings));
- 익명 내부 클래스를 이용할 수 있다.
- 상속하고 싶은 인터페이스든 클래스를 적어준 다음에 블록을 열어서 재정의 해주면 작성할 수 있다.
Arrays.sort(strings, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.substring(2).compareTo(o2.substring(2));
}
);
System.out.println(Arrays.toString(strings));
- 람다식을 이용한 방법
- 클래스없이 작성이 가능한 익명내부클래스를 한번 더 줄임
- 람다식을 이용하는게 가장 간단하고 하기 편함
- sort를 보면 하나만 입력을 받으면 자체를 비교하게 되고 두개를 입력받으면 comparator??
두번째 파라미터는 comparator를 구현한 람다식이라는 것을 알고 있다.
Arrays.sort(strings, (o1, o2) -> o1.substring(3).compareTo(o2.substring(3)));
System.out.println(Arrays.toString(strings));
class Hansol implements Comparable<Hansol>{
String value;
public Hansol(String value) {
this.value = value;
}
@Override
public int compareTo(Hansol o) {
return value.substring(1).compareTo(o.value.substring(1));
}
@Override
public String toString(){
return value;
}
}
Hansol[] hansols =
{new Hansol("fast"),new Hansol("campus"),new Hansol("backend"),new Hansol("java"),
new Hansol("choigo"),new Hansol("best"),new Hansol("people")};
Arrays.sort(hansols);
System.out.println(Arrays.toString(hansols));
- String 이 상속이 가능했다면 이용한 방법
class Fansol extends String{
@Override
public int compareTo(String o){
}
}
람다식을 사용하기 위한 조건
- 위의 코드들은 Comparator 인터페이스를 구현 했는데 이번엔 직접 작성한 것이다.
- 람다식으로 사용하기 위해서는 단 하나의 추상 메소드를 가지고 있어야 한다.
- 추상메소드가 2개이상이면 오류가 발생한다.
- 디폴트 메소드는 구현이 되어 있더라도 상관이 없다.
@FunctionalInterface
interface Runner<T>{
T run();
default void method(){}
}
public class Main {
static void useRunner(Runner<?> runner){
System.out.println(runner.run());
}
public static void main(String[] args) {
useRunner(() -> "This is how to use runner.");
}
}
다양한 람다식 표현 형식
@FunctionalInterface
interface Runner{
String run(String x);
}
@FunctionalInterface
interface RunnerTwo{
String run();
}
public class Main {
static void useRunner(String x, Runner runner){
System.out.println(runner.run(x));
}
static void useRunnerTwo(RunnerTwo runner){
System.out.println(runner.run());
}
public static void main(String[] args) {
useRunner("안녕하세요!",(String x) -> {return x;});
useRunner("안녕하세요!", x -> {return x;});
useRunnerTwo(() -> {return "안녕";});
useRunner("안녕하세요!",(x)->{
return x;
});
useRunner("안녕",x->x);
}
}
람다식과 익명 클래스 객체가 동일한 것은 아니다라는 것의 증명
- 람다식과 익명 내부 클래스가 비슷한 것이라고 했는데 각각 구현을 해서 this를 출력해보니 결과 값이 다르게 나온다.
- 이유는 익명 내부 클래스로 구현을 하면 익명 내부 클래스의 객체가 생성이 되서 this가 그 객체를 가리키지만,
람다식은 다르게 객체가 만들어지지 않아 Main의 객체가 만들어진다.
- 이 부분만 다르고 완전히 똑같다고 생각하면 된다.
interface IFoo{
String method();
}
public class Main {
static void funtionalMethod(IFoo foo){
System.out.println(foo.method());
}
void methodA(){
funtionalMethod(()->{
System.out.println("this"+ this);
System.out.println("OuterClass.this"+Main.this);
return "Lambad expression used.";
});
funtionalMethod(new IFoo() {
@Override
public String method() {
System.out.println("this"+ this);
System.out.println("OuterClass.this"+Main.this);
return "Anonymous local inner class used.";
}
});
}
public static void main(String[] args) {
new Main().methodA();
}
}