[Java] 람다

Dev_Sanizzang·2023년 5월 20일

자바(JAVA)

목록 보기
4/4

🚪 개요

Optional을 공부하던 중 자바문법인 람다와 스트림 문법이 많이 등장했다.
그런데 람다와 스트림에 대한 문법 이해도가 떨어져 자바 코드를 읽는데 힘이 들었다.
고로 이번 기회에 람다와 스트림에 대해 공부해보고자 한다.

이 글은 남궁성님의 자바의 정석 강의를 기반으로 정리해봤다.

💡 [자바의 정석 - 기초편] ch14-1~4 람다식이란? 람다식 작성하기

람다식(Lambda Expression)

함수형 언어

자바는 원래 OOP 언어이다.
jdk 1.8부터 함수형 언어의 기능을 추가시켰다.
자바는 OOP 언어인 동시에 함수형 언어라고 할 수 있다.
그런데 함수형 언어라고 하기에는 좀 기능이 약한 면이 있고 함수형 기능을 포함했다고 보면 된다.

빅데이터가 뜨면서 엄청난 양의 데이터를 처리하기 위해서 함수형 언어가 필요했고 예전에 함수형 언어를 많이 쓰지 않았는데, 각광을 받고 있다.

Python, JS도 그렇고 함수형의 기능을 가지고 있다.

람다식

  • 함수(메서드)를 간단한 '식(expression)'으로 표현하는 방법
int max(int a, int b) {
	return a > b ? a : b;
}
(a, b) -> a > b ? a : b
  • 익명 함수(이름이 없는 함수)
    메서드를 람다식으로 바꿀 때 반환 타입과 이름을 지운다.

💡 함수와 메서드의 차이?

  • 근본적으로 동일. 함수는 일반적 용어, 메서드는 객체지향개념 용어
  • 자바에서는 모든 메서드는 클래스 안에 있어야 한다.
    -> 함수는 클래스에 독립적, 메서드는 클래스에 종속적

람다식 작성하기

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

여기서 람다식이란 함수(메서드)를 간단히 표현한 것이다.
그래서 조금 더 간단히 하고자하는 규칙들이 있다.

  1. 반환값이 있는 경우, 식이나 값만 적고 return문 생략 가능(끝에 ';' 안 붙임)
(int a, int b) -> {
	return a > b ? a : b
}
(int a, int b) -> a > b ? a : b
  1. 매개변수의 타입이 추론 가능하면 생략가능 (대부분의 경우 생략가능)
(int a, int b) -> a > b ? a : b
(a, b) -> a > b ? a : b

람다식 작성하기 - 주의사항

  1. 매개변수가 하나인 경우, 괄호() 생략가능(타입이 없을 때만)
(a) -> a * a
(int a) -> a * a
a -> a * a // OK
int a -> a * a // 에러
  1. 블록 안의 문장이 하나뿐 일 때, 괄호{} 생략가능(끝에 ';' 안 붙임)
(int i) -> {
	System.out.println(i);
}
(int i) -> System.out.println(i)

단, 하나뿐인 문장이 return문이면 괄호{} 생략불가 (대부분 return 생략해서 가볍게 넘어가자)

(int a, int b) -> { return a > b ? a : b; } // OK
(int a, int b) -> return a > b ? a : b // 에러

람다식은 익명 함수? 익명 객체!

  • 람다식은 익명 함수가 아니라 익명 객체이다.
    -> 자바에서는 메서드만 따로 존재할 수 없기 때문.
(a, b) -> a > b ? a: b
new Object() {
	int max(int a, int b) {
    	return a > b ? a : b;
    }
}

-> 익명객체(객체의 선언과 생성을 동시에)

람다식(익명 객체)을 다루기 위한 참조변수가 필요. 참조변수의 타입은?

new Object() {
	int max(int a, int b) {
    	return a > b ? a : b;
    }
}
타입 obj = (a, b) -> a > b ? a : b; // 어떤 타입?
int value = obj.max(3,5); //에러. Object 클래스에 max()가 없음

함수형 인터페이스

단 하나의 추상 메서드만 선언된 인터페이스

interface MyFunction {
	public abstract int max(int a, int b);
}

인터페이스에 선언된 메서드는 추상메서드이다.

->

MyFunction f = new MyFunction() {
	public int max(int a, int b) {
    	return a > b ? a : b;
    }
};

익명 클래스. 클래스 선언, 객체 생성을 동시에 하는 것.

int value = f.max(3, 5); // OK. MyFunction에 max()가 있다.
  • 함수형 인터페이스 타입의 참조변수로 람다식을 참조할 수 있다.
    (단, 함수형 인터페이스의 메서드와 람다식의 매개변수 개수와 반환타입이 일치해야 함.)
MyFunction f = (a, b) -> a > b ? a : b;
int value = f.max(3, 5); // 실제로는 람다식(익명함수)이 호출됨

함수형 인터페이스 -> 람다식을 다루기 위해서 사용하는 것

@FunctionalInterface // 추상메서드가 2개면 에러가 난다. (함수형 인터페이스는 단 하나의 추상 메서드만 가져야 함.)
interface MyFunction {
	public abstract int max(int a, int b); // public abstract는 생략 가능(interface의 메서드는 모두 public abstract이기 때문)
}

class Main {
//	MyFunction f = new MyFunction() {
//      public int max(int a, int b) { // 오버라이딩 - 접근제어자는 좁게 못바꾼다.
//          return a > b ? a : b;
//      }
//	};

	// 람다식(익명 객체)을 다루기 위한 참조변수의 타입은 함수형 인터페이스로 한다.
	MyFunction2 f = (a, b) -> a > b ? a : b; // 람다식, 익명객체
    
    int value = f.max(3, 5); // 함수형 인터페이스
    System.out.println("value="+value);
}

함수형 인터페이스 - example

  • 익명 객체를 람다식으로 대체
List<String> list = Arrays.asList("abc", "aaa", "bbb", "ddd", "aaa");

Collections.sort(list, new Comparator<String>() {
	public itn compare(String s1, String s2) {
    	return s2.compareTo(sq);
    }
});

sort할 때 Comparator를 넣어줘야 했다.
-> 너무 길다.

List<String> list = Arrays.asList("abc", "aaa", "bbb", "ddd", "aaa");

Collections.sort(list, (s1, s2) -> s2.compareTo(s1));

복잡하게 써야했던 코드를 람다로 간단하게 변경 가능

함수형 인터페이스 타입의 매개변수, 반환타입

  • 함수형 인터페이스 타입의 매개변수
@FunctionalInterface
interface MyFunction {
	void myMethod();
}
void aMethod(MyFunction f) {
	f.myMethod(); // MyFunction에 정의된 메서드 호출
}

매개변수로 람다식을 받겠다는 것.

MyFunction f = () -> System.out.println("myMethod()");
aMethod(f);
aMethod(() -> System.out.println("myMethod()"));
  • 함수형 인터페이스 타입의 반환타입
MyFunction myMethod() {
	MyFunction f = () -> {};
    return f;
}
MyFunction myMethod() {
	return () -> {};
}

java.util.function 패키지

: 자주 사용되는 다양한 함수형 인터페이스를 제공

우리가 사용하는 함수의 종류가 몇개 없다.
미리 그걸 만들어 놓은 것

매번 우리가 만드는 것 보다 자바에서 제공을하고 우리가 그것을 사용하도록 한 것 -> 표준화 된다는 장점

이미 만들어진걸 공통으로 사용하면 이해하기도 쉽고 편리하다.

isEmptyStr.test(s)

매개변수가 2개인 함수형 인터페이스

매개변수가 2개인 인터페이스는 앞에 "Bi"가 붙는다.

입력이 3개가 필요한 인터페이스가 필요하면 아래와 같이 직접 만들면된다.

@FunctionalInterface
interface TriFunction<T, U, V, R> {
	R apply(T t, U u, V v);
}

매개변수의 타입과 반환타입이 일치하는 함수형 인터페이스

  • Unary: 단항
  • Binary: 이항

Predicate의 결합

Predicate 여러개를 하나로 결합할 수 있다.

  • and(), or(), negate()로 두 Predicate를 하나로 결합(default 메서드)
Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q = i -> i < 200;
Predicate<Integer> r = i -> i%2 == 0;
Predicate<Integer> notP = p.negate(); // i >= 100
Predicate<Integer> all = notP.and(q).or(r); // 100 <= i && i < 200 || i%2 == 0
Predicate<Integer> all2 = notP.and(q.or(r)); // 100 <= i && (i < 200 || i%2 == 0)

메소드 참조(method reference)

  • 하나의 메서드만 호출하는 람다식은 '메소드 참조'로 '더' 간단히 할 수 있다.

클래스이름::메소드이름
or
참조변수이름::메소드이름

static 메서드 참조

Integer method(String s) { // 그저 Integer.parseInt(String s)만 호출
	 return Integer.parseInt(s);	
}
int result = obj.method("123");
int result = Integer.parseInt("123);

위의 코드는 굳이 obj.method를 호출할 필요없이 바로 Integer.parseInt를 호출하는것과 다름 없다.

Function<String, Integer> f = (String s) -> Integer.parseInt(s);

위의 코드 같은 경우 <String, Integer>를 통해 "String"이라는 입력정보를 알기 때문에 (String s)와 (s)를 생략할 수 있다.

Function<String, Integer> f = Integer::parseInt; // 메서드 참조
class Main {
	public static void main(String[] args) {
    	// Function<String, Integer> f = (String s) -> Integer.parseInt(s);
        Function<String, Integer> f = Integer::parseInt; // 메서드 참조
        
    	System.out.println(f.apply("100") + 200);
    }
}

생성자의 메서드 참조

Supplier<MyClass> s = () -> new MyClass();
Supplier<MyClass> s = MyClass::new;
Function<Integer, MyClass> s = (i) -> new MyClass(i);
Function<Integer, MyClass> s = MyClass::new;

배열과 메서드 참조

Function<Integer, int[]> f = x -> new int[x]; // 람다식

Function<Integer, int[]> f2 = int[]::new; // 메서드 참조

🚪 마무리

오늘은 람다에 대해 공부해 봤다. 내가 지금까지 알고 있었던 람다는 수박 겉핥기에 불과하다는 것을 느꼈고 람다와 친해지기 위해서 더 노력해야겠다..

profile
기록을 통해 성장합니다.

0개의 댓글