최근 개발을 할 때 Optional을 사용하는 경우가 많아졌다. Optional은 잘 쓰면 굉장히 파워풀한 기능이지만 잘못 사용하면 낭비가 될수 있다.
이왕 사용하는 김에 잘 사용하고 싶어서 Optional 사용 tip을 정리를 해보았다.
Optional<Sample> sampleVar = null; // Bad! Optional<Sample> sampleVar = Optional.empty(); // Good!
if (sampleVar.isPresent()){ sampleVar.get(); }else{ // }
// Bad! public static final String NO_VALUE = "UNKNOWN"; pulic String findSample(Integer id){ Optional<String> sampleVal = ... ; if (status.isPresent()) { return status.get(); } else { return NO_VALUE; } }
// Good! public static final String NO_VALUE = "UNKNOWN"; pulic String findSample(Integer id){ Optional<String> sampleVal = ... ; return sampleVal.orElse(NO_VALUE); } }
orElse() 를 사용할 때, 옵셔널 값이 빈 값이 아니라서 orElse() 까지 도달하지 않더라도 그 내부에 있는 객체가 생성이 된다.
// Bad! pulic String findSample(Integer id){ Optional<String> sampleVal = ... ; return sample.orElse(new Sample()); // sample이 빈 값이여도 orElse()을 호출하여 객체를 생성한다 => 낭비 }
// Good! pulic String findSample(Integer id){ Optional<String> sampleVal = ... ; return sample.orElseGet(Sample::new); // sample이 빈 값이면 orElseGet()은 호출되지 않는다. } }
orElseGet() 은 orElse() 와는 다르게 Supplier 함수형 인터페이스를 인자로 받기 때문에 람다식 혹은 메소드 레퍼런스를 인자로 넘겨주면 된다.
public String findSample(Integer id) { Optional<String> sample = ... ; return sample.orElseThrow(); }
자바 버전이 8 혹은 9라면 아래와 같은 방법으로 사용 가능하다.
public String findSample(Integer id) { Optional<String> sample = ... ; return sample.orElseThrow(NoSuchElementException::new); }
// Bad! Optional<Sample> sampleClass = ... ; if (sampleMethod.isPresent()){ myMethod.invoke(sampleClass.get(), ...); }else{ myMethod.invoke(null, ...); }
// Good! Optional<Sample> sampleClass = ... ; myMethod.invoke(sampleClass.get().orElse(null), ...);
// Bad! Optional<Sample> sampleClass = ... ; if (sampleMethod.isPresent()){ System.out.println("Sample: " + sampleMethod.get()); }
// Good! Optional<Sample> sampleClass = ... ; sample.ifPresent(System.out::println);
// Bad! Optional<Sample> sampleClass = ... ; if (sampleMethod.isPresent()){ System.out.println("Sample: " + sampleMethod.get()); }else{ System.out.println("Status not found"); }
// Good! Optional<Sample> sampleClass = ... ; sample.ifPresentOrElse( System.out::println, // if 절의 action () -> System.out.println("Status not found") // else 절의 action );
Optional.or()는 orElse()나 orElseGet()과는 다르게 Optional을 리턴한다
// Good! Optional<Sample> sampleClass = ... ; Optional<Sample> dafaultSample = Optional.of("DEFAULT"); return sampleClass.or(() -> defaultSample);
// Bad! Optional<Sample> sampleClass = sampleClasses.stream() .filter(x -> p.getNumber < num) .findFirst(); if (sampleClass.isPresent()) { return sample.get().getName(); } else { sampleClass "NOT FOUND"; }
// Good! Optional<Sample> sampleClass = ... ; Optional<Sample> sampleClass = sampleClasses.stream() .filter(x -> p.getNumber < num) .findFirst(); .orElse("NOT FOUND");