자바 공부를 하다보니 "->" 이처럼 보이는 코드가 나와서 당황 한 적이 있다. 그래서 찾아보니 수학공부를 하며 자주 들어본 "람다"식 이라는 것 까지 알고 넘어갔지만 다시 정확하게 알고 넘어가야 할 것 같아서 이렇게 피드를 남겨보려 한다.
람다함수
람다함수는 프로그래밍 언어에서 사용되는 개념으로 익명 함수(Anonymous function)를 지칭하는 용어입니다.
현재 사용되고 있는 람다의 근간은 수학과 기초 컴퓨터과학 분야에서의 람다 대수이다. 람다 대수는 간단히 말하자면 수학에서 사용하는 함수를 보다 단순하게 표한하는 방법입니다.
한편에서는 람다식이 너무 많은 코드를 생략하다보니 오히려 프로그램의 가독성을 저해시킨다는 의견도 있다고 합니다.
람다의 특징
람다 대수는 이름을 가질 필요가 없다. - 익명 함수 (Anonymous functions)
두 개 이상의 입력이 있는 함수는 최종적으로 1개의 입력만 받는 람다 대수로 단순화 될 수 있다. - 커링 (Curring)
익명함수?
익명함수란 말그대로 함수의 이름이 없는 함수입니다. 익명함수들은 공통으로 일급객체(First Class citizen)라는 특징을 가지고 있습니다.
이 일급 객체란 일반적으로 다를 객체들에 적용 가능한 연산을 모두 지원하는 개체를 가르킵니다. 함수를 값으로 사용 할 수도 있으며 파라메터로 전달 및 변수에 대입 하기와 같은 연산들이 가능합니다.
람다의 장단점
장점
1. 코드의 간결성 - 람다를 사용하면 불필요한 반복문의 삭제가 가능하며 복잡한 식을 단순하게 표현할 수 있습니다.
2. 지연연산 수행 - 람다는 지연연상을 수행 함으로써 불필요한 연산을 최소화 할 수 있습니다.
3. 병렬처리 가능 - 멀티쓰레디를 활용하여 병렬처리를 사용 할 수 있습니다.
단점
1. 람다식의 호출이 까다롭습니다
2. 람다 stream 사용 시 단순 for문 혹은 while문 사용 시 성능이 떨어집니다.
3. 불필요하게 너무 사용하게 되면 오히려 가독성을 떨어 뜨릴 수 있습니다.
람다식 표현법
람다식은 매개변수+실행문으로 구성됩니다. 즉 접근자, 반환형 모두 생략되는 구조 입니다.
모양은 ( ) -> { }; 의 형태로 구성됩니다. 첫 괄호 ( ) 는 해당 interface 함수의 매개변수를 입력하면 됩니다.
그 다음 -> 를 입력하고, { } 중괄호 안에 실행할 코드를 작성하시면 됩니다.
예제
public interface Calculator{
public int cal(int num1, int num2);
}
아래의 람다식에서 사용될 인터페이스 입니다.
1. 기본 사용법( (매개변수 타입) -> { }; )
public static void main(String[] args) {
Calculator cal = (int num1, int num2) -> {return num1 + num2; };
System.out.println(cla.cal(1,2));
}
출력값은 3이고, 람다식에 필요한 기호를 모두 사용한 방법입니다.
2.매개변수 타입 생략( (매개변수) -> { }; )
public static void main(String[] args) {
Calculator car = (num1, num2) -> {return num1 + num2; };
System.out.println(cal.cal(1,2));
}
출력값은 3이고, 매개변수가 1개 이거나 2개 이상의 매개변수의 타입이 모두 같을 때에는 타입을 생략할 수 있습니다.
3.매개변수가 없는 경우( ( ) -> { }; )
public static void main(String[] args) {
Calculator car = () -> {System.out.println("매개변수가 없는 경우"); };
cal.cal();
}
만약 Calculator 인터페이스의 cal() 메소드에 매개변수가 없다면 위와 같이 매개변수를 생략할 수 있습니다.
4.중괄호 생략( ( ) -> ; )
public static void main(String[] args) {
Calculator car = (num1, num2) -> num1 + num2;
System.out.println(cal.cal(1,2));
}
실행할 문장이 1개일 때에는 { } "중괄호"를 생략할 수 있습니다. 이 때 중요한 점은 반환이 필요한 메소드의 경우 return 키워드를 생략해야 한다는 것 입니다.
5.소괄호 중괄호 생략( 매개변수 -> ; )
public static void main(String[] args) {
Calculator cal = num1 -> System.out.println(num1);
cal.cal(1);
}
매개변수가 1개이고 실행할 문장도 1개이면 위와 같이 ( )와 { }를 생략할 수 있습니다.
람다 사용 조건
람다식을 사용하기 위한 인터페이스에는 조건이 있습니다. 바로 구현해야할 인터페이스의 추상 메소드가 단1개 이어야 한다는 조건입니다. 2개 이상인 경우 어떤 메소드를 람다식으로 표현했는지 알 수 없으니 당연하지 않냐구요? 예 사용하는 입장에서는 당연합니다. 사용하는 입장이라는게 무슨 말일까요? 그럼 사용하는 입장의 반대의 경우는 누구일까요? 바로 인터페이스를 설계 또는 작성하는 사람일 것입니다.
람다식으로 사용하기 위한 인터페이스를 만들었고, 잘 사용하고 있었습니다. 하지만 해당 인터페이스를 구현하는 클래스에서 또 다른 기능이 필요하게 되었고 개발자가 해당 인터페이스에 메소드를 추가했습니다. 어? 그런데 사용하는 곳에서 갑자기 에러가 나기 시작합니다. 개발자는 이제서야 해당 인터페이스가 람다식을 위한 단일 추상메소드를 가진 인터페이스였음을 알게 되었습니다.
//Object 객체의 메소드만 인터페이스에 선언되어 있는 경우는 Functional Interface가 아님
public interface NotFunctional {
public boolean equals(Object obj);
}
//Object 객체의 메소드를 제외하고 하나의 추상 메소드만 선언되어 있는 경우는 Functional Interface임
public interface Functional {
public boolean equals(Object obj);
public void execute();
}
//Object객체의 clone 메소드는 public 메소드가 아니기 때문에 Functional Interface의 대상이 됨
public interface Functional {
public Object clone();
}
public interface NotFunctional {
public Object clone();
public void execute();
}
이뿐만이 아닙니다. 람다식을 위한 인터페이스를 작성하기 위해서는 지켜야할 규칙이 더 있습니다. 개발자가 이것을 모두 기억한 상태로 모든 인터페이스들을 검사하며 개발하기에는 너무 어렵습니다. 물론 인터페이스 이름으로 구분하는 것도 좋은 방법입니다. 하지만 이보다 더 확실한 방법이 있습니다.
출처 : https://galid1.tistory.com/509
출처의 출처 : http://wiki.sys4u.co.kr/pages/viewpage.actionpageId=7766426
@FunctionalInterface Annotation
바로 @FunctionalInterface 어노테이션을 사용하는 것 입니다.
위와 같이 람다식으로 표현하기 위해 사용될 interface위에 @FunctionalInterface만 붙혀준다면 컴파일 타임에 알아서 에러를 잡아줍니다. 위의 그림에서 Interface에 메소드를 2개 작성하여 에러가 표시된것을 알 수 있습니다.
추가로 Java에서 지원하는 java.util.function 인터페이스를 알아보겠습니다.
java.util.function Interface는 예제로 2가지만 설명 하겠습니다.
IntFunction<R>
int 값의 인수를 받아들이고 결과를 생성하는 함수를 나타냅니다.
예제
IntFunction intSum = (x) -> x+1;
System.out.println(intSum.apply(1));
실행 결과는 2 입니다.
BinaryOperator<T>
동일한 유형의 두 피연산자에 대한 연산을 나타내며 피연산자와 동일한 유형의 결과를 생성합니다.
예제
BinaryOperator stringSum=(x, y)->x+" "+y;
System.out.println(stringSum.apply("Welcome","jihoon blog"));
실행 결과는 Welcome jihoon blog 입니다.
그외 다양한 Interface 목록
https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
다음 피드에선 Stream API에 대해 알아보겠습니다!!!
출처🎶🎶😊😊