자바 8에서 추가된 스트림 개념은 배열이나 컬렉션을 다루는 개념으로 다양한 프레임 워크를 다루는데 혹은 클린코드 등의 주제에서 많이 등장한다. 자바8이라고 해서 얼마 안된 느낌이지만 이러한 기술이 뭔가 지금 사용하는 기술에 매우 유용하게 사용되고 있는 것 같아서 정리하려고 한다. 

 

스트림에서 주로 사용되는 것은 람다식과 표준 API 함수 인터페이스다. 뭔가 낯선 개념이지만 하나씩 정리하려 한다.

 

람다식

함수형 프로그래밍 방식을 지원하는 표현식이다. 컬랙션 or 배열 조작을 위한 방법으로 사용되며 이러한 람다식을 사용하면 코드가 매우 간결해진다. 먼저 람다식을 표현하는 방법에 대해 살펴보자

 

람다식 작성 법칙

람다식은 익명 함수다. 이름이 없는 함수다. 람다식은 아래와 같이 작성된다. 

// 일반 함수(메서드)
int max(int a, int b) {
	return a > b ? a: b;
}

// 람다식으로 바꾼 식
(a, b) -> a > b ? a: b;

함수의 return 타입과 함수 이름이 사라지며 ()와 { } 사이에 화살표(->) 가 추가 된다.  또한 정해진 규칙에 따라 특정 형태에서 타입, 괄호, return, 세미클론 등을 생략할 수 있다. 

 

1. 매개변수의 타입이 추론 가능하면 생략가능하다.(보통 제네릭과 함께 사용되어 타입이 추론된다.)

(int a, int b) -> a > ? a: b  ☞ (a, b) -> a > ? a: b

2. 괄호({   })안에 문장이 하나인 경우 괄호({  })가 생략하능하다. 괄호가 생략되는 경우 끝에 세미클론(;)을 붙이지 않는다.

(int a, int b) -> { return a > b ? a : b; }  ☞ (int a, int b) -> a > b ? a : b
  • 주의 사항
    • 매개변수가 하나일 때만 (  )를 생략할 수 있다. 두 개이상일 때는 (  )를 생략 불가 / 그리고 (  ) 를 생략하는 경우에는 자료형도 함께 생략한다. 매개변수 옆에 자료형이 붙어 있다면 (  )는 생략할 수 없다. 
    • return이 붙어있다면 {   } 를 생략할 수 없다. 
  • 람다식 접근 순서
    • 먼저 함수 이름, 반환타입 제거하고 ( )과 { } 사이에 -> 를 넣자
    • 매개변수가 2개 이상이면 ( )를 지우지 말고 제네릭으로 매개변수 타입이 추론 가능하면 매개변수 타입을 지우고 {  } 안에 문장이 1개이면 return을 지우고 return을 지우면 {  } 를 지우고 {  } 안에 있는 세미클론(;)을 을 빼자

 

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

(a, b) -> a > b ? a : b   

↕ 위 아래는 사실 동일한 표현

new Object() {
	int max(int a, int b){
    	return a > b ? a : b;
    }
}

Object obj = new Object() {
	int max(int a, int b){
    	return a > b ? a : b;
    }
}
Object obj = (a, b) -> a > b ? a : b ;// 에러
int value = obj.max(3, 5); // 에러

사실 람다식은 Object로 정의한 익명클래스다. 그래서 익명 객체라고도 한다. 하지만  obj.max(3, 5);을 호출하려고 하기에는 Object에서 max가 정의되지 않아 에러가 난다. 결과적으로 반드시 함수형 인터페이스를 써야 한다. 

 

함수형 인터페이스 

단 하나의 추상 메서드만 선언된 인터페이스다. @FunctionalInterface라는 애너테이션을 붙이는데 이 직접 함수형 인터페이스를 구현할 때 @FunctionalInterface를 안붙여도 에러는 나지는 않는다. ( @FunctionalInterface를 붙이면  인터페이스에 메서드를 2개이상 정의시 컴파일 에러를 띄운다. ) 그리고 이것은 익명 클래스와 비슷하다. (interface냐 class냐 차이)

 

interface MyInterface {
	public abstract int max(int a, int b); // 추상메서드는 이거하나 만듬
}

위의 인터페이스의 메서드는 아래의 코드에서 익명 클래스로 메서드가 구현이 되고 인터페이스 자료형으로 메서드를 호출한다. 

MyInterface m = new MyInterface() { // 
    @Override
    public int max(int a, int b) {
        return a > b ? a : b;
    }
};

int value = m.max(3, 5);  // 위의 호출을 다음의 방식으로 해결, 왜냐면 MyInterface에 max 메서드가 정의되어 있다.

↕ 위, 아래 동일

MyInterface f = (a, b) -> a > b ? a : b; 
int value = f.max(3, 5);

// 축약시에는 new MyInterface() { } 부분이 벗겨진다. > 후에 람다식 표현 적용

결과적으로 람다식의 모형과 함수형 인터페이스로 인스턴스를 만드는 방식과 모형이 동일하고 실제로 이 둘은 같다. 람다식은 이러한 함수형 인터페이스를 베이스로 만들어진 것이다. 

 

표준 API 함수 인터페이스

자바에서는 미리 정의된 함수형 인터페이스 API를 제공한다. 

 

1. Comsumer : 소비만 한다. 리턴이 없는 구조이다. 

 - 메서드 이름 : accept

- T -> void
- Consumer<T>
- BiConsumer<T, U> : 매개변수 갯수가 2개 

 

2. Supplier : 공급만 한다. 소비가 없다. 매개변수가 없다. 

 - 메서드 이름 : get

- () -> T 
- Supplier<T>

 

3. Function : 매개변수와 리턴값이 있는 함수다. 함수의 개념을 생각하면 된다. 

 - 메서드 이름 : apply
- Function<T, R>
- BiFunction<T, U, R> : 매개변수 갯수가 2개 

 

4. Operator

- 메서드 이름 : identity

- T -> T
- UnaryOperator<T>
- BinaryOperator<T>
- Function 하위셋

 

5. Predicate : true/false 인지 판단하는 의미로 boolean 리턴형을 가진다. 

- 메서드 이름 : test

- T -> boolean 
- Predicate<T>
- BiPredicate<T, U>
- Function 하위셋

- true/false가 결과로 나오는 형태 

 

6. Comparator : 비교 메서드를 구현, sort 함수에서 인자로 넣는 경우가 있음

- 메서드 이름 : compare  

- (T, T) -> int

 

7. Runnable : 실행 가능한 이라는 의미를 가진다. 매개변수도 없고 반환형도 없는 메서드를 가짐

- 메서드이름 : run

- () -> void

 

8.Callble : 호출 가능한 의미를 가진다. 호출해서 결과를 반환하는 리턴값을 가짐 

- 메서드 이름 :call

- () -> T

 

 

 

'자바' 카테고리의 다른 글

JVM 구조  (0) 2023.07.23

+ Recent posts