Day16

피오·2021년 11월 22일
0
post-thumbnail

ToDo

  • 자바 GUI 공부(AWT)(O)
  • 람다 공부(O)
  • 스트림 공부(X)



1. 람다

자바는 OOP언어이지만 jdk 1.8이후로 함수형 언어의 기능을 추가했다.



1.1 람다식

  • 람다식이란 함수(메서드)를 간단한 식(expression)으로 표현하는 방법이다.


1.2 람다식 작성하는 법

  • 메서드의 이름과 반환타입을 제거하고 '->'를 {} 앞에 추가한다.
int max(int a, int b) {
  return a > b ? a : b;
}

(a, b) -> a > b ? a : b
  • 반환값이 있는 경우엔 return 생략 가능
  • 매개변수 타입이 추론 가능하면 생략 가능
(int a, int b) -> a > b ? a : b
=> (a, b) -> a > b ? a : b

작성시 주의사항

  1. 매개변수가 하나일 때 괄호() 생략 가능
(a) -> a * a
=> a -> a * a
  1. 블록 안의 문장이 하나일 때, 중괄호 생략 가능(; 안붙임)
(int i) -> {
    System.out.println(i);
}
=> (int i) -> System.out.println(i)

🖐 중괄호 내의 한줄짜리 문장이 return문이라면 중괄호 생략 불가

⭕️
(int i, int b) -> {
    return a + b;
}(int i, int b) -> return a + b 

but 위에서 반환값이 있으면 return문을 생략할 수 있다고 했다. 이는 즉 return을 붙일 거면 중괄호도 붙이라는 거고, return을 생략할 거면 중괄호도 생략할 수 있다는 말이다. 그러면 위 코드에서 아래문장은 이렇게 바꿔 써도 된다.

(int i, int b) -> a + b //매개변수 타입도 생략가능


1.3 람다식은 익명 객체

  • 람다식은 익명 함수가 아니라 익명 객체이다.

자바의 언어적 특성상 함수만 독립적으로 존재할 수 없고, 클래스 내부의 메서드로만 존재할 수 있다. 때문에 람다식은 익명 클래스의(선언과 동시에 생성되는 클래스) 객체와 동등하다.

뭐가 되었든 람다식도 메서드이므로 참조변수가 있어야 람다식을 호출할 수 있는데, 자바에서는 이 참조변수의 타입을 함수형 인터페이스로 받기로 했다.



1.4 함수형 인터페이스

  • 함수형 인터페이스란 단 하나의 추상 메서드만 선언된 인터페이스이다.
  • @FunctionalInterface 키워드를 붙이면 컴파일러가 함수형 인터페이스를 올바르게 작성했는지 체크해준다.
  • 람다식은 함수형 인터페이스 타입의 참조변수로 다룰 수 있다. 즉 함수형 인터페이스는 람다식을 다루기 위해 사용하는 것.
//max라는 이름의 추상 메서드 하나만 선언되어있는 함수형 인터페이스
@FunctionalInterface
interface MyFunction {
  public abstract int max(int a, int b); 
}

//익명 클래스로 람다식을 구현했고, 참조변수의 타입은 MyFunction이라는 이름의 함수형 인터페이스 타입
MyFunction f = new MyFunction {
  public int max(int a, int b) {
    return a > b ? a : b;
  }
}

int value = f.max(3,5); //5

1.4.1 java.util.function 패키지

이 패키지에는 FunctionalInterface들이 선언되어있다. 함수형 인터페이스를 매개변수로 받는 메서드를 사용할 때 매번 사용자가 함수형 인터페이스를 정의해서 사용할 필요 없이 자주 쓰이는 기능을 담은 함수형 인터페이스들을 가져다 쓰면 되도록 자바에서 제공해주는 패키지이다.

참고로 패키지 내에 있는 인터페이스를 보면 추상 메서드 단 하나만 선언되어 있지 않다는 걸 확인할 수 있다. 원래라면 인터페이스에 추상 메서드만 선언할 수 있었지만, 자바 1.8버전부터 인터페이스에 디폴트 메서드와 static 메서드를 선언하는 게 가능해졌다.

이 디폴트 메서드들을 이용하여 두 개 이상의 람다식을 이어붙이거나 하는 등의 다양한 작업을 수행할 수 있다.



1.4.2 컬렉션 프레임워크와 함수형 인터페이스

java.util.function 패키지에는 Predicate라는 함수형 인터페이스가 있다. 이 인터페이스는 조건식을 람다식으로 표현하는 데 사용된다.

사진을 보면 Predicate 인터페이스에는 test라는 추상 메서드가 선언되어 있는데, 이를 다음과 같은 코드로 사용할 수 있다.

Predicate<String> isEmptyStr = s -> s.length() == 0;
String s = "";

if (isEmptyStr.test(s)) {
  System.out.println("빈 문자열입니다.");
}

위처럼 Predicateboolean값을 리턴하기 위한 조건식을 람다식으로 구현할 수 있는 틀을 제공해준다.

그런데 이러한 함수형 인터페이스를 컬렉션 프레임워크도 사용하고 있는데, 용례는 다음과 같다.

사진은 Collection클래스의 removeIf메서드 코드를 캡처한 것이다.
removeIf메서드가 인자로 Predicate를 구현한 객체를 받는 것을 볼 수 있다. 우리가 작성한 람다식을 갖고 있는 Predicate타입 익명 객체가 인자로 넘어가면 그 조건식에 따라 true를 일치하면 요소를 삭제하는 코드이다.

이 외에도 Iterable인터페이스의 forEach(Consumer<T> action)등 다양한 메서드에서 람다식을 매개변수로 받고 있으니 사용중인 메서드가 어떤 함수형 인터페이스 타입을 받고 있는지 코드도 확인해보면 좋을 것 같다.



1.4.3 메서드 참조

  • 메서드 참조는 람다식을 더 간결하게 표현할 수 있는 방식이다.
  • 람다식이 하나의 메서드만 호출하는 경우에 사용할 수 있다.
  • 하나의 메서드만 호출하는 람다식은 '클래스이름::메서드이름' 또는 '참조변수::메서드이름'으로 변경 가능.
import java.util.function.Function;

public class MethodReferenceEx {

  public static void main(String[] args) {
    // 3. 원래 람다식은 s -> new MyClass(s) 인데,
    // Function<String, MyClass>를 통해 람다식의 매개변수의 타입과 리턴타입을 알 수 있으니 매개변수를 생략하고,
    // '클래스명::메서드명' 으로 변경해줄 수 있다. 이게 메서드 참조를 사용하는 방식.
    MyClass myClass = makeMyClass(MyClass::new); // = makeMyClass(s -> new MyClass(s));
    System.out.println("name is: " + myClass.getName()); // name is: method reference

  }


  // 1. makeMyClass란 메서드에 매개변수로 Function인터페이스 타입의 람다식을 받겠다고 작성했다.
  // 이 람다식은 String타입 매개변수를 받고, MyClass타입을 리턴한다.
  private static MyClass makeMyClass(Function<String, MyClass> lambda) {
    // 2. 람다식에 "method reference"이란 String 값을 인자로 전달한다.
    String name = "method reference";
    return lambda.apply(name);
  }
}


public class MyClass {

  String name;

  public MyClass(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }
}



참고

profile
블로그 이전했습니다. https://pzbg.tistory.com/

0개의 댓글