본문 바로가기
개발/JAVA

자바 8 정리

by 설이주인 2022. 7. 8.

해당 게시글은 아래 강의를 듣고 정리한 것입니다.

https://www.inflearn.com/course/the-java-java8/dashboard

 

더 자바, Java 8 - 인프런 | 강의

자바 8에 추가된 기능들은 자바가 제공하는 API는 물론이고 스프링 같은 제 3의 라이브러리 및 프레임워크에서도 널리 사용되고 있습니다. 이 시대의 자바 개발자라면 반드시 알아야 합니다. 이

www.inflearn.com

 

 

https://codechacha.com/ko/

 

codechacha

Java, Kotlin, Android, Python, Linux 등 Programming, Software 개발 방법을 다룹니다.

codechacha.com

 

 


구글 DOC

함수형 인터페이스 (Functional Interface)

  • 추상 메소드를 딱 하나만 가지고 있는 인터페이스
  • SAM (Single Abstract Method) 인터페이스
  • @FuncationInterface 애노테이션을 가지고 있는 인터페이스

람다 표현식 (Lambda Expressions)

  • 함수형 인터페이스의 인스턴스를 만드는 방법으로 쓰일 수 있다.
  • 코드를 줄일 수 있다.
  • 메소드 매개변수, 리턴 타입, 변수로 만들어 사용할 수도 있다.

자바에서 함수형 프로그래밍

  • 함수를 First class object로 사용할 수 있다.
  • 순수 함수 (Pure function)
    • 사이드 이팩트가 없다. (함수 밖에 있는 값을 변경하지 않는다.)
    • 상태가 없다. (함수 밖에 있는 값을 사용하지 않는다.)
  • 고차 함수 (Higher-Order Function)
    • 함수가 함수를 매개변수로 받을 수 있고 함수를 리턴할 수도 있다.
  • 불변성

Functional Interface 안에서 추상화 메서드하나만 존재 해야한다.

→ SAM (Single Abstract Method)

이러한 interface를 사용하기 위해서 우리는 익명 내부 클래스를 선언했습니다.

보통 이렇게 선언했었다. 자바 8 이후에는 이제 간단하게 선언하는 방법이 생긴 상황이다. → 람다식 표현 //회색부분에서 (option + enter)

익명 함수 람다로 변환 했다. +) 안에서 실향문이 하나 일시 아래와 같이 생략 되며

실행문이 두 단계 이상일 시 아래와 같이 합쳐진다.

람다식에서 생략하여 작성해도 기존 클래스 안의 행동을 작동 할 수 있다는 점에서

→ 함수를 First class object로 사용할 수 있다의 정의이다 (?? 뭐래 다시 정리해) + !! 고차함수 : 함수가 함수를 리턴하는 것 ( 당연히 가능 )

!! 순수 함수 : 입력 받은 값이 동일한 경우 결과 값이 같아야 한다. 기존 interface (A)에 10을 넣었고 우리가 해당 인터페이스를 사용하는 상황(B) 에서 10을 넣는다면 기존 인터페이스 A와 우리가 지금 사용하는 B에서는 같은 결과가 나와야 한다. 다른 결과가 나와서는 안된다.

자바에서 제공하는 함수형 인터페이스


구글 DOC

Java가 기본으로 제공하는 함수형 인터페이스

  • java.util.function 패키지
  • 자바에서 미리 정의해둔 자주 사용할만한 함수 인터페이스
  • Function<T, R>
  • BiFunction<T, U, R>
  • Consumer<T>
  • Supplier<T>
  • Predicate<T>
  • UnaryOperator<T>
  • BinaryOperator<T>

Function<T, R>

  • T 타입을 받아서 R 타입을 리턴하는 함수 인터페이스
    • R apply(T t)
  • 함수 조합용 메소드
    • andThen
    • compose

BiFunction<T, U, R>

  • 두 개의 값(T, U)를 받아서 R 타입을 리턴하는 함수 인터페이스
    • R apply(T t, U u)

Consumer<T>

  • T 타입을 받아서 아무값도 리턴하지 않는 함수 인터페이스
    • void Accept(T t)
  • 함수 조합용 메소드
    • andThen

Supplier<T>

  • T 타입의 값을 제공하는 함수 인터페이스
    • T get()

Predicate<T>

  • T 타입을 받아서 boolean을 리턴하는 함수 인터페이스
    • boolean test(T t)
  • 함수 조합용 메소드
    • And
    • Or
    • Negate

UnaryOperator<T>

  • Function<T, R>의 특수한 형태로, 입력값 하나를 받아서 동일한 타입을 리턴하는 함수 인터페이스

BinaryOperator<T>

  • BiFunction<T, U, R>의 특수한 형태로, 동일한 타입의 입렵값 두개를 받아 리턴하는 함수 인터페이스

기본으로 제공하는 함수 인터페이스

Function apply, compose →

BiFunction 위의 Function과 유사하지만 입력 값을 두개 받는다.

Consumer 값을 받지만 리턴하지 않는다.

andThen :: andThen()은 Consumer들을 연결해주는 Chaining 메소드입니다.

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class ConsumerExample4 {
/*
		아래 코드에서 addNumber.andThen(printList).accept(fruits)를 호출하면 
		addNumber()가 수행된 이후에 printList()가 수행됩니다. 
		두개의 메소드로 전달되는 인자는 fruits로, 동일한 객체가 전달됩니다.
*/
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "kiwi", "orange");

        Consumer<List<String>> addNumber = list -> {
            for (int i = 0; i < list.size(); i++) {
                list.set(i, (i + 1) + ". " + list.get(i));
            }
        };

        Consumer<List<String>> printList = list -> {
            for (String fruit : list) {
                System.out.println(fruit);
            }
        };

        addNumber.andThen(printList).accept(fruits);
    }
}

/*
	// Consumer.java
	public interface Consumer<T> {
	  ...
	  default Consumer<T> andThen(Consumer<? super T> after) {
	      Objects.requireNonNull(after);
	      return (T t) -> { accept(t); after.accept(t); };
	  }
	}*/

Supplier

T 타입의 값을 제공하는 함수 인터페이스

//10을 받아옴
Supplier<Integer> get10 = () -> 10;
System.out.println(get10);

//출력값
//T get()
//get10.get()을 출력한다.
me.whiteship.java8to11.Foo$$Lambda$19/0x0000000800b92270@1d81eb93

Predicate

인자값을 받아서 Ture/False를 리턴 해주는 인터페이스

UnaryOperator (Funtion의 특수한 상태)

입력값과 결과값의 타입이 같은 경우 사용 할 수 있다.

//둘이 같은 행동이
Function<Integer, Integer> plus = (i) -> i + 10;
UnaryOperator<Integer> plusUnary = (i) -> i + 10;

BinaryOperator

BiFunction의 특수 상태

BinaryOperator는 Type T의 인자 두개를 받고, 동일한 Type T 객체를 리턴하는 함수형 인터페이스입니다.

import java.util.function.BinaryOperator;

public class BinaryOperatorExample1 {

    public static void main(String[] args) {

        BinaryOperator<Integer> binaryOperator = (n1, n2) -> n1 + n2;

        Integer sum = binaryOperator.apply(10, 100);

        System.out.println(sum);
    }
}

람다 표현식


구글 DOC

람다

  • (인자 리스트) -> {바디}

인자 리스트

  • 인자가 없을 때: ()
  • 인자가 한개일 때: (one) 또는 one
  • 인자가 여러개 일 때: (one, two)
  • 인자의 타입은 생략 가능, 컴파일러가 추론(infer)하지만 명시할 수도 있다. (Integer one, Integer two)

바디

  • 화상표 오른쪽에 함수 본문을 정의한다.
  • 여러 줄인 경우에 { }를 사용해서 묶는다.
  • 한 줄인 경우에 생략 가능, return도 생략 가능.

변수 캡처 (Variable Capture)

  • 로컬 변수 캡처
    • final이거나 effective final 인 경우에만 참조할 수 있다.
    • 그렇지 않을 경우 concurrency 문제가 생길 수 있어서 컴파일가 방지한다.
  • effective final
    • 이것도 역시 자바 8부터 지원하는 기능으로 “사실상" final인 변수.
    • final 키워드 사용하지 않은 변수를 익명 클래스 구현체 또는 람다에서 참조할 수 있다.
  • 익명 클래스 구현체와 달리 ‘쉐도윙’하지 않는다.
    • 익명 클래스는 새로 스콥을 만들지만, 람다는 람다를 감싸고 있는 스콥과 같다.

참고


private void run() {
        int baseNumber = 10;

				//로컬 클래스
        class LocalClass{
            void printBaseNumber(){
                System.out.println(baseNumber);
            }
        }

				//익명 클래스
        Consumer<Integer> integerConsumer = new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(baseNumber);
            }
        };

				//로컬 람다 클래스
        IntConsumer printInt = (i) ->{
            System.out.println(i + baseNumber);
        };

        printInt.accept(10);
    }

→ 로컬, 익명 클래스 안에서 다시 선언하는 변수(?)는 덮어씌기 처리가 된다. 쉐도잉(?)이라고 함

→ 하지만 람다는 해당행위가 이뤄지지 않는다.

→ 같은 scope이다 라는 말을 조금 더 알아보자.

```java
//기존
int max(int a, int b) {
	return a > b ? a : b;
}

//람다식 - int max  생략
(int a, int b) -> {
	return a > b ? a : b;
}

//return문 대신 expression 사용
// int max, {} 생략
(int a, int b) -> a > b ? a: b

//매개변수 타입 생략
//int max, {}, int 매개변수 타입 생략
(a, b) -> a > b ? a : b

//매개변수 1개일 경우 괄호 생략
a -> a*a     //OK

int a -> a*a //에러

//본문 문장 1개일 경우 중괄호 생략
(String name, int i) -> System.out.println(name+"="+i)
:: - 메서드 참조 (Method reference)

람다식이 하나의 메서드만 호출하는 경우, 메서드 참조를 통해 람다식을 간략히
할 수 있다.

클래스명:;메서드명 / 참조변수::메서드명

// 기존
Function<String, Integer> f = (String s) -> Integer.parseInt(s);
//(String s) -> Integer.parseInt(s);
//  [메서드 타입,메서드 명] (String s) {
//     return Integer.parseInt(s);
//  }

// 메서드 참조
Funcation<String, Integer> f = Integer::parseInt;
====================================================================

생성자 호출 람다식 - 메서드 참조 변환 가능

Supplier<MyClass> s = () -> new MyClass();  // 람다식
Supplier<MyClass> s = MyClass::new; // 메서드 참조

/*
	.orElseThrow(EntityNotFoundException::new)

	.orElseThrow(() -> new EntityNotFoundException())
*/

메소드 레퍼런스 << 따로 한번 알아보기

---

***구글 DOC*** 

람다가 하는 일이 기존 메소드 또는 생성자를 호출하는 거라면, 메소드 레퍼런스를 사용해서 매우 간결하게 표현할 수 있다.

메소드 참조하는 방법

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3c85c4c0-b8ef-4a64-b244-c426adb5677d/Untitled.png

- 메소드 또는 생성자의 매개변수로 람다의 입력값을 받는다.
- 리턴값 또는 생성한 객체는 람다의 리턴값이다.

참고

https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html


---

코드에서 **::** << 메소드 레퍼런스

인터페이스의 변화 (인터페이스 기본 메소드와 스태틱 메소드)

---

***구글 DOC*** 

기본 메소드 (Default Methods)

- 인터페이스에 메소드 선언이 아니라 구현체를 제공하는 방법
- 해당 인터페이스를 구현한 클래스를 깨트리지 않고 새 기능을 추가할 수 있다.
- 기본 메소드는 구현체가 모르게 추가된 기능으로 그만큼 리스크가 있다.
    - 컴파일 에러는 아니지만 구현체에 따라 런타임 에러가 발생할 수 있다.
    - 반드시 문서화 할 것. (@implSpec 자바독 태그 사용)
- Object가 제공하는 기능 (equals, hasCode)는 기본 메소드로 제공할 수 없다.
    - 구현체가 재정의해야 한다.
- 본인이 수정할 수 있는 인터페이스에만 기본 메소드를 제공할 수 있다.
- 인터페이스를 상속받는 인터페이스에서 다시 추상 메소드로 변경할 수 있다.
- 인터페이스 구현체가 재정의 할 수도 있다.

스태틱 메소드

- 해당 타입 관련 헬퍼 또는 유틸리티 메소드를 제공할 때 인터페이스에 스태틱 메소드를 제공할 수 있다.

참고

https://docs.oracle.com/javase/tutorial/java/IandI/nogrow.html
https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html

---

기본적으로 인터페이스 생성 후 implements를 받은 클래스 내에서 override를 해줘야 한다. 안하면 에러남

하지만 override를 해주지 않아도 사용 할 수 있게 구현체를 제공하는 방법이 만들어진 상태이다.

→ 에러 발생

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a7e616e4-9fcd-43c0-ba66-eef5f4da80b3/Untitled.png

→ default 추가

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a2c47180-d3c2-4160-adc7-c8a8236b8fa1/Untitled.png

→ 주의할점 + 

재정의는 구현제가 모르게 추가된 기능으로 그만큼 리스크가 존재한다.

컴파일에서 에러는 발생하기 보다는 런타임에서 발생하기에 반드시 문서화 해야한다.

**@implSec**

→ Object에서 제공하는 메서드들은 재정의 불가능하다.

자바 8에서 추가 된 기본 API

---

***구글 DOC*** 

자바 8에서 추가한 기본 메소드로 인한 API 변화

Iterable의 기본 메소드

- forEach()
- spliterator()

Collection의 기본 메소드

- stream() / parallelStream()
- removeIf(Predicate)
- spliterator()

Comparator의 기본 메소드 및 스태틱 메소드

- reversed()
- thenComparing()
- static reverseOrder() / naturalOrder()
- static nullsFirst() / nullsLast()
- static comparing()

참고

https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.html
https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html
https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html
https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html

---

 forEach()

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/437ed6e3-c15c-4f3f-999d-5a6c8b265950/Untitled.png

spliterator() << List, Set을 나누는 행위 

Comparator 정렬 

Stream(스트림)  API 

---

***구글 DOC*** 

Stream

- sequence of elements supporting sequential and parallel aggregate operations
- 데이터를 담고 있는 저장소 (컬렉션)이 아니다.
- Funtional in nature, 스트림이 처리하는 데이터 소스를 변경하지 않는다.
- 스트림으로 처리하는 데이터는 오직 한번만 처리한다.
- 무제한일 수도 있다. (Short Circuit 메소드를 사용해서 제한할 수 있다.)
- 중개 오퍼레이션은 근본적으로 lazy 하다.
- 손쉽게 병렬 처리할 수 있다.

**스트림 파이프라인**

- 0 또는 **다수의 중개 오퍼레이션 (intermediate operation)**과 **한개의 종료 오퍼레이션 (terminal operation)**으로 구성한다.
- 스트림의 데이터 소스는 오직 터미널 오퍼네이션을 실행할 때에만 처리한다.

중개 오퍼레이션

- **Stream을 리턴한다.**
- Stateless / Stateful 오퍼레이션으로 더 상세하게 구분할 수도 있다. (대부분은 Stateless지만 distinct나 sorted 처럼 이전 이전 소스 데이터를 참조해야 하는 오퍼레이션은 Stateful 오퍼레이션이다.)
- filter, map, limit, skip, sorted, ...

종료 오퍼레이션

- **Stream을 리턴하지 않는다.**
- collect, allMatch, count, forEach, min, max, ...

참고

https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

---

Stream 

- 연속된 데이터를 처리하는 오퍼레이션
- Funtional in nature(작업중인 데이터를 변경하지 않는다.)
- 중개 오퍼레이션은 근본적으로 lazy 하다.
- → 기본적으로  stream안에는 중개 오퍼레이터와 종료 오퍼레이션이 존재한다.
- →제일 큰 차이점은  stream을 리턴 하느냐 마냐 또한 중개형 오퍼리이터들은 터미널 오퍼레이터가 오지 않는 이상 실제로 실행을 하지 않는다 (아래의 map은 중개형 오퍼레이터이다. )

**중개형 오퍼레이터만 펼치고 정의한 상황**

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2064b0f0-430d-468e-a772-2d392be91761/Untitled.png

**실제로 종료 오퍼레이터를 정의한 상황**

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6d72be46-c3ab-4316-9bb5-de2b9b5b8167/Untitled.png

Stream API

---

***구글 DOC*** 

걸러내기

- Filter(Predicate)
- 예) 이름이 3글자 이상인 데이터만 새로운 스트림으로

변경하기

- Map(Function) 또는 FlatMap(Function)
- 예) 각각의 Post 인스턴스에서 String title만 새로운 스트림으로
- 예) List<stream>을 String의 스트림으로

생성하기

- generate(Supplier) 또는 Iterate(T seed, UnaryOperator)
- 예) 10부터 1씩 증가하는 무제한 숫자 스트림
- 예) 랜덤 int 무제한 스트림

제한하기

- limit(long) 또는 skip(long)
- 예) 최대 5개의 요소가 담긴 스트림을 리턴한다.
- 예) 앞에서 3개를 뺀 나머지 스트림을 리턴한다.

---

스트림에 있는 데이터가 특정 조건을 만족하는지 확인

- anyMatch(), allMatch(), nonMatch()
- 예) k로 시작하는 문자열이 있는지 확인한다. (true 또는 false를 리턴한다.)
- 예) 스트림에 있는 모든 값이 10보다 작은지 확인한다.

개수 세기

- count()
- 예) 10보다 큰 수의 개수를 센다.

스트림을 데이터 하나로 뭉치기

- reduce(identity, BiFunction), collect(), sum(), max()
- 예) 모든 숫자 합 구하기
- 예) 모든 데이터를 하나의 List 또는 Set에 옮겨 담기

---

```java
//filter
springClass.stream()
        .filter(oc -> oc.getTitle().startWith("spring"))
        .forEach(oc -> sout(oc.getId));

//Predicate
springClass.stream()
        .filter(Predicate.not(OnlineClass::isClosed))
        .forEach(oc -> sout(oc.getId()));

//map
springClass.stream()
        .map(OnlineClass::getTitle)
        .forEach(sout);

//flatMap 각list들을 펼친다.
//flat 리스트<클래스> 각 각에 들어가 있었던 요소를 한번에 줄로 펼치는 행위
someEvents.stream()
        .flatMap(Collection::stream)
        .forEach(oc ->sout(oc.getId()));

//iterate
//10부터 1씩 증가하는 무제한 스트림 
Stream.iterate(10, i-> i + 1)
    .skip(10)//앞 10은 건너뛰고
    .limit(10)//10번 행동 
    .forEach(sout(i));

//Match
boolean test = springClass.stream().anyMatch(oc -> oc.getTitle.contains("Test"));

//순서에 따라서 나오는 갑싱 다름
springClass.stream()
        .filter(oc -> oc.getTitle().contains("spring"))//Stream class
        .map(OnlineClass::getTitle)
        .collect(Collectors.toList());

springClass.stream()
        .map(OnlineClass::getTitle)//Stream
        .filter(oc -> oc.getTitle().contains("spring"))
        .collect(Collectors.toList());

Optional


구글 DOC

자바 프로그래밍에서 NullPointerException을 종종 보게 되는 이유

  • null을 리턴하니까! && null 체크를 깜빡했으니까!

메소드에서 작업 중 특별한 상황에서 값을 제대로 리턴할 수 없는 경우 선택할 수 있는 방법

  • 예외를 던진다. (비싸다, 스택트레이스를 찍어두니까.)
  • null을 리턴한다. (비용 문제가 없지만 그 코드를 사용하는 클리어인트 코드가 주의해야 한다.)
  • (자바 8부터) Optional을 리턴한다. (클라이언트에 코드에게 명시적으로 빈 값일 수도 있다는 걸 알려주고, 빈 값인 경우에 대한 처리를 강제한다.)

Optional

  • 오직 값 한 개가 들어있을 수도 없을 수도 있는 컨네이너.

주의할 것

  • 리턴값으로만 쓰기를 권장한다. (메소드 매개변수 타입, 맵의 키 타입, 인스턴스 필드 타입으로 쓰지 말자.)
  • Optional을 리턴하는 메소드에서 null을 리턴하지 말자.
  • 프리미티브 타입용 Optional을 따로 있다. OptionalInt, OptionalLong,...
  • Collection, Map, Stream Array, Optional은 Opiontal로 감싸지 말 것.

참고


에러를 if처리로 찾아 왔었는데 기본적으로 리소스를 사용하는 상황으로 진짜 필요시에 예외 처리를 해야한다. 즉, 로직을 처리할때 이런 nullpointException을 사용하는것은 좋은 습관이 아니다.

//옵션 1) 클래스에서 null을 체크한다.
OneClass.java 
public Progress getProgress(){
	if (this.progress == null){
			thorw.new IllegalStateException();
	}
}

//옵션 2) 클라이언트 코드에서 체크 하낟.
Progress progress = spring_boot.getProgress();
if(progress != null){
	sout(progress.getStudyDuration());
}

//옵션 1과 2의 문제점은 우리는 코드는 사람이 작성 하기에 실수가 존재하고 빼먹을 수 있는 가능성이 존재한다.
//자바8부터는 해당 사항을 명시적으로 표현 할 수 있는 상태가 만들어졌다.

//Optional //가능하면 리턴 타입으로만 쓰는것을 추천한다.
OneClass.java 
public OPtional<Progress> getProgress(){
		return Optional.ofNullable(progress); // of : return이 무조건 null이 아니다. (nullpointException)
																					// ofNullable: null이 가능하다.
}

Optional API


구글 DOC

Optional 만들기

  • Optional.of()
  • Optional.ofNullable()
  • Optional.empty()

Optional에 값이 있는지 없는지 확인하기

  • isPresent()
  • isEmpty() (Java 11부터 제공)

Optional에 있는 값 가져오기

  • get()
  • 만약에 비어있는 Optional에서 무언가를 꺼낸다면??

Optional에 값이 있는 경우에 그 값을 가지고 ~~를 하라.

  • ifPresent(Consumer)
  • 예) Spring으로 시작하는 수업이 있으면 id를 출력하라.

Optional에 값이 있으면 가져오고 없는 경우에 ~~를 리턴하라.

  • orElse(T)
  • 예) JPA로 시작하는 수업이 없다면 비어있는 수업을 리턴하라.

Optional에 값이 있으면 가져오고 없는 경우에 ~~를 하라.

  • orElseGet(Supplier)
  • 예) JPA로 시작하는 수업이 없다면 새로 만들어서 리턴하라.

Optional에 값이 있으면 가졍고 없는 경우 에러를 던져라.

  • orElseThrow()

Optional에 들어있는 값 걸러내기

  • Optional filter(Predicate)

Optional에 들어있는 값 변환하기

  • Optional map(Function)
  • Optional flatMap(Function): Optional 안에 들어있는 인스턴스가 Optional인 경우에 사용하면 편리하다.

Optional ::

of() - null 안됨

ofNullable() - null 허용

empty() - 해당 Optional이 비어있을 수 있다.

get() - Optional 안에 값이 들어가 있다면 찾아온다. / 없는 경우에는 if로 감싸주는 방안이다. //하지만 get()외의 방법으로 값을 가져오는것을 추천한다.

orElse(T) = optional이 존재하던 말던 실행

orElseGet() = optional이 존재하지 않을때만 실행

orElseThorw() = 에러 발생시 떤질 예외 //주로 IlleagalArgumentException::new

map = 기본적으로 optional안에 들어있는 값을 리턴 함. 단, optional을 return 할시에는 주의가 필요하다.

flatMap = map이optional을 리턴할려고 할때 쓰는 map :: 한번더 껍질을 벗겨줌

++)) optional안의 map과 stream에서의 map은 다르다.

List<OnlineClass> springClasses = new ArrayList<>();
springClasses.add(new OnlineClasses(1, "spring boot", true));
springClasses.add(new OnlineClasses(5, "rest api development", false));

Optional<OnlineClass> optional = springClasses.stream()
				.filter(oc -> oc.getTitle().startsWith("spring"))
				.findFirst();

//optional이 해당 값을 갖고 있을때
optional.ifPresent(oc -> sout(oc.getTitle()));   // 안에 spring  존재시 spring boot 출력, 
																								// 없는 값 찾을시에는 아무것도 출력안됨

OnlineClasses onlineClass = optional.orElse(createNewClass());
//orElse()는 위의 상황에서 spring을 갖고 있는 상황이여도 createNewClass가 실행된다.
// 물론 없는 ㅅ상황에서는 당연히 실행 된다.
sout(onlineClass.fgetTitle());

//orElse의 요소가 있던 말던 무조건으로 실행하는 상황은 우리가 희망하는 상황이 아니다. 그래서 다음꺼를 써보자
//spring이 존재할 시에는 작동하지 않는다, 없을 시에 작동
OnlineClasses onlineClass = optional.orElseGet(() -> createNewClass());

++) orElse는 정적인 요소  orElseGet은 동적으로 무언가를 만들어야할때 사용하는것을 추천한다.

//무언가를 만들 수 없을때 orElseThrow()를 사용한다.
OnlineClass onlineclass = optional.orElseThrow(() -> {
		return new IlleagalArgumentException();
});
OnineClass onlineClass = optional.oerElseThorw(IlleagalArgumentException::new);

//map 기본적으로 리턴하는 값을 map 한다 중간에 주의할 점은 optional map이 optional을 리턴 할때이다..
//두번 열어줘야함.
OPtional<Optional<Progress>> progress =optional.map(OnlineClass::getProgress);
Optional<Progress> progress1 = progress.orElseThrow();
progress1.orElseThrow()

//위의 map을 사용할 때 두번 열지 않도록 해주는 flatMap
optional.flatMap(OnlineClass::getProgress);
//getProgress에서 리턴 되는 타입이 optional이다 하면 해당 껍질을 까준다.
Optional<Optional<Progress>> progress =optional.map(OnlineClass::getProgress);
Optional<Progress> progress1 = progress.orElseThrow(Optional.empty());
//와 같음 ++)) 주의 optional에서 제공하는map과 stream에서 제공하는 map과는 다르다.
//stream에서의 map은 1대1 매핑이다.
//flatMap은 input은 하나지만 output이 여러개일 경우에 사용한다.

private static OnlineClass createVewClass(){
	sout("createing new online class";
	return new OnlineClass(10, "nNewclass", flase);)
}

Date / Time


구글 DOC

자바 8에 새로운 날짜와 시간 API가 생긴 이유

  • 그전까지 사용하던 java.util.Date 클래스는 mutable(값이 바뀐다.) 하기 때문에 thead safe하지 않다.
  • 클래스 이름이 명확하지 않다. Date인데 시간까지 다룬다.
  • 버그 발생할 여지가 많다. (타입 안정성이 없고, 월이 0부터 시작한다거나..)
  • 날짜 시간 처리가 복잡한 애플리케이션에서는 보통 Joda Time을 쓰곤했다.

자바 8에서 제공하는 Date-Time API

  • JSR-310 스팩의 구현체를 제공한다.
  • 디자인 철학
    • Clear
    • Fluent
    • Immutable
    • Extensible

주요 API

  • 기계용 시간 (machine time)과 인류용 시간(human time)으로 나눌 수 있다.
  • 기계용 시간은 EPOCK (1970년 1월 1일 0시 0분 0초)부터 현재까지의 타임스탬프를 표현한다.
  • 인류용 시간은 우리가 흔히 사용하는 연,월,일,시,분,초 등을 표현한다.
  • 타임스탬프는 Instant를 사용한다.
  • 특정 날짜(LocalDate), 시간(LocalTime), 일시(LocalDateTime)를 사용할 수 있다.
  • 기간을 표현할 때는 Duration (시간 기반)과 Period (날짜 기반)를 사용할 수 있다.
  • DateTimeFormatter를 사용해서 일시를 특정한 문자열로 포매팅할 수 있다.

참고(1. 기존 데이트 타입에 무슨 문제가 있었는지 알려주는 링크)


기존 Date는 값이 변할 수 있는 존재였다 mutable, 위의 thread safe 하지 않다는 의미는

트랜잭션 1에서 Date를 사용 하는 도중에 트랜잭션 2 또한 Date를 사용한다. 이 상황에서

T1이 먼저 +1을 하고 있다. 해당 동작이 끝나기 전에 T2에서 Date에 -1을 실행한다.

그럼 T1이 기대하고 있는 Date + 1의 행동이 T2 Date -1 때문에 영향을 받는다. 이런 상황을 thread safe 하지 않다라고 한다.

Date와 Time API


구글 DOC

지금 이 순간을 기계 시간으로 표현하는 방법

  • Instant.now(): 현재 UTC (GMT)를 리턴한다.
  • Universal Time Coordinated == Greenwich Mean Time
Instant now = Instant.now();
System.out.println(now);
System.out.println(now.atZone(ZoneId.of("UTC")));

ZonedDateTime zonedDateTime = now.atZone(ZoneId.systemDefault());
System.out.println(zonedDateTime);

인류용 일시를 표현하는 방법

  • LocalDateTime.now(): 현재 시스템 Zone에 해당하는(로컬) 일시를 리턴한다.
  • LocalDateTime.of(int, Month, int, int, int, int): 로컬의 특정 일시를 리턴한다.
  • ZonedDateTime.of(int, Month, int, int, int, int, ZoneId): 특정 Zone의 특정 일시를 리턴한다.
//(year, Month, day, hour, minute, second) 
LocalDateTime birthDay = LocalDateTime.of(1999, Month.July, 15, 0,0,0); 

//서울 현 날짜, 시간이 나옴
ZonedDateTime nowInKorea = ZonedDateTime.now(ZoneId.of("Asia/Seoul"));

기간을 표현하는 방법

  • Period(사람용) / Duration(기계용) . between()
Period between = Period.between(today, birthDay);
System.out.println(between.get(ChronoUnit.DAYS));

파싱 또는 포매팅

DateTimeFormatter formatter =
								DateTimeFormatter.ofPattern("MM/d/yyyy");

LocalDate date = LocalDate.parse("07/15/1982", formatter);

System.out.println(date);
System.out.println(today.format(formatter));

레거시 API 지원

  • GregorianCalendar와 Date 타입의 인스턴스를 Instant나 ZonedDateTime으로 변환 가능.
  • java.util.TimeZone에서 java.time.ZoneId로 상호 변환 가능.
ZoneId newZoneAPI = TimeZone.getTimeZone("PST").toZoneId();
TimeZone legacyZoneAPI = TimeZone.getTimeZone(newZoneAPI);

Instant newInstant = new Date().toInstant();
Date legacyInstant = Date.from(newInstant);

LocalDateTime now = LocalDateTime.now();
LocalDateTime plus = now.plus(10, ChronoUnit.Days); //새로운 인스턴스를 선언해주어야함.
plus.~~~  ;;

CompletableFuture


Executors


Callable과 Future


CompletableFuture 1


CompletableFuture 2


어노테이션의 변화


구글 DOC

애노테이션 관련 두가지 큰 변화

  • 자바 8부터 애노테이션을 타입 선언부에도 사용할 수 있게 됨.
  • 자바 8부터 애노테이션을 중복해서 사용할 수 있게 됨.

타입 선언 부

  • 제네릭 타입
  • 변수 타입
  • 매개변수 타입
  • 예외 타입
  • ...

타입에 사용할 수 있으려면

  • TYPE_PARAMETER: 타입 변수에만 사용할 수 있다.
  • TYPE_USE: 타입 변수를 포함해서 모든 타입 선언부에 사용할 수 있다.

중복 사용할 수 있는 애노테이션을 만들기

  • 중복 사용할 애노테이션 만들기
  • 중복 애노테이션 컨테이너 만들기
    • 컨테이너 애노테이션은 중복 애노테이션과 @Retention 및 @Target이 같거나 더 넓어야 한다.
//Chicken.java (중복 사용할 애노테이션)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@Repeatable(ChickenContainer.class)
public @interface Chicken {
    String value();
}

//ChickenContainer.java (중복 애노테이션의 컨테이너 애노테이션)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface ChickenContainer {
    Chicken[] value();
}

//컨테이너 애노테이션으로 중복 애노테이션 참조하기
@Chicken("양념")
@Chicken("마늘간장")
public class App {

    public static void main(String[] args) {
        ChickenContainer chickenContainer = App.class.getAnnotation(ChickenContainer.class);
        Arrays.stream(chickenContainer.value()).forEach(c -> {
            System.out.println(c.value());
        });
    }
}

제네릭Generic 타입

자바 [JAVA] - 제네릭(Generic)의 이해

배열 Parallel 정렬


구글 DOC

Arrays.parallelSort()

  • Fork/Join 프레임워크를 사용해서 배열을 병렬로 정렬하는 기능을 제공한다.

병렬 정렬 알고리듬

  • 배열을 둘로 계속 쪼갠다.
  • 합치면서 정렬한다.

sort()와 parallelSort() 비교

				int size = 1500;
        int[] numbers = new int[size];
        Random random = new Random();
        IntStream.range(0, size).forEach(i -> numbers[i] = random.nextInt());

        long start = System.nanoTime();
        Arrays.sort(numbers);
        System.out.println("serial sorting took " + (System.nanoTime() - start));
				//serial sorting took 548957

        IntStream.range(0, size).forEach(i -> numbers[i] = random.nextInt());
        start = System.nanoTime();
        Arrays.parallelSort(numbers);
        System.out.println("parallel sorting took " + (System.nanoTime() - start));
				//parallel sorting took 364074

Metaspace //가비지 컬렉션


구글 DOC

JVM의 여러 메모리 영역 중에 PermGen 메모리 영역이 없어지고 Metaspace 영역이 생겼다.

PermGen

  • permanent generation, 클래스 메타데이터를 담는 곳.
  • Heap 영역에 속함.
  • 기본값으로 제한된 크기를 가지고 있음.
  • XX:PermSize=N, PermGen 초기 사이즈 설정
  • XX:MaxPermSize=N, PermGen 최대 사이즈 설정

Metaspace

  • 클래스 메타데이터를 담는 곳.
  • Heap 영역이 아니라, Native 메모리 영역이다.
  • 기본값으로 제한된 크기를 가지고 있지 않다. (필요한 만큼 계속 늘어난다.)
  • 자바 8부터는 PermGen 관련 java 옵션은 무시한다.
  • XX:MetaspaceSize=N, Metaspace 초기 사이즈 설정.
  • XX:MaxMetaspaceSize=N, Metaspace 최대 사이즈 설정.

참고


요약


구글 DOC

자바 8의 주요 기능의 의미

  • 함수형 프로그래밍
    • 함수형 인터페이스
    • 람다 표현식
    • 메소드 레퍼런스
  • 비동기 프로그래밍
    • CompletableFuture
  • 편의성 개선
    • Optional
    • Date & Time
    • 인터페이스
    • ...

앞으로

  • 당분간은 자바8이 가장 많이 쓰이는 버전으로 남을 가능성이 높다.
  • 하지만 시간이 지날수록 계속해서 자바 11로 넘어가는 비율이 높아질 것이다.
  • 따라서 자바 9부터 자바 11까지의 기능도 학습해 둘 것
            •  

 

'개발 > JAVA' 카테고리의 다른 글

ModelMapper와 친해지기  (0) 2022.07.14
Iterator  (0) 2022.07.11
Junit - Assert 메소드  (0) 2022.06.02
[JUNIT] 테스트 코드 연습[1]  (0) 2022.06.02
[JAVA] StringTokenizer  (0) 2022.05.23