Free Lines Arrow
본문 바로가기
Language/Java

[Java] Optional 제대로 쓰기

by skahn1215 2023. 2. 6.
728x90
반응형

Optional 제대로 쓰기

Optional에 대해 공부를 해보다가 좋은 예제들이 있어서 적어봅니다.

출처를 남겼으며 나머지 내용들도 확인해 보시면 좋을것 같습니다.

 

옵셔널 변수에 널값 사용하지 않기

Optional 을 쓸때는 null 지옥을 피하기위해 사용함으로 null 값을 쓰는것은 의미가 없다.

// AVOID
public Optional<Cart> fetchCart() {

    Optional<Cart> emptyCart = null;
    ...
}

 

 

// PREFER
public Optional<Cart> fetchCart() {

    Optional<Cart> emptyCart = Optional.empty();
    ...
}

 

Get을 쓰기전에 값 존재 여부확인

// AVOID
Optional<Cart> cart = ... ; // this is prone to be empty
...
// if "cart"is empty then this code will throw a java.util.NoSuchElementException
Cart myCart = cart.get();

 

 

// PREFER
if (cart.isPresent()) {
    Cart myCart = cart.get();
    ... // do something with "myCart"
} else {
    ... // do something that doesn't call cart.get()
}

 

디폴트 값이 있다면 orElse 를 사용하자

// AVOID
public static final String USER_STATUS = "UNKNOWN";
...
public String findUserStatus(long id) {

    Optional<String> status = ... ; // prone to return an empty Optional

    if (status.isPresent()) {
        return status.get();
    } else {
        return USER_STATUS;
    }
}

 

 

// PREFER
public static final String USER_STATUS = "UNKNOWN";
...
public String findUserStatus(long id) {

    Optional<String> status = ... ; // prone to return an empty Optional

    return status.orElse(USER_STATUS);
}

 

orElse 대신 orElseGet 을 쓰자

orElse 같은 경우는 optional 이 비워져 있지 않아도 평가 연산을 수행한다.

// AVOID
public String computeStatus() {
    ... // some code used to compute status
}

public String findUserStatus(long id) {

    Optional<String> status = ... ; // prone to return an empty Optional

    if (status.isPresent()) {
        return status.get();
    } else {
        return computeStatus();
    }
}

 

 

// PREFER
public String computeStatus() {
    ... // some code used to compute status
}

public String findUserStatus(long id) {

    Optional<String> status = ... ; // prone to return an empty Optional

    // computeStatus() is called only if "status" is empty
    return status.orElseGet(this::computeStatus);
}

 

 

값이 없는 경우 익셉션을 던지자

// AVOID
public String findUserStatus(long id) {

    Optional<String> status = ... ; // prone to return an empty Optional

    if (status.isPresent()) {
        return status.get();
    } else {
        throw new IllegalStateException(); 
    }
}

 

 

// PREFER
public String findUserStatus(long id) {

    Optional<String> status = ... ; // prone to return an empty Optional

    return status.orElseThrow();
}

 

 

// PREFER
public String findUserStatus(long id) {

    Optional<String> status = ... ; // prone to return an empty Optional

    return status.orElseThrow(IllegalStateException::new);
}

 

값이 존재 할때만 메소드를 실행하고 싶은 경우

// AVOID
Optional<String> status = ... ;
...
if (status.isPresent()) {
    System.out.println("Status: " + status.get());
}

 

 

// PREFER
Optional<String> status ... ;
...
status.ifPresent(System.out::println);

 

값 존재 여부에 따라 처리를 해야 될때

// AVOID
Optional<String> status = ... ;

if(status.isPresent()) {
    System.out.println("Status: " + status.get());
} else {
    System.out.println("Status not found");
}

 

 

// PREFER
Optional<String> status = ... ;

status.ifPresentOrElse(
    System.out::println, 
    () -> System.out.println("Status not found")
);

 

생성자에 Optional 값을 쓰지 말자

// AVOID
public class Customer {

    private final String name;               // cannot be null
    private final Optional<String> postcode; // optional field, thus may be null

    public Customer(String name, Optional<String> postcode) {
        this.name = Objects.requireNonNull(name, () -> "Name cannot be null");
        this.postcode = postcode;
    }

    public Optional<String> getPostcode() {
        return postcode;
    }
    ...
}

 

 

// PREFER
public class Customer {

    private final String name;     // cannot be null
    private final String postcode; // optional field, thus may be null

    public Cart(String name, String postcode) {
        this.name = Objects.requireNonNull(name, () -> "Name cannot be null");
        this.postcode = postcode;
    }

    public Optional<String> getPostcode() {
        return Optional.ofNullable(postcode);
    }
    ...
}

 

컬렉션 리턴 타입에 옵셔널을 쓰지말자

  • 대신 Collections.emptyList() 를 사용하자
// AVOID
public Optional<List<String>> fetchCartItems(long id) {

    Cart cart = ... ;    
    List<String> items = cart.getItems(); // this may return null

    return Optional.ofNullable(items);
}

 

 

// PREFER
public List<String> fetchCartItems(long id) {

    Cart cart = ... ;    
    List<String> items = cart.getItems(); // this may return null

    return items == null ? Collections.emptyList() : items;
}

 

 

 

 

Optional.of() 와 Optional.ofNullable() 을 혼동하지 말자

// AVOID
public Optional<String> fetchItemName(long id) {

    String itemName = ... ; // this may result in null
    ...
    return Optional.of(itemName); // this throws NPE if "itemName" is null :(
}

 

 

// PREFER
public Optional<String> fetchItemName(long id) {

    String itemName = ... ; // this may result in null
    ...
    return Optional.ofNullable(itemName); // no risk for NPE    
}

 

Optional<T> 사용을 지양하자 대신 OptionalInt, OptionalLong, or OptionalDouble 쓰자!

// AVOID
Optional<Integer> price = Optional.of(50);
Optional<Long> price = Optional.of(50L);
Optional<Double> price = Optional.of(50.43d);

 

 

// PREFER
OptionalInt price = OptionalInt.of(50);           // unwrap via getAsInt()
OptionalLong price = OptionalLong.of(50L);        // unwrap via getAsLong()
OptionalDouble price = OptionalDouble.of(50.43d); // unwrap via getAsDouble()

 

Asserting Equality 을 수행 할때 UnWrap을 할 필요가 없다

// AVOID
Optional<String> actualItem = Optional.of("Shoes");
Optional<String> expectedItem = Optional.of("Shoes");        

assertEquals(expectedItem.get(), actualItem.get());

 

 

// PREFER
Optional<String> actualItem = Optional.of("Shoes");
Optional<String> expectedItem = Optional.of("Shoes");        

assertEquals(expectedItem, actualItem);

 

특정값을 조건처리할때 filter 를 쓰자.

// AVOID
public boolean validatePasswordLength(User userId) {

    Optional<String> password = ...; // User password

    if (password.isPresent()) {
        return password.get().length() > 5;
    }

    return false;
}

 

 

// PREFER
public boolean validatePasswordLength(User userId) {

    Optional<String> password = ...; // User password

    return password.filter((p) -> p.length() > 5).isPresent();
}

 

Optional 값을 비교 할때 equals 를 쓰자

// AVOID
Product product = new Product();
Optional<Product> op1 = Optional.of(product);
Optional<Product> op2 = Optional.of(product);

// op1 == op2 => false, expected true
if (op1 == op2) { ..

 

 

// PREFER
Product product = new Product();
Optional<Product> op1 = Optional.of(product);
Optional<Product> op2 = Optional.of(product);

// op1.equals(op2) => true,expected true
if (op1.equals(op2)) { ...

 

 

 

 

출처:

https://dzone.com/articles/using-optional-correctly-is-not-optional

 

26 Reasons Why Using Optional Correctly Is Not Optional - DZone

We take a look at the top 25 most important concepts to keep in mind when working with Optionals in Java, focusing on null variables and more.

dzone.com

 

728x90
반응형

'Language > Java' 카테고리의 다른 글

[Java] sort, nullsLast, nullsFirst  (0) 2024.03.17
[Java] Optional<T> 기본  (0) 2023.02.06
[Java] SQL Mapper 만들기  (0) 2022.10.20
[Java] Generic 메서드  (0) 2022.06.25
[Java] Generic 와일드 카드  (0) 2022.06.25

댓글