전통적인 널 포인터 검사 대신 Java 8+에서 Optional을 사용하는 이유는 무엇입니까?


110

우리는 최근에 Java 8로 옮겼습니다. 이제 응용 프로그램에 Optional개체가 넘칩니다 .

Java 8 이전 (스타일 1)

Employee employee = employeeServive.getEmployee();

if(employee!=null){
    System.out.println(employee.getId());
}

Java 8 이후 (스타일 2)

Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
    Employee employee = employeeOptional.get();
    System.out.println(employee.getId());
}

Optional<Employee> employeeOptional = employeeService.getEmployee();서비스 자체가 옵션을 반환 할 때의 부가 가치는 없습니다 .

Java 6 배경에서 스타일 1이 더 명확하고 코드 줄이 적습니다. 내가 여기서 놓친 진정한 이점이 있습니까?

모든 답변의 이해와 블로그의 추가 연구로부터 통합



28
왝! employeeOptional.isPresent()옵션 유형의 요점이 완전히 누락 된 것 같습니다. @MikePartridge의 의견에 따르면 권장하지 않거나 관용적이지 않습니다. 명시 적으로 옵션 유형인지 확인 또는 아무것도 것은 노 전혀 없다. 당신은 단순히 map또는 flatMap그들 위에 있어야 합니다.
Andres F.

7
참조 스택 오버플로가 에 대한 답변 에 의해 브라이언 게츠자바 언어 건축가 오라클. Optional
Basil Bourque

6
이것은 "모범 사례"라는 이름으로 두뇌를 끌 때 일어나는 일입니다.
immibis 2012 년

9
그것은 옵션의 사용이 상당히 좋지 않지만 이점이 있습니다. 널 (null)을 사용하면 모든 것이 널 (null) 수 있으므로 계속 확인해야합니다. 선택적으로이 특정 변수가 누락되었을 가능성이 있음을 나타냅니다. 확인하십시오.
Richard Tingle

답변:


108

스타일 2는 Java 8을 충분히 활용하지 못할 것입니다. 당신은 전혀 원하지 않습니다 if ... use. Oracle의 예제를 참조하십시오 . 우리는 그들의 조언을 받아 :

스타일 3

// Changed EmployeeServive to return an optional, no more nulls!
Optional<Employee> employee = employeeServive.getEmployee();
employee.ifPresent(e -> System.out.println(e.getId()));

또는 더 긴 스 니펫

Optional<Employee> employee = employeeServive.getEmployee();
// Sometimes an Employee has forgotten to write an up-to-date timesheet
Optional<Timesheet> timesheet = employee.flatMap(Employee::askForCurrentTimesheet); 
// We don't want to do the heavyweight action of creating a new estimate if it will just be discarded
client.bill(timesheet.orElseGet(EstimatedTimesheet::new));

19
실제로 우리는 isPresent (코드 검토에 의해 잡히는)
jk

10
@jk. 과부하이 있다면 실제로, void ifPresent(Consumer<T>, Runnable)나는의 전면 금지에 대한 주장 isPresentget
Caleth

10
@ Caleth : Java 9에 관심이있을 수 있습니다 ifPresentOrElse(Consumer<? super T>, Runnable).
wchargin

9
Java 6 스타일과 비교 하여이 Java 8 스타일의 한 가지 단점을 발견했습니다. 코드 범위 도구를 바보입니다. if 문을 사용하면 여기가 아닌 지점을 놓쳤을 때 표시됩니다.
Thierry

14
@Aaron은 "코드 가독성을 크게 줄입니다". 가독성을 정확히 감소시키는 부분은 무엇입니까? 객관적인 추론과는 달리 "나는 항상 다르게 해왔고 습관을 바꾸고 싶지 않다"고 들린다.
Voo

47

당신이 사용하는 경우 Optional여전히 반환 할 수 있습니다 이전 API 사이의 "호환성"층으로 null, 당신이있어 최신 단계에서 선택적 요소 (비어)를 생성하는 것이 도움이 될 수 있는지 당신이 뭔가를 가지고있다. 예를 들어, 당신이 쓴 곳 :

Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
    Employee employeeOptional= employeeOptional.get();
    System.out.println(employee.getId());
}

나는 다음을 선택했다.

Optional.of(employeeService)                 // definitely have the service
        .map(EmployeeService::getEmployee)   // getEmployee() might return null
        .map(Employee::getId)                // get ID from employee if there is one
        .ifPresent(System.out::println);     // and if there is an ID, print it

요점은 직원이 아닌 직원 서비스 가 있다는 것을 알고 있으므로 with로 마무리 할 수 ​​있다는 것 입니다 . 그런 다음 전화를 걸면 직원을 얻거나 얻지 못할 수 있습니다. 그 직원은 신분증을 가지고있을 수도 있습니다. 그런 다음 ID로 끝나면 인쇄하려고합니다.OptionalOptional.of()getEmployee()

명시 적으로 확인 할 필요가 없습니다 어떤 이 코드 등을 널 (null), 존재는.


4
(...).ifPresent(aMethod)over 를 사용하면 어떤 이점이 if((...).isPresent()) { aMethod(...); }있습니까? 거의 동일한 작업을 수행하는 또 다른 구문 인 것 같습니다.
Ruslan

2
@Ruslan 왜 모든 경우에 똑같이 잘 작동하는 일반적인 솔루션이있을 때 특정 호출마다 하나의 특수 구문을 사용합니까? 우리는 여기서 map과 co에서 같은 아이디어를 적용하고 있습니다.
Voo

1
@Ruslan ... API는 빈 컬렉션이나 배열 대신 null을 반환합니다. : 예, 당신은 같은 (아이가 존재하지 않는 경우 getChildren ()는 어린이 세트, 또는 null을 반환하는 부모 클래스 가정) 할 수있는 Set<Children> children = Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet); 지금이 처리 할 수있는 children, 예를 들어, 균일를 children.forEach(relative::sendGift);. 그것들은 결합 될 수도 있습니다 : Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet).forEach(relative::sendGift);. null 점검 등 모든 보일러 플레이트
Joshua Taylor

1
@Ruslan ...은 표준 라이브러리에서 시도되고 진정한 코드로 위임되었습니다. 프로그래머로서 필자가 작성, 테스트, 디버깅해야하는 코드의 양이 줄어 듭니다.
Joshua Taylor

1
Swift를 사용하고 있습니다. employee = employeeService.employee 인 경우 {print (employee.Id); }
gnasher729

23

Optional단일 값 을 갖는 추가 값이 없습니다 . 보시다시피, 그것은 null수표를 존재 여부 확인으로 대체합니다 .

거대한 가진 부가가치 Collection같은 것들입니다. 구식 저수준 루프를 대체하기 위해 도입 된 모든 스트리밍 방법은 Optional사물을 이해 하고 그에 대한 올바른 일을 수행합니다 (처리하지 않거나 궁극적으로 다른 unset 반환 Optional). 개별 항목보다 n 개의 항목을 더 자주 처리하고 (실제 프로그램의 99 %가 수행하는 경우) 선택적으로 존재하는 항목을 기본적으로 지원하는 것이 크게 개선되었습니다. 이상적인 경우에는 확인 null 하거나 존재 하지 않아도됩니다 .


24
Collections / streams 외에도, Optional은 API를보다 스스로 문서화 할 수있게 해줍니다 ( 예 : Employee getEmployee()vs. Optional<Employee> getEmployee()기술적으로 둘 다 돌아올 수 있지만 null그 중 하나가 문제를 일으킬 가능성이 훨씬 높습니다.
amon

16
단일 값의 옵션에는 많은 가치가 있습니다. .map()도중에 null을 확인하지 않고도 할 수 있는 것이 좋습니다. 예, Optional.of(service).map(Service::getEmployee).map(Employee::getId).ifPresent(this::submitIdForReview);.
Joshua Taylor

9
부가 가치가 없다는 귀하의 초기 의견에 동의하지 않습니다. 선택 사항은 코드에 대한 컨텍스트를 제공합니다. 한 눈에 기능의 정확성을 바로 잡을 수 있으며 모든 경우에 적용됩니다. null 검사와는 달리 모든 단일 메소드에서 모든 단일 참조 유형을 null 검사해야합니까? 유사한 사례가 클래스 및 공개 / 개인 수정 자에도 적용될 수 있습니다. 그들은 코드에 0의 가치를 더합니다.
ArTs

3
@JoshuaTaylor 솔직히, 표현 과 비교할 때 , 나는 구식 검사를 선호합니다 null.
Kilian Foth

2
단일 값으로도 Optional의 또 다른 큰 장점은 수행해야 할 때 null을 확인하는 것을 잊을 수 없다는 것입니다. 그렇지 않으면 확인을 잊어 버리고 NullPointerException으로 끝날 수 있습니다 ...
Sean Burton

17

Optional대한 멋진 API처럼 사용 하는 한, 예만 isNotNull()확인하면 아무런 차이가 없습니다 null.

당신은 사용하지 말아야 할 것들 Optional에 대한

Optional값이 있는지 확인하는 데만 사용 하는 것은 Bad Code ™입니다.

// BAD CODE ™ --  just check getEmployee() != null  
Optional<Employee> employeeOptional =  Optional.ofNullable(employeeService.getEmployee());  
if(employeeOptional.isPresent()) {  
    Employee employee = employeeOptional.get();  
    System.out.println(employee.getId());
}  

당신이 사용해야하는 것들 Optional에 대한

널 리턴 API 메소드를 사용할 때 필요할 때 값을 생성하여 존재하지 않는 값을 피하십시오.

Employee employee = Optional.ofNullable(employeeService.getEmployee())
                            .orElseGet(Employee::new);
System.out.println(employee.getId());

또는 새 직원을 만드는 데 비용이 많이 드는 경우 :

Optional<Employee> employee = Optional.ofNullable(employeeService.getEmployee());
System.out.println(employee.map(Employee::getId).orElse("No employee found"));

또한 메소드가 값을 리턴하지 않을 수 있음을 모두에게 알리십시오 (어떤 이유로 든 위와 같은 기본값을 리턴 할 수없는 경우).

// Your code without Optional
public Employee getEmployee() {
    return someCondition ? null : someEmployee;
}
// Someone else's code
Employee employee = getEmployee(); // compiler doesn't complain
// employee.get...() -> NPE awaiting to happen, devs criticizing your code

// Your code with Optional
public Optional<Employee> getEmployee() {
    return someCondition ? Optional.empty() : Optional.of(someEmployee);
}
// Someone else's code
Employee employee = getEmployee(); // compiler complains about incompatible types
// Now devs either declare Optional<Employee>, or just do employee = getEmployee().get(), but do so consciously -- not your fault.

사람들은 정말로 비록 그리고 마지막으로, 이미 다른 답변에서 설명 된 모든 스트림과 같은 방법을 거기에 당신이 사용하기로 결정 Optional하지만 사용하고 Optional다른 사람에 의해 제공합니다.


1
@Kapep 과연. 우리는 / R / 자바에서이 토론을 통해 있었는데, 나는 말했다 : «내가 볼 Optional기회를 놓치는한다. "여기서이 새로운 Optional것을 만들어서 null 값을 확인하도록해야합니다. 아, 그런데 ... get()방법을 추가해서 실제로 아무것도 확인하지 않아도됩니다;);)" . (...) Optional는 "THIS MIGHT BE NULL"네온 사인으로 좋지만, 그 get()방법 이 거의 없기 때문에» . 그러나 OP는 Optional반대 이유나 설계 결함이 아닌 사용 이유를 묻고있었습니다 .
walen

1
Employee employee = Optional.ofNullable(employeeService.getEmployee());세 번째 스 니펫에서 유형이 Optional<Employee>?
Charlie Harding

2
@immibis 어떤 방법으로 절름발이? 사용하는 주된 이유 get는 레거시 코드와 상호 작용해야하는 경우입니다. 나는 새로운 코드에서 사용할 많은 유효한 이유를 생각할 수 없다 get. 그것은 레거시 코드와 상호 작용할 때 대안이 없지만 종종 walen은 get초보자가 잘못된 코드를 작성하게 한다는 점을 지적했습니다 .
Voo

1
@immibis 나는 Map한 번 언급하지 않았으며 , 이전 버전과 호환되지 않는 극적인 변경을하는 사람을 알지 못하므로 의도적으로 나쁜 API를 의도적으로 설계 할 수 Optional있습니다. 은 Optional일부 감각을 만들 것입니다 tryGet하지만 방법.
Voo

2
@Voo Map는 모두에게 친숙한 예입니다. 물론 Map.get이전 버전과의 호환성으로 인해 Optional 을 반환 할 수는 없지만 호환성 제약이없는 다른 Map과 같은 클래스를 쉽게 상상할 수 있습니다. 말해봐 class UserRepository {Optional<User> getUserDetails(int userID) {...}}. 이제 로그인 한 사용자의 세부 사항을 가져 오려고합니다. 사용자가 로그인 관리하고 시스템이 사용자를 삭제하지 않기 때문에 해당 정보가 존재한다는 것을 알고 있습니다. 그렇습니다 theUserRepository.getUserDetails(loggedInUserID).get().
immibis

12

Optional을 사용할 때의 핵심은 개발자가 함수 반환 값을 확인 해야하는지 여부를 명확하게 유지하는 것입니다.

//service 1
Optional<Employee> employeeOptional = employeeService.getEmployee();
if(employeeOptional.isPresent()){
    Employee employeeOptional= employeeOptional.get();
    System.out.println(employee.getId());
}

//service 2
Employee employee = employeeService.getEmployeeXTPO();
System.out.println(employee.getId());

8
옵션이있는 멋진 기능이 있지만이 점보다 중요하다고 생각합니다. Java 8 이전에는 호출에서 응답을 널 검사해야하는지 알기 위해 Javadoc을 파헤쳐 야했습니다. Java 8을 게시하면 "아무것도"얻을 수 있는지 여부를 반환 유형에서 알 수 있습니다.
Buhb

3
글쎄, 여전히 널이 null을 반환 할 수있는 "오래된 코드"문제가 있습니다 ...하지만 서명에서 알 수있는 것은 훌륭합니다.
Haakon Løtveit

5
예, 선택적 <T>가 값이 누락 될 수 있음을 나타내는 유일한 방법 , 즉 null ptr이없는 언어에서 더 잘 작동합니다 .
dureuill

8

가장 큰 실수는 여전히 더 절차적인 용어로 생각하고 있다는 것입니다. 이것은 사람으로서 당신을 비판하는 것이 아니라 단지 관찰 일뿐입니다. 보다 기능적인 용어로 생각하는 것은 시간과 연습이 필요하기 때문에 메소드가 현재 존재하고 가장 분명한 것처럼 보일 것입니다. 이차적 인 사소한 실수는 메소드 내부에서 Optional을 생성하는 것입니다. 선택 사항은 어떤 것이 값을 반환하거나 반환하지 않을 수 있음을 문서화하는 데 도움이됩니다. 아무것도 얻지 못할 수도 있습니다.

이것은 당신이 완벽하게 합리적으로 보이는 완벽하게 읽을 수있는 코드를 작성하도록 이끌었지만, 당신은 get and isPresent 인 사악한 쌍둥이 유혹에 유혹을 받았습니다.

물론 문제는 빨리 "현재 존재하는 이유는 무엇입니까?"

많은 사람들이 여기서 놓친 것은 isPresent ()는 gosh-darn 유용한 람다가 얼마나 기능적이며 기능적인 것을 좋아하는 사람들이 완전히 탑승 한 사람들이 작성한 새로운 코드를 위해 만들어진 것이 아니라는 것입니다.

그러나 그것은 우리에게 몇 가지 좋은, 위대하고 매력적인 (?) 이점을 제공합니다.

  • 새로운 기능을 사용하기 위해 레거시 코드를 쉽게 전환 할 수 있습니다.
  • Optional의 학습 곡선을 완화합니다.

첫 번째는 다소 간단합니다.

다음과 같은 API가 있다고 상상해보십시오.

public interface SnickersCounter {
  /** 
   * Provides a proper count of how many snickers have been consumed in total.
   */
  public SnickersCount howManySnickersHaveBeenEaten();

  /**
    * returns the last snickers eaten.<br>
    * If no snickers have been eaten null is returned for contrived reasons.
    */
  public Snickers lastConsumedSnickers();
}

그리고 당신은 이것을 빈칸으로 채우는 레거시 클래스를 가졌습니다.

Snickers lastSnickers = snickersCounter.lastConsumedSnickers();
if(null == lastSnickers) {
  throw new NoSuchSnickersException();
}
else {
  consumer.giveDiabetes(lastSnickers);
}

확실한 예. 그러나 여기서 나와 함께하십시오.

이제 Java 8이 출시되었으며 우리는 탑승을 시작했습니다. 따라서 우리가하는 일 중 하나는 이전 인터페이스를 Optional을 반환하는 것으로 교체하고 싶다는 것입니다. 왜? 다른 사람이 이미 은혜롭게 언급 한 것처럼 : 이것은 무언가가 null 일 수 있는지의 여부를 추측합니다 . 이것은 이미 다른 사람들에 의해 지적되었습니다. 그러나 지금 우리는 문제가 있습니다. 우리가 (무고한 방법으로 alt + F7을 치는 동안 실례합니다), 그렇지 않으면 훌륭한 일을하는 잘 테스트 된 레거시 코드 에서이 방법이 호출되는 곳을 상상해보십시오. 이제이 모든 것을 업데이트해야합니다.

이 곳은 isPresent가 빛나는 곳입니다.

지금 : Snickers lastSnickers = snickersCounter.lastConsumedSnickers (); if (null == lastSnickers) {throw new NoSuchSnickersException (); } else {consumer.giveDiabetes (lastSnickers); }

된다 :

Optional<Snickers> lastSnickers = snickersCounter.lastConsumedSnickers();
if(!lastSnickers.isPresent()) {
  throw new NoSuchSnickersException();
}
else {
  consumer.giveDiabetes(lastSnickers.get());
}

그리고 이것은 당신이 새로운 후배에게 줄 수있는 간단한 변화입니다 : 그는 유용한 무언가를 할 수 있고, 동시에 코드베이스를 탐험하게 될 것입니다. 상생. 결국이 패턴과 비슷한 것이 널리 퍼져 있습니다. 그리고 이제 람다 등을 사용하기 위해 코드를 다시 작성할 필요가 없습니다. (이 특별한 경우에는 사소한 일이지만 독자에게 연습하기가 어려운 예를 생각해 봅니다.)

이는 사용자가 수행 한 방식이 본질적으로 값 비싼 재 작성없이 레거시 코드를 처리하는 방법이라는 것을 의미합니다. 새 코드는 어떻습니까?

글쎄, 당신의 경우에, 당신이 무언가를 인쇄하고 싶을 때, 당신은 간단하게 할 것입니다 :

snickersCounter.lastConsumedSnickers (). ifPresent (System.out :: println);

아주 간단하고 완벽합니다. 그러면 표면까지 천천히 버블 링되는 지점은 get () 및 isPresent ()에 대한 사용 사례가 있다는 것입니다. 새로운 코드를 너무 많이 생각하지 않고도 기존 코드를 기계적으로 수정하여 최신 유형을 사용할 수 있습니다. 따라서 수행중인 작업은 다음과 같은 방식으로 잘못 안내됩니다.

  • null을 반환 할 수있는 메서드를 호출하고 있습니다. 올바른 아이디어는 메소드가 널을 리턴한다는 것입니다.
  • 람다 환상을 포함하는 맛있는 새로운 방법을 사용하는 대신 레거시 반창고 방법을 사용하여이 옵션을 처리합니다.

Optional을 간단한 null 안전 검사로 사용하려면 다음을 수행해야합니다.

new Optional.ofNullable(employeeServive.getEmployee())
    .map(Employee::getId)
    .ifPresent(System.out::println);

물론 이것의 좋은 버전은 다음과 같습니다.

employeeService.getEmployee()
    .map(Employee::getId)
    .ifPresent(System.out::println);

그건 그렇고, 꼭 필요한 것은 아니지만 작업마다 새로운 줄을 사용하는 것이 더 쉽기 때문에 읽기 쉽습니다. 읽기 쉽고 이해하기 쉬운 요일 간결함.

이것은 물론 우리가하려는 모든 것을 이해하기 쉬운 매우 간단한 예입니다. 실생활에서 항상 간단한 것은 아닙니다. 그러나이 예에서 우리가 표현하고있는 것이 우리의 의도라는 점을 주목하십시오. 우리는 직원에게 GID와 ID를 부여하고 가능하면 인쇄하고 싶습니다. 이것은 Optional의 두 번째 큰 승리입니다. 보다 명확한 코드를 만들 수 있습니다. 또한 맵에 피드를 제공 할 수 있도록 많은 것들을 수행하는 메소드를 만드는 것과 같은 일을하는 것이 일반적으로 좋은 생각이라고 생각합니다.


1
여기서 문제는 이러한 기능 버전을 이전 버전과 마찬가지로 읽을 수있는 것처럼 들리게하는 경우가 있는데, 어떤 경우에는 예제가 "완전히 명확하다"고 말하는 경우도 있습니다. 그렇지 않습니다. 그 예는 분명하지만 더 많은 생각이 필요하기 때문에 완벽 하지는 않습니다 . map(x).ifPresent(f)단순히 같은 종류의 직관적 인 의미가 if(o != null) f(x)없습니다. if버전은 완벽하게 분명하다. 이것은 사람들이 인기있는 밴드 왜건을 뛰어 넘는 또 다른 경우이며 실제 진행 상황이 아닙니다 .
Aaron

1
함수형 프로그래밍이나을 사용하면 이점이 전혀 없다고 말하는 것은 아닙니다 Optional. 나는이 특정 경우에 선택적 사용만을 언급하고 있으며, 따라서 여러 사람들 이이 형식과 같거나 더 읽기 쉬운 것처럼 행동하기 때문에 가독성을 참조하십시오 if.
Aaron

1
그것은 우리의 명확성에 달려 있습니다. 당신이 아주 좋은 지적을하는 동안, 나는 또한 많은 사람들에게 람다는 어떤 시점에서 새롭고 이상하다고 지적하고 싶습니다. 나는 꽤 소수의 사람들이 isPresent라고 말하고 유쾌한 구호를 느끼는 인터넷 쿠키를 베팅 할 것입니다. 이제 레거시 코드를 업데이트하고 나중에 람다 구문을 배울 수 있습니다. 반면에 Lisp, Haskell 또는 이와 유사한 언어 경험을 가진 사람들은 아마도 자바에서 자신의 문제에 친숙한 패턴을 적용 할 수 있다는 사실에 기뻐했을 것입니다.
Haakon Løtveit

@Aaron-명확성은 선택적 유형의 요점이 아닙니다. 나는 개인적으로 그들이하지 않는 것을 발견 감소 선명도, 어떤 경우는 (아니이 경우이기는하지만)가 그들이 그것을 증가 할 경우,하지만 장점은 (당신이 사용하지 않는만큼 즉 isPresentget그들이 현실적으로 가능한 한 많이) 안전을 향상시킵니다 . 형식이 Optional<Whatever>nullable을 사용하는 것이 아니라면 값이 없는지 확인하지 못하는 잘못된 코드를 작성하는 것이 더 어렵습니다 Whatever.
Jules

사용하지 않는 한get 즉시 값을 꺼내더라도 결 측값이 발생할 때 수행 할 작업을 선택해야합니다. 기본값을 제공하거나 선택한 값 을 던지 려면 orElse()(또는 orElseGet())를 사용합니다. 예외의 문제를 문서화하는 예외 는 스택 추적을 파헤칠 때까지 의미가없는 일반 NPE보다 낫습니다. orElseThrow()
Jules

0

Style 2에는 이점이 없습니다. 여전히 null 확인이 필요하다는 것을 알아야합니다. 이제는 더 크므로 읽을 수 없습니다.

employeeServive.getEmployee ()가 Optional을 반환하면 코드는 다음과 같이됩니다.

Optional<Employee> employeeOptional = employeeServive.getEmployee();
if(employeeOptional.isPresent()){
    Employee employee= employeeOptional.get();
    System.out.println(employee.getId());
}

이렇게하면 employeeServive.getEmployee (). getId ()를 호출 할 수 없으며 어떻게 든 선택적 valoue를 처리해야합니다. 그리고 전체 코드베이스 메소드에서 null을 반환하지 않거나 팀에 예외 가이 규칙에 대해 거의 알려지지 않은 경우) NPE에 대한 안전성이 향상됩니다.


12
옵션은 사용하는 순간 무의미한 합병증이됩니다 isPresent(). 옵션을 사용하므로 테스트 할 필요가 없습니다.
candied_orange

2
다른 사람들이 말했듯이 사용하지 마십시오 isPresent(). 이것은 선택 사항에 대한 잘못된 접근 방식입니다. map또는 flatMap대신 사용하십시오 .
Andres F.

1
@CandiedOrange 그리고 가장 인기있는 답변 (사용 ifPresent)은 테스트하는 또 다른 방법입니다.
immibis 2018 년

1
@CandiedOrange ifPresent(x)는만큼 테스트 if(present) {x}입니다. 반환 값은 관련이 없습니다.
immibis 2012 년

1
@CandiedOrange 그럼에도 불구하고, 당신은 원래 "우리는 테스트 할 필요가 없다"고 말했다. 실제로 테스트 중일 때는 "테스트 할 필요가 없습니다"라고 말할 수 없습니다.
Aaron

0

나는 가장 큰 장점은 당신의 방법 서명이를 반환하는 경우이다 생각 Employee없는 널 (null)을 확인합니다. 그 서명으로 직원을 돌려주는 것이 보장된다는 것을 알고 있습니다. 장애 사례를 처리 할 필요가 없습니다. 옵션으로 당신은 알고 있습니다.

내가 보았던 코드에는 사람들이 null을 사용할 수 있는지 여부를 결정하기 위해 코드를 추적하지 않기 때문에 결코 실패하지 않는 null 검사가 있다는 것을 알았습니다. 이것은 코드를 조금 느리게하지만 더 중요한 것은 코드를 훨씬 더 시끄럽게 만듭니다.

그러나 이것이 작동하려면이 패턴을 일관되게 적용해야합니다.


1
이를 달성하는 가장 간단한 방법은 정적 분석을 사용 @Nullable하고 @ReturnValuesAreNonnullByDefault함께 사용하는 것입니다.
maaartinus

@maaartinus 데코레이터 주석에 정적 분석 및 문서 형태로 툴링을 추가하면 컴파일 시간 API 지원 이 더 간단 합니까?
Sled

코드 변경이 필요하지 않기 때문에 툴링을 추가하는 것이 더 간단합니다. 또한 다른 버그도 발견하므로 어쨌든 갖고 싶습니다. +++ 나는 추가 사소한 스크립트 작성 package-info.java@ReturnValuesAreNonnullByDefault내가 할 일은 추가하는 것입니다, 내 모든 패키지를 @Nullable당신이로 전환해야 할 곳 Optional. 이것은 적은 문자, 추가 된 쓰레기 및 런타임 오버 헤드가 없음을 의미합니다. 메소드를 가리킬 때 표시되는 문서를 찾을 필요가 없습니다. 정적 분석 도구는 실수와 이후의 Null 변경 가능성을 포착합니다.
maaartinus

가장 큰 관심사 Optional는 nullability를 처리하는 다른 방법을 추가한다는 것입니다. 처음부터 Java로 있었다면 괜찮을지 모르지만 이제는 고통입니다. 코 틀린 길을가는 것이 훨씬 더 좋습니다. +++ List<@Nullable String>여전히 문자열 목록 List<Optional<String>>이지만 그렇지는 않습니다.
maaartinus

0

내 대답은 : 그냥하지 마십시오. 적어도 그것이 실제로 개선되는지에 대해 적어도 두 번 생각하십시오.

다음과 같은 표현 (다른 답변에서 가져옴)

Optional.of(employeeService)                 // definitely have the service
        .map(EmployeeService::getEmployee)   // getEmployee() might return null
        .map(Employee::getId)                // get ID from employee if there is one
        .ifPresent(System.out::println);     // and if there is an ID, print it

원래 nullable 반환 메서드를 처리하는 올바른 기능적 방법 인 것 같습니다. 그것은 멋지게 보이고 결코 던져 지지 않으며 결코 "부재"사례를 처리하는 것에 대해 생각하게하지 않습니다.

예전 방식보다 낫다

Employee employee = employeeService.getEmployee();
if (employee != null) {
    ID id = employee.getId();
    if (id != null) {
        System.out.println(id);
    }
}

else절이 있을 수 있습니다 .

기능적인 스타일

  • 완전히 다른 것처럼 보임 (사용하지 않는 사람은 읽을 수 없음)
  • 디버깅이 어렵다
  • "결석"사례를 처리하기 위해 쉽게 확장 할 수 없음 ( orElse항상 충분하지는 않음)
  • "결석"사례를 무시하도록 오도

어떤 경우에도 만병 통치약으로 사용해서는 안됩니다. 그것이 보편적으로 적용 가능하다면, .오퍼레이터의 시맨틱은 ?.오퍼레이터 의 시맨틱으로 대체되어야 하고, NPE는 잊혀지고 모든 문제는 무시된다.


큰 자바 팬이지만 기능적 스타일은 그저 자바 사람이 그 진술을 대신하는 것이라고 말합니다.

employeeService
.getEmployee()
?.getId()
?.apply(id => System.out.println(id));

다른 언어에서 잘 알려져 있습니다. 우리는이 진술에 동의합니까?

  • (실제로) 기능하지 않습니까?
  • 구식 코드와 매우 유사합니다. 훨씬 간단합니까?
  • 기능적 스타일보다 훨씬 더 읽기 쉬운가?
  • 디버거 친화적입니까?

핵심 라이브러리 클래스를 사용한 Java 구현 과는 완전히 다른 언어 기능을 사용하여이 개념 의 C # 구현 을 설명하는 것 같습니다 . 그들은 나에게
똑같아 보인다

@Caleth 안돼. 우리는 "무작위 안전 평가를 단순화하는 개념"에 대해 말할 수는 있지만이를 "개념"이라고 부르지는 않습니다. 우리는 자바가 핵심 라이브러리 클래스를 사용하여 동일한 것을 구현한다고 말할 수 있지만, 그것은 엉망입니다. employeeService.getEmployee().getId().apply(id => System.out.println(id));안전 탐색 연산자를 사용하여 한 번 및을 사용하여 한 번 시작하여 null 안전하도록 설정하십시오 Optional. 물론이를 "구현 세부 사항"이라고 할 수 있지만 구현이 중요합니다. 라마가 코드를 더 간단하고 멋지게 만드는 경우가 있지만 여기서는 추악한 학대입니다.
maaartinus

라이브러리 클래스 : Optional.of(employeeService).map(EmployeeService::getEmployee).map(Employee::getId).ifPresent(System.out::println);언어 기능 : employeeService.getEmployee()?.getId()?.apply(id => System.out.println(id));. 두 가지 구문이지만 결국 나와 비슷하게 보입니다 . "개념"은 Optional일명 Nullable일명Maybe
Caleth

나는 왜 당신이 그 중 하나가 "추악한 학대"라고 생각하는지 이해하지 못합니다. 나는 생각을 이해할 수 모두 "추악한 남용"또는 둘 다 좋은.
Caleth

@Caleth 한 가지 차이점은 두 개의 물음표를 추가하는 대신 모든 물음표를 다시 작성해야한다는 것입니다. 문제는 언어 기능과 라이브러리 클래스 코드가 많이 다르다는 것이 아닙니다. 괜찮아. 문제는 원래 코드와 라이브러리 클래스 코드가 많이 다르다는 것입니다. 같은 생각, 다른 코드. 유감 이네요. +++학대당하는 것은 무효를 처리하기위한 라마입니다. 람다는 괜찮지 만 Optional그렇지 않습니다. Nullability는 Kotlin과 같은 적절한 언어 지원이 필요합니다. 또 다른 문제 List<Optional<String>>는와 관련이 없으며 관련이 List<String>필요합니다.
maaartinus

0

선택적은 함수형 프로그래밍에서 나오는 개념 (고차 형)입니다. 그것을 사용하면 중첩 된 null 검사가 없어지고 피라미드의 운명에서 벗어날 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.