부트캠프

Java - Annotation, Lambda, Stream

hunm719 2023. 1. 6. 23:17

1.애너테이션(annotation) : 소스 코드가 컴파일되거나 실행될 때에 컴파일러 및 다른 프로그램에게 필요한 정보를 전달해주는 문법 요소
 -애너테이션은 @로 시작하며, 클래스, 인터페이스, 필드, 메서드 등에 붙여서 사용할 수 있음
 -프로그램마다 제공하는 애너테이션의 종류가 다름, 여기에서는 JDK가 제공하는 애너테이션중 중요한 것만 다룰 예정

1-1.표준 애너테이션 : JDK에 내장되어 있는 일반적인 애너테이션

 (1)@Override
  -선언한 메서드가 상위 클래스의 메서드를 오버라이딩하거나 추상 메서드를 구현하는 메서드임을 컴파일러에게 알려주는 역할
  -개발자의 실수로 메서드 이름이 잘못 작성될 경우를 대비해 에러의 원인을 쉽게 찾아내기 위해 사용

 (2)@Deprecated
  -선언된 대상이 새로운 것으로 대체되었으니 기존의 것을 사용하지 않도록 유도하는 역할
  -기존의 코드를 다른 코드와의 호환성 문제로 삭제하기 곤란해 남겨두어야만 하지만 더 이상 사용하는 것을 권장하지 않을 때에 사용

 (3)@SuppressWarnings
  -컴파일 경고 메시지가 나타나지 않도록 하는 역할
  -경우에 따라 경고가 발생할 것이 충분이 예상됨에도 묵인해야 할 때 사용
  -@SuppressWarings("") 안에 억제하고자하는 경고메시지 지정 가능, 여러 유형을 나열함으로써 여러 경고를 동시에 묵인 가능

 (4)@FunctionalInterface
  -함수형 인터페이스를 선언할 때, 컴파일러가 함수형 인터페이스의 선언이 바르게 선언되었는지 확인하는 역할
  -아래의 람다식에서 자세히 다룰 예정


1-2.메타 애너테이션 : 애너테이션을 정의할 때 사용하는 애너테이션
 
 (1)@Target
  -애너테이션을 적용할 대상을 지정하는 데 사용
  -@Target() 안에 대상 타입 지정 가능, java.lang.annotation.ElementType 이라는 열거형에 정의되어 있음

 (2)@Documented
  -애너테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 하는데 사용
  -JDK 표준 및 메타 애너테이션 중 @Override와 @SuppressWarnings를 제외한 모두가 적용됨.

 (3)@Inherited
  -하위 클래스가 애너테이션을 상속받도록 하는데 사용

 (4)@Retention
  -특정 애너테이션의 지속 시간을 결정하는 데 사용
  -애너테이션이 유지되는 기간을 지정하는 속성을 유지 정책이라 표현함

Retention의 유지 정책

  (5)@Repeatable
  -애너테이션을 여러 번 붙일 수 있도록 허용함
  -같은 이름의 애너테이션이 여러번 적용될 수 있기 때문에, 해당 애너테이션들을 하나로 묶어주는 애너테이션도 별도로 작성해야 함


1-3.사용자 정의 애너테이션 : 사용자가 직접 정의해서 사용하는 애너테이션
  -애너테이션은 java.lang.annotation 인터페이스를 상속받기 때문에 다른 클래스나 인터페이스를 상속 받을 수 없음


2.람다식(Lambda Expression) : 함수형 프로그래밍 기법을 지원하는 자바의 문법요소
 -수학자 Alonzo Church가 발표한 람다 계산법을 그의 제자 John McCarthy가 프로그래밍 언어에 도입
 -메서드를 하나의 ‘식(expression)’으로 표현한 것으로, 코드를 매우 간결하면서 명확하게 표현할 수 있음

 *람다식에서는 기본적으로 반환타입과 이름을 생략할 수 있고, 람다함수를 익명 함수(anonymous function)라고도 함
 **나아가 메서드 바디에 문장이 실행문 하나만 존재할 때, 중괄호와 return 문을 생략할 수 있고 이 경우 세미콜론까지 생략해야 함
 ***더 나아가 매개변수 타입을 함수형 인터페이스를 통해 유추할 수 있는 경우에는 매개변수의 타입을 생략할 수 있음

 +자바에서는 반드시 클래스 객체를 먼저 생성한 후 생성한 객체로 메서드를 호출해야 함. 같은 맥락에서 람다식은 메서드가 아닌 객체라는 사실을 알 수 있으며, 이름이 없기 때문에 익명 객체라고 함.
 +익명 객체는 익명 클래스를 통해 만들 수 있는데, 익명 클래스란 객체의 선언과 생성을 동시에 하여 오직 하나의 객체를 생성하고, 단 한번만 사용되는 일회용 클래스임

2-1.함수형 인터페이스(FunctionalInterface)
 -자바에서 함수형 프로그래밍을 하기 위한 새로운 문법 요소를 도입하는 대신, 기존의 인터페이스 문법을 활용하여 람다식을 다루는 것
 -함수형 인터페이스를 사용하면 참조변수의 타입으로 함수형 인터페이스를 사용하여 원하는 메서드에 접근 가능

 +함수형 인터페이스에는 단 하나의 추상 메서드만 선언 가능 (람다식과 인터페이스의 메서드가 1:1로 매칭되어야 하기 때문)
 +자바에서는 빈번하게 사용되는 함수형 인터페이스를 기본적으로 제공함, 아래는 API DOC 및 레퍼런스 링크

 

JDK 19 Documentation - Home

The documentation for JDK 19 includes developer guides, API documentation, and release notes.

docs.oracle.com

 

 

Java8 - 함수형 인터페이스(Functional Interface) 이해하기

함수형 인터페이스는 1개의 추상 메소드를 갖고 있는 인터페이스를 말합니다. Single Abstract Method(SAM)라고 불리기도 합니다. 함수형 인터페이스를 사용하는 이유는 자바의 람다식은 함수형 인터페

codechacha.com




2-2.메서드 참조(Method Reference) : 람다식에서 불필요한 매개변수를 제거할 때 주로 사용
 -람다식으로 간결해진 익명 객체를 더욱 간단하게 사용하기 위해 만들어짐
 -기존 메서드를 단순히 호출만 하는 람다식의 경우 입력값과 출력값의 반환타입을 쉽게 유추할 수 있기 때문에 아래와 같이 메서드 참조를 이용해 처리할 수 있음  

1
2
    (left, right) -> Math.max(left, right) // 두 개의 값을 받아 큰 수를 리턴하는 Math 클래스의 max() 정적 메서드를 호출하는 람다식
            Math :: max // 클래스_명::메서드_명 으로 메서드 참조
cs

 +메서드 참조도 인터페이스의 익명 구현 객체로 생성되므로 인터페이스의 추상 메서드가 어떤 매개 변수를 가지고, 리턴 타입이 무엇인가에 따라 달라짐

  (1)정적 메서드와 인스턴스 메서드 참조
   -정적 메서드를 참조할 경우, 클래스_명 :: 정적_메서드_명
   -인스턴스 메서드를 참조할 경우, (먼저 객체 생성 후) 참조_변수 :: 메서드_명
  
  (2)생성자 참조
   -생성자를 참조한다는 것은 객체 생성을 의미함
   -단순히 객체를 생성하고 리턴하도록 구성된 람다식은 생성자 참조로 대치 가능
   -문법은 다음과 같음, 클래스_명 :: new

   +생성자가 오버로딩 되어 여러 개가 있을 경우 컴파일러는 함수형 인터페이스의 추상 메서드와 동일한 매개 변수 타입과 개수를 가지고 있는 생성자를 찾아 실행함


3.스트림(Stream) : 배열, 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자
 -스트림을 사용하면 선언형 프로그래밍(Declarative Programming) 방식으로 데이터를 처리할 수 있어 보다 인간친화적이고 직관적인 코드 작성이 가능
  *대비되는 개념인 명령형 프로그래밍(Imperative Programming)은 코드 한 줄 한 줄의 동작 원리를 이해하고 순차적이고 세세하게 규정하는 방식
 -또한, 데이터 소스가 무엇이냐에 관계없이 같은 방식으로 데이터를 다룰 수 있음
  // 다른 데이터 소스(배열or컬렉션)을 사용할 경우 기존에는 어떠한 기능을 사용할 때 서로 다르게 사용해야했음

  // 예)배열정렬-Arrays.sort, List정렬-Collection.sort

3-1.스트림의 특징 4가지
 (1)스트림 처리 과정은 생성, 중간 연산, 최종 연산 세 단계의 파이프라인으로 구성될 수 있음

스트림 처리 과정

 (2)원본 데이터 소스를 변경하지 않음(read-only)
 (3)일회용(onetime-only)
 (4)내부 반복자(Internal iterator)
  *내부 반복자 <-> 외부 반복자(External Iterator)

외부 반복자(위 그림에선 iterator)와 내부 반복자의 차이

  **외부 반복자는 요소가 필요할 때마다 순차적으로 컬렉션에서 필요한 요소들을 불러옴. Index를 사용하는 for문, Iterator를 사용하는 while문이 대표적
  ***내부 반복자는 데이터 처리 코드만 컬렉션 내부로 주입해줘서 그 안에서 모든 데이터 처리가 이뤄지도록 하기 때문에 효율적인 데이터 처리 가능

3-2.스트림 생성
 -스트림을 생성할 수 있는 데이터 소스는 배열, 컬렉션, 임의의 수, 특정 범위의 정수 등 다양한데, 이에 따라 스트림의 생성 방법에 조금씩 다름

 (1)배열 스트림 생성 : Arrays.stream() 메서드 또는 Stream.of() 메서드 사용
 (2)컬렉션 스트림 생성 : stream() 메서드를 사용
 (3)임의의 수 스트림 생성 : Random() 메서드를 사용

3-3.스트림 중간 연산
 -중간 연산자(Intermediate Operation) : 하나 혹은 여러 개의 중간 연사자를 통해 데이터를 가공

 (1)필터링 : 필요에 따라 조건에 맞는 데이터들만을 정제하는 역할
  -distinct() : Stream의 요소들에 중복된 데이터가 존재하는 경우, 중복을 제거하기 위해 사용
  -filter(): Stream에서 조건에 맞는 데이터만을 정제하여 나머지는 버리고 해당된 데이터를 모아 하나의 컬렉션을 만듦
   *filter() 메서드에는 매개값으로 조건(Predicate)이 주어지고, 조건이 참이 되는 요소만 필터링함. 조건은 람다식을 사용하여 정의 할 수 있음

 (2)매핑 : 스트림 내 요소들에서 원하는 필드만 추출하거나 특정 형태로 변환할 때 사용
  -map()
  -flatMap() : 중첩 구조를 제거하고 단일 컬렉션(Stream<String>)으로 만들어주는 역할을 하며 이를 플래트닝(flattening)이라고 함

 (3)정렬 : 정렬 할 때 사용
  -sorted() : 괄호() 안에 Comparator 라는 인터페이스에 정의된 static 메서드와 디폴트 메서드를 사용하여 간편하게 정렬 작업 수행

 (4)기타
  -skip() - 스트림의 일부 요소를 건너뛸 때 사용
  -limit() - 스트림의 일부를 자를 때 사용
  -peek() - 요소들을 순회하며 특정 작업을 수행
   *forEach()는 최종 연산자라서 마지막 단 한번만 사용 가능, peek()는 중간 연산자라 여러번 연결하여 사용 가능


3-4.스트림 최종 연산
 -중간 연산은 최종 연산자가 수행될 때야 비로소 스트림의 요소들이 중간 연산을 거쳐 가공된 후에 최종 연산에서 소모되는데 이를 '지연된 연산(lazy evaluation)'이라 표현
 -forEach() : 데이터 소스의 각 요소들을 순회하면서 람다식 안에 정의된 어떤 명령을 실행하는 데 사용하는 최종연산자

 (1)기본 집계 : 숫자와 관련된 기본적인 집계의 경우에는 대부분 최종 연산자
  -sum(), count(), average(), max(), min()

 (2)매칭 : 조건식 람다 Predicate 를 매개변수로 넘겨 스트림의 각 데이터 요소들의 특정 조건 충족 여부를 검사하여, 그 결과를 boolean 값으로 반환
  -allMatch() : 모든 요소들이 조건을 만족하는 지 여부를 판단
  -noneMatch() : 모든 요소들이 조건을 만족하지 않는 지 여부를 판단
  -anyMatch() : 하나라도 조건을 만족하는 요소가 있는 지 여부를 판단

 (3)요소 소모
  -reduce() : 스트림의 요소를 줄여나가면서 연산을 수행하고 최종적인 결과를 반환
   *reduce()메서드는 최대 3개까지 매개변수를 받을 수 있음
   **첫 번째 매개변수는 특정 연산을 시작할 때 설정되는 초기값
   ***두 번째 매개변수는 각 요소들을 연산하여 나온 누적된 결과값을 생성하는 데 사용하는 조건식
   ****세 번째는 추후 자세히 업로드 예정

 (4)요소 수집 : 요소들을 수집하는 최종 처리 메서드
  -collect() : 스트림의 요소들을 List, Set, Map 등 다른 타입의 결과로 수집하고 싶은 경우 사용
 
 (5)기타
  -findAny(), findFirst(), toArray() 등 다양한 최종 연산자가 있음. 추후 reduce() 최종 연산자를 보강할 때 추가할 예정

 

 

이미지 및 내용 출처 - code states

'부트캠프' 카테고리의 다른 글

Java - Thread, JVM  (0) 2023.01.10
Java - 파일 입출력(I/O)  (0) 2023.01.09
Java 컬렉션 프레임워크  (1) 2023.01.04
Java 열거형, 제네릭, 예외 처리  (1) 2023.01.04
Java 객체지향 프로그래밍 심화 2  (1) 2022.12.30