Java/개념

Optional

자바 8에서 추가된 키워드입니다.

 

어떠한 작업의 결과로 null이 왔을 때, 이를 처리하기 위해서는 보통 if문을 사용하여 체크합니다.

College Ahn = College.student(1234567, "ddings");

Integer age =  Ahn.getAge();
if(age != null){
    /* do something */
}

 

이런식으로 null을 체크하게되면, 컴파일 타임에 IDE가 알려주지 않기 때문에 사용자가 실수로 빠트리는 경우 + 코드 작성 시의 번거로움이 생깁니다.

 

한 두개의 경우는 별로 신경쓰이지 않더라도 null체크를 해야하는 값이 많아지면 많아질수록 사용자가 실수할 확률은 점점 상승하며, 코드의 가독성은 하락할 것으로 예상됩니다.

 

 

Optional

Optional을 사용하게 되면 Optional에서 제공하는 메소드를 이용하여 null체크를 하는 것이 가능합니다.

 

Optional로 감싼 데이터가 null일 경우 [ Optional.empty ] 로 표현되며 내부의 값 체크는 isPresent() 또는 ifPresent()등의 메소드를 통하여 할 수 있습니다.

 

public static void main(String[] args) {

    College Ahn = College.student(1627315, "ddings");

    Optional<Integer> age = Ahn.getAge();

    age.ifPresent(System.out::println);
}


public static class College{

    private Integer Student_num;

    private String password;

    private Integer age;

    private College(Integer student_num, String password) {
        this.Student_num = student_num;
        this.password = password;
    }

    public static College student(Integer student_num, String password){
        return new College(student_num, password);
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Optional<Integer> getAge() {
        return Optional.ofNullable(age);
    }
}

 

 

Optional은 메소드 파라미터, 메소드 리턴값, map의 key값, 필드등에 사용할 수 있습니다.

 

사용은 할 수 있지만 메소드 리턴값으로만 사용하는 것을 권장하며, 그 이유는 다른목적로 사용시에 대체로 처리가 더 번거로워지던가 사용목적에 어긋남 등이 있습니다.

 

 

 

public void setAge(Optional<Integer> age) {
    age.ifPresent(age->this.age = age);
}

실제로 메소드 파라미터에 Optional을 적용하게 되면, 만일 사용자가 메소드 파라미터 자체를 null로 보낼 시에 

Optional<Integer> age 자체가 이미 null이 되기 때문에 NullPointerException이 발생하게 됩니다.

 

 

 

 

주요 메소드

Optional과 관련된 메소드는 java.util.Optional<T>에서 확인할 수 있습니다.

 

Optional (Java Platform SE 8 )

A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value. Additional methods that depend on the presence or absence of a contained value are provided, such as orEl

docs.oracle.com

 

Optional은 결국 실제 값을 감싸는 보호막을 하나 생성한다는 느낌과 동일하게 느껴집니다.

 

값을 Optional로 감싸는 방법은 여러가지가 존재합니다.

// Optional에 아무것도 들어있지 않은 Optional생성
Optional<College> emptyCollege = Optional.empty();

//특정 값을 Optional에 감싸기
Optional<College> OptionalOF = Optional.of(College.student(1234567, "ddings"));

//특정 값을 Optional로 감싸되, 그 값이 null이 될 수도 있음.
Optional<College> Nullable = Optional.ofNullable(College.student(1234567, "ddings"));

 

  • Optional.empty() = 빈 Optional 생성
  • Optional.of() = 특정 값을 Optional로 감싸서 생성
  • Optional.ofNullable() = of와 동일하나 null이 올 수 있음. ( null이 오면 Optional.empty가 반환됨)

 

 

 

 

Optional에 값이 들어있는지를 체크하는 방법Optional에 있는 값을 꺼내는 방법에 대해 알아봅시다.

College Ahn = College.student(1234567, "ddings");
Optional<Integer> age = Ahn.getAge();

// Optional에 들어있는 값이 존재하는지 boolean으로 반환
System.out.println(age.isPresent());

// Optional내에 값이 존재한다면 그 값으로 처리하기 
age.ifPresent(item -> System.out.println(item));
// == age.ifPresent(System.out::println);

// Optional 내에 들어있는 값 얻기
System.out.println(age.get());

 

setAge를 통해 age를 따로 설정하지 않았기 때문에 Ahn.getAge()는 빈 Optional을 반환하게 됩니다.

 

따라서 첫번째 출력은 false가 출력되며

두번쨰 출력은  이루어지지 않고

세번쨰 출력은 비어있는 값을 꺼내려고 하므로 NullPointerException이 발생합니다.

 

 

 

 

 

Optional이 비어있을 시에 대신 처리할 행동을 지정할 수도 있습니다.

// 값이 있으면 그걸 사용하고, 없으면 예외를 던져라

//NoSuchElementException 발생
Ahn.getAge().orElseThrow();

//지정하는 예외 발생
Ahn.getAge().orElseThrow(IllegalArgumentException::new);

//값이 있으면 그대로 쓰고, 없으면 딴걸로 대체하기
Integer Ahn_age = Ahn.getAge().orElse(10);
System.out.println(Ahn_age);

Ahn.setAge(20);
Ahn_age = Ahn.getAge().orElse(10);
System.out.println(Ahn_age);

//값이 있으면 그걸 사용하고, 없으면 Supplier의 실행을 거친 값을 사용하기.
Ahn.getAge().orElseGet(() -> {
    Ahn.setAge(25);
    return Ahn.getAge().get();
});

orElseThrow()의 경우 예외를 발생시키고

orElse의 경우 다른 값을 반환하며

orElseGet은 Supplier 실행을 거쳐서 값을 반환합니다.