-
Lambda기술 면접/Java 2022. 8. 18. 21:08
람다식이란?
람다 식이란 Java8에 새로 추가 됐고 함수형 프로그래밍을 위한 첫 단추가 됐다.
람다식을 사용하는 함수를 람다 함수라 한다. 람다 함수는 익명 함수를 지칭하는 용어이며 함수를 보다 단순하게 표현하기 위해 사용된다.
람다식을 사용하면 메소드가 어느 특정 클래스에 선언하지 않으며 기능을 수행할 수 있다. 또한 객체처럼 전달되어 요청시 실행할 수 있다.
람다 표현식
기본식
(매개변수, ...) -> { 실행문 ... }
(매개변수, ...)는 오른쪽 중괄호 { } 블록을 실행하기 위해 필요한 값을 제공하는 역할을 한다. 매개 변수의 이름은 개발자가 자유롭게 지정할 수 있으며 인자타입도 명시하지 않아도 된다.
-> 기호는 매개 변수를 이용해서 중괄호 { } 바디를 실행한다는 뜻으로 해석하면 된다.
람다식의 종류
//정상적인 유형 () -> {} () -> 1 () -> { return 1; } (int x) -> x+1 (x) -> x+1 x -> x+1 (int x) -> { return x+1; } x -> { return x+1; } (int x, int y) -> x+y (x, y) -> x+y (x, y) -> { return x+y; } (String lam) -> lam.length() lam -> lam.length() (Thread lamT) -> { lamT.start(); } lamT -> { lamT.start(); } //잘못된 유형 선언된 type과 선언되지 않은 type을 같이 사용 할 수 없다. (x, int y) -> x+y (x, final y) -> x+y
- 함수몸체 ({ 실행문 ... }) 가 단일 실행문이면 괄호{}를 생략 할 수 있다.
- 함수몸체가 return문으로만 구성되어 있는 경우 괄호{}를 생략 할 수 없다.
람다식 예제
함수형 인터페이스
함수형 인터페이스는 구현해야 할 추상 메소드가 하나만 정의된 인터페이스를 가리킨다.
람다 식은 메서드 매개 변수(Calc)의 매개 변수 타입(int first, int second)과 맞춰야 한다.
함수형 인터페이스 구현에 람다식을 사용하기 전에는 익명 클래스(java 1.1)를 사용했다.
함수형 인터페이스
@FunctionalInterface interface Math { public int Calc(int first, int second); }
추상 메소드 구현 및 사용
Math plusLambda = (first, second) -> first + second; System.out.println(plusLambda.Calc(4, 2)); Math minusLambda = (first, second) -> first - second; System.out.println(minusLambda.Calc(4, 2));
익명 클래스
Math plusLambda = (first, second) -> first + second; Math plusLambda = new Math() { @Override public int Calc(int first, int second) { return first + second; } }; System.out.println(plusLambda.Calc(4, 2)); // Math minusLambda = (first, second) -> first - second; Math minusLambda = new Math() { @Override public int Calc(int first, int second) { return first - second; } }; System.out.println(minusLambda.Calc(4, 2));
Event Listener/Callback
람다는 특히 간단한 이벤트 리스너 콜백 함수로 많이 사용된다.
함수형 인터페이스
public interface OnClickListener { void onClick(View var1); }
버튼이 눌렸을 때 실행 되는 onClick 함수를 포함한 onClickListener 인터페이스
인터페이스 포함 클래스
public void setOnClickListener(@Nullable View.OnClickListener l) { throw new RuntimeException("Stub!"); }
OnClickListener 추상 메소드를 매개변수로 전달
추상 메소드 구현 및 사용
btn_signUp.setOnClickListener(view -> { Intent intent = new Intent(StartActivity.this, SignUpActivity.class); startActivity(intent); finish(); });
익명 클래스
btn_signUp.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(StartActivity.this, SignUpActivity.class); startActivity(intent); finish(); } });
java.util.function
Java에서 지원하는 함수형 인터페이스
IntFunction<R>
int 값의 인수를 받아들이고 결과를 생성하는 함수를 나타낸다.
추상메소드 구현 및 사용
IntFunction intSum = (x) -> x+1; System.out.println(intSum.apply(1));
익명 클래스
IntFunction intSum = new IntFunction() { @Override public Object apply(int value) { return value + 1; } }; System.out.println(intSum.apply(1));
BinaryOperator<T>
같은 타입의 두 피연산자에 대한 연산을 나타내며 피연산자와 같은 타입의 결과를 생성한다.
추상메소드 구현 및 사용
BinaryOperator stringSum=(x, y)->x+" "+y; System.out.println(stringSum.apply("Hello","world"));
익명 클래스
BinaryOperator stringSum= new BinaryOperator() { @Override public Object apply(Object o, Object o2) { return o+" "+o2; } }; System.out.println(stringSum.apply("Hello","world"));
실행 결과
Hello world
Stream API
Stream이란 다양한 데이터를 표준화된 방법으로 다루기 위한 라이브러리이다. 자바 8부터 추가 됐다.
기본 형식
example.stream().filter(x -> x < 2).count
stream() <- 스트림생성
filter <- 중간 연산 (스트림 변환) - 연속에서 수행 가능
count <- 최종 연산 (스트림 사용) - 마지막에 단 한 번만 사용 가능
예제
IntStream.range(1, 11 ) .filter(i-> i%2==0) .forEach(System.out::println);
익명 클래스
IntStream.range(1, 11).filter(new IntPredicate() { @Override public boolean test(int value) { return (value&2) == 0; } }).forEach(System.out::println);
람다식 과 익명 인터페이스 구현의 차이점?
람다식(feat. 익명 구현 클래스 vs 람다식) (tistory.com)
Difference Between Anonymous Inner Class And Lambda Expression In Java 8 | Programmerbay
오버라이드
함수형 인터페이스
@FunctionalInterface public interface MyEventConsumer { public void consume(Object event); }
람다식
MyEventConsumer consumer = event -> System.out.println(event.toString() + " consumed");
익명 클래스
MyEventConsumer consumer = new MyEventConsumer() { @Override public void consume(Object event){ System.out.println(event.toString() + " consumed"); } };
익명 인터페이스에서는 람다식으로 표현할 함수를 오버라이드 하기 때문에 재설계가 가능하다.
재설계
MyEventConsumer consumer = new MyEventConsumer() { private int eventCount = 0; **@Override** public void consume(Object event){ System.out.println(event.toString() + " consumed " + this.eventCount++ + " times."); } };
내부 작동 방식
익명 클래스
public class AnonymousClass { public static void main(String[] args) { IntBinaryOperator plus = new IntBinaryOperator() { @Override public int applyAsInt(int left, int right) { return left + right; } }; } }
바이트 코드
public class com/whiteship/white_ship_study/week15/AnonymousClass { **static INNERCLASS com/whiteship/white_ship_study/week15/AnonymousClass$1 null null** public static main([Ljava/lang/String;)V L0 LINENUMBER 8 L0 NEW com/whiteship/white_ship_study/week15/AnonymousClass$1 DUP **INVOKESPECIAL com/whiteship/white_ship_study/week15/AnonymousClass$1.<init> ()V** ASTORE 1 L1 LINENUMBER 14 L1 RETURN L2 LOCALVARIABLE args [Ljava/lang/String; L0 L2 0 LOCALVARIABLE plus Ljava/util/function/IntBinaryOperator; L1 L2 1 MAXSTACK = 2 MAXLOCALS = 2 }
람다
public class LambdaEx { private IntBinaryOperator plus() { IntBinaryOperator plus = (x, y) -> { return x + y; }; return plus; } }
바이트 코드
// class version 52.0 (52) // access flags 0x21 public class com/whiteship/white_ship_study/week15/NotAnonymous { // compiled from: NotAnonymous.java // access flags 0x19 public final static INNERCLASS java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup // access flags 0x2 private plus()Ljava/util/function/IntBinaryOperator; L0 LINENUMBER 12 L0 **INVOKEDYNAMIC applyAsInt()Ljava/util/function/IntBinaryOperator;** [ // handle kind 0x6 : INVOKESTATIC java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; // arguments: (II)I, // handle kind 0x6 : INVOKESTATIC com/whiteship/white_ship_study/week15/NotAnonymous.lambda$plus$0(II)I, (II)I ] ASTORE 1 L1 LINENUMBER 15 L1 ALOAD 1 ARETURN L2 LOCALVARIABLE this Lcom/whiteship/white_ship_study/week15/NotAnonymous; L0 L2 0 LOCALVARIABLE plus Ljava/util/function/IntBinaryOperator; L1 L2 1 MAXSTACK = 1 MAXLOCALS = 2 **// access flags 0x100A private static synthetic lambda$plus$0(II)I // parameter synthetic x // parameter synthetic y L0 LINENUMBER 13 L0 ILOAD 0 ILOAD 1 IADD IRETURN L1 LOCALVARIABLE x I L0 L1 0 LOCALVARIABLE y I L0 L1 1 MAXSTACK = 2 MAXLOCALS = 2 }**
결론 - 익명 내부 클래스는 새로운 클래스를 생성하지만, 람다는 따로 클래스를 생성하지 않는다. 대신 새로운 메소드를 만든다.
람다의 장단점
장점
1. 코드의 간결성 - 람다를 사용하면 불필요한 반복문의 삭제가 가능하며 복잡한 식을 단순하게 표현할 수 있습니다.
2. 병렬처리 가능 - 멀티쓰레드를 활용하여 병렬처리를 사용 할 수 있습니다.
더보기병렬 처리코드실행 결과
코드
IntStream.range(0, 10).**parallel()**.forEach(index -> { System.out.println("Starting lambda" + Thread.currentThread().getName() + ", index=" + index + ", " + new Date()); try { Thread.sleep(1000); } catch (InterruptedException e) { } });
실행 결과
Starting lambdaForkJoinPool.commonPool-worker-1, index=2, Wed Jul 13 17:46:35 KST 2022 Starting lambdaForkJoinPool.commonPool-worker-2, index=8, Wed Jul 13 17:46:35 KST 2022 Starting lambdaForkJoinPool.commonPool-worker-4, index=5, Wed Jul 13 17:46:35 KST 2022 Starting lambdaForkJoinPool.commonPool-worker-6, index=3, Wed Jul 13 17:46:35 KST 2022 Starting lambdaForkJoinPool.commonPool-worker-5, index=4, Wed Jul 13 17:46:35 KST 2022 Starting lambdaForkJoinPool.commonPool-worker-7, index=0, Wed Jul 13 17:46:35 KST 2022 Starting lambdamain, index=6, Wed Jul 13 17:46:35 KST 2022 Starting lambdaForkJoinPool.commonPool-worker-3, index=1, Wed Jul 13 17:46:35 KST 2022 Starting lambdaForkJoinPool.commonPool-worker-7, index=7, Wed Jul 13 17:46:36 KST 2022 Starting lambdaForkJoinPool.commonPool-worker-6, index=9, Wed Jul 13 17:46:36 KST 2022
3. 객체화 - 람다 표현식은 함수형 인터페이스의 객체이므로, 람다 표현식을 하나의 변수에 할당한 후 다른 함수에 넘겨줄 수 있다.
더보기@FunctionalInterface public interface TaskComparator{ public boolean compareTasks(int a1, int a2); } public class TaskComparatorImpl{ public static void main (String[] args){ TaskComparator myTaskComparator = (int a1, int a2) -> {return a1 > a2;}; boolean result = myTaskCompartor.compareTasks(5,2); System.out.println(result); } }
단점
- 불필요하게 너무 사용하게 되면 오히려 가독성을 떨어 뜨릴 수 있습니다.
Ref: https://jenkov.com/tutorials/java/lambda-expressions.html
[JAVA] 람다식(Lambda)의 개념 및 사용법 :: 히진쓰의 서버사이드 기술 블로그 (tistory.com)
'기술 면접 > Java' 카테고리의 다른 글
Java에서 Null을 안전하게 사용하기 (0) 2022.08.14 #1 Garbage Collection (0) 2022.07.06