본문 바로가기

프로그래밍/자바

[자바/기본] 람다식 (Lambda) 란?

728x90
반응형
SMALL

람다  표현식

  • Java8 부터 도입
  • 추상메소드가 하나뿐인 인터페이스를 Override를 통해 구현
    • 이를 함수형 인터페이스 (Functional Interface) 라고 부름
  • 익명 클래스 (관련 설명은 여기에[내부 클래스]) 의 더 간략한 표현식이라고 볼 수 있다.
  • 메소드를 간결한 함수 식으로 표현한 것으로 메소드의 이름, 반환값을 생략할 수 있기 떄문에 코드가 매우 간결해진다는 장점
  • 한번 사용하고 버려질 클래스 라면, 선언 하지 않고 일회성 오버라이딩으로 사용 가능
  • 예시
int add(int a, int b) {
	return a + b;
}
//아래와 같이 간단한 두 매개변수를 더하는 함수를 람다식으로 표현할 수 있다.

(a, b) -> {return a+b;}

 

  • 문법이 매우 짧아 지는 것을 확인 할 수 있다.
  • 람다 식을 통해 가독성이 좋아지고 특히 Collection Framework (관련 글) 를 사용 할 때, 요소를 필터링 하거나 정렬 되는 방식을 지정해 줄 수 있다.(Compareable)

람다  표현식 문법

  • 매개변수의 타입은 생략해서 표현이 가능
  • 매개변수가 여러개 있을 경우
    • 모든 매개변수의 타입을 생략해서 표현하거나
    • 모든 매개변수의 타입을 모두 다 표현해야 함. 
  • 밑에 예시에서는 모든 타입들을 생략하도록 하겠음
// 소괄호 생략 가능 과정
(a) -> {System.out.println(a);}		// 매개변수가 한개일 때는 소괄호 생략 가능
a -> {System.out.println(a);}		// 가능

(a,b) -> {System.out.println(a+b);}	//매개 변수가 두개일때는 소괄호 생략 불가능
a,b -> {System.out.println(a+b);}	//불가능

() -> {System.out.println("안녕?");}	// 매개변수가 없는 경우에는 생략 불가능

 

  • 매개변수가 하나일 때만, 소괄호 생략 가능하다.
// 중괄호 생략 가능 문법

// 보통 하나의 문장으로만 이루어진 코드는 중괄호를 생략 할 수 있다 라는 표현을 사용
(a)-> {return a+1;}		//중괄호 안에 구현 코드가 return 문만 존재할 때 중괄호와 return 을 생략 가능
(a) -> a+1;				//가능
(a) -> System.out.println(a); //가능 리턴값이 없는 void 일 때 가능 

//메소드의 반환 타입이 void가 아닌경우 혹은 안에 return 문만 있는 것이 아닐때에는 중괄호를 생략할 수 없다.
(a) -> {						//불가능
    if (a > 0){
    	return -1;
    }else {
    	return 1;
    }
}
  • 중괄호는 하나의 문장일때 만 생략이 가능하다.

 

람다 식에 관련 되어서 아래 3가지 방법으로 구현 하는 방법이 있다.

1. 클래스이름 implements 인터페이스이름 + 함수 오버라이드 & 구현

2. 익명 클래스를 통한 구현

3. 람다 표현식을 통한 구현


함수형 인터페이스 란? @FunctionalInterface? [java.util.function]

Runnable이라는 함수형 인터페이스에서 @FuntaionalInterface 확인

  • 함수형 인터페이스란 딱 하나의 추상 메소드가 선언된 인터페이스를 말한다.
  • 람다식을 사용하기 위해서는 인터페이스에 두개 이상의 추상 메소드가 선언되어 있으면 안된다
    • 어떤 메소드를 사용하고자 하는지 알 수 없기 때문에 사용할 수 없다. 
    • 생각해보면 당연한 거였는데 본인은 꽤 오래 생각했던거 같다.
  • @FuntionalInterface는 람다식으로 표현 가능한 함수형 인터페이스를 만들 때, 두 개 이상의 메소드가 선언 되면 컴파일러가 확인해주는 기능을 지원한다.
    • 예를들어 어노테이션이 걸려있는 이 부분에 추상 메소드를 두개를 선언하면 컴파일 오류를 발생 시켜준다는 것이다.
      • 개발자들 한테 참 좋다고 한다.

 

자바에서 제공하는 기본적인 함수형 인터페이스 [ java.util.function ]

Runnable 매개변수 없음.
return type : void
Supplier<T> 매개변수 없음.
return type : T
Consumer<T> 매개변수 type: T
return type : void
Function<T, R> 매개변수 type: T
return type : R
Predict<T> 매개변수 type: T
return type : T
UnaryOperator<T> 매개변수 type: T (2개)
return type : T
BinaryOperator<T> 매개변수 type: T (2개)
return type : T 
BiPredict<T, U> 매개변수 type: T,U (서로 다른 타입 2개)
return type : Boolean
BiConsumer<T, U> 매개변수 type: T , U (2개)
return type : void
BiFunctional<T, U, R> 매개변수 type: T,U (서로 다른 타입 2개)
return type : R
Comparator<T> 자바의 전통적인 인터페이스중 하나
매개변수 type: T (2개)
return type : int
// Runnable
{
    Runnable r = () -> System.out.println("hello functional");
    r.run();	//hello functional
}

//  Supplier<T>
{
    Supplier<String> s = () -> "hello supplier";
    String result = s.get();
    System.out.println(result);	//hello supplier
}

// Consumer<T>
{
    Consumer<String> c = str -> System.out.println(str);
    c.accept("hello consumer");	//hello consumer
}

// Function<T, R>
{
    Function<String, Integer> f = str -> Integer.parseInt(str);
    Integer result = f.apply("1");
    System.out.println(result);	// 1
}

//  Predicate<T>
{
    Predicate<String> p = str -> str.isEmpty();
    boolean result = p.test("hello");
    System.out.println(result);   // false
    System.out.println(p.test("")); // true
}

// UnaryOperator<T>
{
    UnaryOperator<String> u = str -> str + " operator";
    String result = u.apply("hello unary");
    System.out.println(result);		//hello unary operator
}

// BinaryOperator<T>
{
    BinaryOperator<String> b = (str1, str2) -> str1 + " " + str2;
    String result = b.apply("안녕", "얘들아");
    System.out.println(result);		//안녕 얘들아
}

// BiPredicate<T, U>
{
    BiPredicate<String, Integer> bp = (str, num) -> str.equals(Integer.toString(num));
    boolean result = bp.test("1", 1);
    System.out.println(result);	//true
}

//  BiConsumer<T, U>
{
    BiConsumer<String, Integer> bc = (str, num) -> System.out.println(str + " :: " + num);
    bc.accept("숫자", 5);	//숫자::5
}

// BiFunction<T, U, R>
{
    BiFunction<Integer, String, String> bf = (num, str) -> String.valueOf(num) + str;
    String result = bf.apply(5, "678");
    System.out.println(result);	//5678
}

// Comparator<T>
{
    Comparator<String> c = (str1, str2) -> str1.compareTo(str2);
    int result = c.compare("aaa", "bbb");
    System.out.println(result);		//-1
}

Method Reference (메소드 레퍼런스)

  • 람다 표현식에서 '메소드 1회'로 코드가 끝나는 경우 메소드 레퍼런스를 이용하면 코드가 훨씬 간략해 진다.
    • static 메소드 레퍼런스
      • 호출하고자 하는 정적 클래스::메소드
    • 매개변수의 인스턴스 메소드 레퍼런스
      • 매개변수 타입이 명확할때 해당 타입 클래스::메소드
    • 생성자 메소드 생성
      • 리턴해야하는 타입명::new
    • 외부 인스턴스 메소드 레퍼런스
      • 람다 캡처링을 이용해서 람다표현식 바깥에 있는 인스턴스의 메소드를 호출 할때 사용
      • 본인은 람다식을 통해 있는 변수를 람다식 구현파트에 사용하는 것이라고 생각 
        • 하지만 사용되는 변수는 effective final (이유는 익명 클래스를 사용하는 것이기 때문에! 위에 내부 클래스 링크를 타고 가면 있음)
{
	//static 메소드 레퍼런스
	Consumer<String> consumer = System.out::println;
    consumer.accept("static method reference")
}
{
	//인스턴스 메소드 레퍼런스
	UnaryOperator<String> unary = String::toUpperCase;
    System.out.println(unary.apply("hihi"));	//HIHI
}
{
	//생성자 메소드 레퍼런스
	String s = String::new;
}
{
	//외부 인스턴스 temp 선언
	String temp = "안녕하세요?";
	
    //외부 캡쳐링! (람다 식 내부에서 외부 인스턴스를 참조)
	Predict<String> predict = temp::equals;
    System.out.println(predict.test("아니?"));	//false
}
728x90
반응형
LIST

'프로그래밍 > 자바' 카테고리의 다른 글

[자바/기본] Stream 이란?  (0) 2024.02.20
[자바/기본] Optional 이란?  (0) 2024.02.19
[자바/기본] 내부 클래스(InnerClass)  (0) 2024.02.14
[자바/기본] List 란?  (0) 2024.02.13
[자바/기본] Collection Framework란?  (0) 2024.02.11