이브, 프시케 그리고 푸른 수염 어쩌고 따라하고 싶었어요ㅎㅎ..
Lambda
익명 함수로써 이름 없이 정의되며, 코드 블록을 1급 시민으로 다룰 수 있는 프로그래밍 언어의 기능
- 🧐 1급 시민 First-Class Citizen | 1급 객체
- 변수에 할당할 수 있어야 한다.
- 함수의 매개변수로 전달할 수 있어야 한다.
- 함수의 리턴값으로 사용할 수 있어야 한다.
- 함수나 메소드가 1급 시민으로 간주되기 위한 조건
- 함수형 프로그래밍 스타일을 지원
- 코드를 간결하게 관리
- 주로 컬렉션 처리, 스트림 연산, 콜백 함수 등에 사용
→ 자바에선 함수를 일급시민으로 쓸 수 없었음, 람다 개념 등장 → 일급시민으로 사용할 수 있게됨
람다 표현식 작성방법
어떤 언어에서든 익명함수를 다 써보셨을 거라고 예상됩니다. 자바에서도 마찬가지로 위와 같이 람다를 사용합니다.
Functional Interface
@FunctionalInterface
interface CustomInterface<T> {
// abstract method 오직 하나
T myCall();
// default method 는 존재해도 상관없음
default void printDefault() {
System.out.println("Hello Default");
}
// static method 는 존재해도 상관없음
static void printStatic() {
System.out.println("Hello Static");
}
}
1개의 추상 메소드를 갖는 인터페이스를 뜻함 (SAM, Single Abstract Method)
→ 2개 이상의 추상 메소드가 존재하면 함수형 인터페이스가 될 수 없다
하지만 default, static 메소드는 여러 개 있어도 괜찮다!
이 하나의 추상 메소드가 1급 시민의 특성을 가진다!!!
Java의 인터페이스에서 default, static 키워드는 언제 왜 등장했을까요?
Java8에서 등장. 이전까지는 인스턴스 메소드만 가질 수 있었음
- 유틸리티나 헬퍼 함수 같은 공통 코드
- 기존 코드와 호환성과 업데이트 용이성
(어!!! 인터페이스에 추상 메소드를 추가하니까 구현체들 모두 상속시켜야 되네!!! 귀찮은데 디폴트 구현체로 넣자!!!)
public interface Collection<E> extends Iterable<E> {
...
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
...
}
@FunctionalInterface 어노테이션은 뭔가요?
해당 인터페이스가 함수형 인터페이스 조건에 맞는지를 검사한다
필수는 아니지만 인터페이스 검증과 유지보수를 위해 붙여 주는 게 좋아요
근데요 함수형 인터페이스를 우리가 구현하는 일이 거의 없어용>.<
기본적으로 제공하는 인터페이스를 사용
- Function<T, R>
- BiFunction<T, U, R>
- Consumer<T>
- Supplier<T>
- Predicate<T>
- Runnable
- Callable 기타 등등
Predicate<T>로 알아보는 함수형 인터페이스 사용법
인자를 받아 boolean 타입을 리턴하는 인터페이스를 제공
T → boolean
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
// 간단 사용 예제
Predicate<Integer> sample = (count) -> count.equals(50); // Integer::equals
// 오른쪽 람다식이 'test 추상메소드의 구현체다'라고 생각하면 됨
sout(sample.test(10)); // false
sout(sample.test(50)); // true
Consumer<T>
인자 하나 받고 아무것도 리턴하지 않음
T → void
소비자라는 이름에 맞게 무언가 소비만 하고 끝낸다
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
Supplier<T>
인자를 받지 않고 T타입의 객체를 리턴
() → T
공급자라는 이름처럼 아무것도 받지 않고 특정 객체를 리턴
@FunctionalInterface
public interface Supplier<T> {
T get();
}
- Callable 또한 ()→T 함수형 인터페이스다. Supplier와 거의 차이가 없다고 생각하면 된다. 하지만 Callable은 Runnable과 함께 등장한 개념으로 ExecutorService 와 사용됨
Function<T, R>
T타입의 인자를 받아 R타입 객체를 리턴
T → R
말 그대로 수학적인 의미에서 함수!
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
BiFunction 은 Function에서 파생된 클래스로 (T, U) → R
Comparator<T>
T타입 인자 2개를 입력 받아서 int타입으로 리턴
(T, T) -> int
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
Stream API
람다 표현식, Optional, 메소드 참조(Class::method) 개념이 있어야 함
.map( i → sout(i) ) ⇒ .map(System.out::println)
stream이란 ‘줄줄이 이어지다’라는 뜻으로 일련의 연속성을 갖는 흐름을 의미
stream API는 자바 컬렉션을 다루는데 사용되는 강력한 기능을 제공하는 인터페이스의 집합
- 일관성 있는 연산 지원
- 한 번 생성하고 사용한 스트림은 재사용할 수 없다
- 생성(creation), 중개(intermediate), 최종(terminal) 연산으로 구분된다
- lazy invocation이 적용된다. ⇒ terminal 연산이 호출돼야 최종 결과가 반영된다.
⇒ what 위주의 내부 반복 연산을 수행!
Stream Creation
스트림의 데이터 소스로 컬렉션도 배열도 파일도 string도 무한스트림으로도 사용될 수 있음
Stream.empty()
*Collection*.stream()
Arrays.stream(arr)
Stream.generate( ()→ T ).limit(10) : 스트림 무한하기 때문에 limit를 걸어줘야 함
Stream.iterate(T, Collable).limit(10)
*Primitive*.range(start, end)
Stream Intermediate
함수형 인터페이스를 인자로 중개 연산을 합니다!
public interface Stream<T> extends BaseStream<T, Stream<T>> {
Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
}
filter 메소드를 보면 Predicate를 인자로 받는 걸 볼 수 있죠?
Stream 생성 클래스
public final class StreamSupport {
public static <T> Stream<T> stream(Supplier<? extends Spliterator<T>> supplier,
int characteristics,
boolean parallel) {
Objects.requireNonNull(supplier);
return new ReferencePipeline.Head<>(supplier,
StreamOpFlag.fromCharacteristics(characteristics),
parallel);
}
//... IntStream, DoubleStream 등등 존재
}
스트림 문법의 장점 : 간결함, 가독성 높아짐, 순차처리, 병렬처리 지원
스트림 문법의 단점 : 연산 비용 증가, 언박싱/박싱 비용
하지만 요즘은 하드웨어가 워낙 발전해서 연산 비용보다 유지보수 비용이 더 크기 때문에 스트림을 사용하는 것을 권장한다고 합니다. (물론 회사마다 다르겠지만요?)
'Develop > Java' 카테고리의 다른 글
[Java] Thread 클래스와 ThreadPoolExecutor 클래스 (0) | 2023.09.10 |
---|---|
Java JVM 아키텍쳐 (1) (2) | 2023.07.16 |
[Java] String to Json 파싱하기 | Jackson ObjectMapper (0) | 2023.04.02 |
TDD 테스트 주도 개발 (0) | 2023.02.19 |
코드 테스트에 관한 약어(FIRST, Right-BICEP, CORRECT) (0) | 2023.02.16 |