Java 8 게터가 선택적 유형을 반환해야합니까?


288

Optional Java 8에 도입 된 type은 많은 개발자에게 새로운 것입니다.

Optional<Foo>클래식 대신 getter 메소드가 유형을 리턴 Foo하는 것이 좋은 습관입니까? 값이 일 수 있다고 가정합니다 null.


8
이것은 의견이있는 답변을 유치 할 수 있지만 좋은 질문입니다. 나는 주제에 대한 실제 사실에 대한 답변을 기대하고 있습니다.
Justin

8
문제는 nullablitity를 피할 수 없는지 여부입니다. 구성 요소에는 null이 허용되는 속성이있을 수 있지만 해당 구성 요소를 사용하는 프로그래머는 해당 속성을 엄격하게 유지하지 않기로 결정할 수 있습니다 null. 따라서 프로그래머는 Optional그때 를 다룰 필요가 없습니다 . 즉, null실제로 검색 결과와 같은 값이 없음을 나타내 Optional거나 ( null가능한 경우) 가능한 값 집합의 한 구성원 일뿐입니다.
Holger

1
@NotNull주석 에 대한 토론도 참조하십시오 : stackoverflow.com/q/4963300/873282
koppor

답변:


516

물론 사람들은 원하는 것을 할 것입니다. 그러나 우리는이 기능을 추가 할 때 분명한 의도를 가지고 있었으며 , 많은 사람들이 우리를 그렇게 좋아했을 것 같은 일반적인 목적은 아마 유형 이 아니 었습니다. 우리의 의도는 "결과 없음"을 나타내는 명확한 방법이 필요했던 라이브러리 메소드 리턴 유형에 대해 제한된 메커니즘을 제공하는 것이었고,이를 사용 null하는 것은 압도적으로 오류를 일으킬 가능성이있었습니다.

예를 들어, 결과 배열 또는 결과 목록을 리턴하는 무언가에는 사용해서는 안됩니다. 대신 빈 배열 또는 목록을 반환하십시오. 거의 필드 나 메소드 매개 변수의 필드로 사용해서는 안됩니다.

게터에 대한 반환 값으로 일상적으로 사용하는 것이 과용이라고 생각합니다.

Optional은 피해야한다는 것은 잘못된 일이 아닙니다. 많은 사람들이 원하는 것이 아니기 때문에 열성적인 과도한 사용의 위험에 대해 상당히 우려하고있었습니다.

(공공 서비스 발표 : null이 아니라는 것을 증명할 수 없으면 전화 하지 마십시오Optional.get . 대신 orElse또는 같은 안전한 방법 중 하나를 사용하십시오 ifPresent. 돌이켜 보면 이와 get비슷한 것 getOrElseThrowNoSuchElementException또는 매우 위험한 방법 이라는 것을 명확하게 한 것을 호출해야합니다 즉, 전체 목적을 훼손 Optional처음에 레슨을 배웠습니다.. (UPDATE : 자바 (10)가 Optional.orElseThrow()의미 적으로 동등하다, get().,하지만 이름이 더 적절하다))


24
(마지막 부분에 대해서) ... 그리고 우리는 값이 결코 없다는 것을 확신 할 때 null우리가 사용할 수있는 orElseThrow(AssertionError::new), 에헴 또는 orElseThrow(NullPointerException::new)...
홀거

25
범용 어쩌면 또는 어떤 유형을 소개 하려는 의도 있다면 어떻게 다르게 했습니까? Optional이 하나의 청구서에 맞지 않는 방법이 있습니까, 아니면 새로운 API 전체에 Optionals를 도입하면 Java가 아닌 것입니까?
David Moles 2012

22
"general-purpose type"이라는 용어는 대략적인 라이브러리 클래스를 제공하는 것이 아니라 언어 유형 시스템에 빌드하는 것을 의미했습니다. (일부 언어에는 T? (T 또는 null) 및 T! (널 (nullable T)) 유형이 있습니다. 선택은 클래스 일뿐입니다. 언어 지원과 함께 Foo와 Optional <Foo>를 암시 적으로 변환 할 수 없습니다.
Brian Goetz

11
Java 세계에서 강조하는 것이 왜 선택적이며 더 나은 정적 분석이 아닌지 잠시 동안 궁금했습니다. 옵션은 몇 가지 장점이 있지만 null이전 버전과의 호환성은 큰 장점입니다 . Map::get반환 널 (NULL) V이 아닌 Optional<V>, 결코 변화 않을거야. @Nullable그래도 쉽게 주석을 달 수있었습니다 . 이제 우리는이 두 가지 에 있어야 정말 나쁜 위치처럼 보인다 일어나는 정적 분석을 얻을의 가치 부족-플러스 적은 인센티브를 표현하는 방법을.
yshavit

21
나는 그것을 속성의 반환 값으로 사용하는 것이 여기 에서이 답변을 읽을 때까지는 의도하지 않은 것을 알지 못했습니다. 사실, 나는 오라클 웹 사이트 ( oracle.com/technetwork/articles/java/) 에서이 기사를 읽은 후 이것이 의도 한 바라고 믿게되었다. 설명해 주셔서 감사합니다.
Jason Thompson

73

내 자신의 약간의 연구를 한 후, 나는 이것이 적절한시기를 제안 할 수있는 많은 것들을 발견했습니다. 가장 권위있는 것은 오라클 기사에서 다음 인용문입니다.

"선택적 클래스의 의도는 모든 단일 널 참조를 대체하는 것이 아니라는 점에 주목해야합니다 . 대신, 그 목적은 메소드의 서명 만 읽음으로써 보다 이해하기 쉬운 API를 디자인하는 데 도움을주는 것입니다. "선택적인 값을 기대할 수 있습니다. 이렇게하면 값이없는 경우 처리 할 수 ​​있도록 선택적으로 포장을 풀어야합니다." - 널 포인터 예외에 지치셨습니까? Java SE 8의 선택적 사용을 고려하십시오!

또한 Java 8 Optional 에서 발췌 한 내용을 찾았 습니다. 사용 방법

"선택 사항은 우리에게 아무것도 사지 않기 때문에 이러한 맥락에서 사용되도록 의도되지 않았습니다 :

  • 도메인 모델 계층에서 (직렬화 할 수 없음)
  • DTO에서 (같은 이유)
  • 메소드의 입력 매개 변수
  • 생성자 매개 변수에서 "

어떤 유효한 포인트를 올리는 것 같습니다.

나는 Optional피해야 할 것을 암시하는 부정적인 의미 나 적신호를 찾을 수 없었다 . 일반적인 아이디어는 API의 유용성 또는 유용성을 향상시키는 경우 사용하는 것입니다.


11
stackoverflow.com/questions/25693309는 - 그것은 잭슨, 이미 그것을 지원하지 않습니다 그래서 "직렬화하지가"더 이상 : 유효한 이유로 통과 보인다
Vlasec

1
Optional메소드의 입력 매개 변수 (특히 생성자)를 "우리에게 아무것도 사지 않을 것"의 입력 매개 변수로 사용 하는 이유에 대한 답변을 제시하는 것이 좋습니다 . dolszewski.com/java/java-8-optional-use-cases 에 좋은 설명이 있습니다.
길리

다른 API로 변환해야하는 선택적 결과를 커리려면 선택적 매개 변수가 필요하다는 것을 알았습니다. 결과는 상당히 이해하기 쉬운 API입니다. 참조 stackoverflow.com/a/31923105/105870
칼에게 이교도

1
링크가 끊어졌습니다. 답변을 업데이트 해 주시겠습니까?
softarn

2
문제는 개발자의 최선의 의도에도 불구하고 종종 API를 개선하지 않는다는 것입니다. 나는 프로덕션 코드에서 가져온 Optional나쁜 사용법에 대한 컬렉션 예제를 보았습니다 .
MiguelMunoz

20

나는 일반적으로 nullable 일 수있는 반환 값에 선택적 유형을 사용하는 것이 좋습니다. 그러나 프레임 워크를 사용하면 클래식 getter를 선택적 유형으로 바꾸면 getter 및 setter의 코딩 규칙에 의존하는 프레임 워크 (예 : Hibernate)로 작업 할 때 많은 문제가 발생한다고 가정합니다.


14
이 조언은 stackoverflow.com/a/26328555/3553087 에서 "열심히 남용 될 위험에 대해 우려하고 있습니다"라는 의미 입니다.
Brian Goetz

13

OptionalJava에 추가 된 이유 는 다음과 같습니다.

return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .findFirst()
    .getOrThrow(() -> new InternalError(...));

이보다 깨끗합니다.

Method matching =
    Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .getFirst();
if (matching == null)
  throw new InternalError("Enclosing method not found");
return matching;

내 요점은 Optional이 함수형 프로그래밍을 지원하기 위해 작성되었으며 동시에 Java에 추가되었다는 것입니다. (이 예제는 Brian Goetz블로그 에서 제공 한 것입니다 . orElse()이 코드는 예외를 발생 시키지만이 방법을 사용하면 더 좋은 예제를 사용할 수 있습니다 .)

그러나 이제 사람들은 매우 다른 이유로 Optional을 사용하고 있습니다. 그들은 언어 디자인의 결함을 해결하기 위해 그것을 사용하고 있습니다. 결함은 다음과 같습니다. 어떤 API 매개 변수 및 리턴 값이 널 (null)이 될 수 있는지 지정할 방법이 없습니다. javadocs에서 언급 될 수 있지만 대부분의 개발자는 코드에 대해 javadoc을 작성하지 않으며 작성하는 javadoc을 확인하는 사람은 많지 않습니다. 따라서 호출 스택에서 9-10 회 반복해서 이미 유효성이 검사 되었기 때문에 null 일 수없는 경우에도 사용하기 전에 항상 null 값을 확인하는 많은 코드가 생성됩니다.

새로운 Optional 클래스를 본 많은 사람들이 그 목적이 API에 명확성을 추가하는 것이라고 가정했기 때문에이 결함을 해결하기위한 갈증이 있다고 생각합니다. 사람들이 "게터가 옵션을 돌려줘야합니까?"와 같은 질문을하는 이유는 무엇입니까? 아닙니다. getter가 함수형 프로그래밍에 사용될 것으로 예상하지 않는 한 아마 그렇게해서는 안됩니다. 실제로 Java API에서 Optional이 사용되는 위치를 살펴보면 주로 기능 프로그래밍의 핵심 인 Stream 클래스에 있습니다. (매우 철저히 확인하지는 않았지만 Stream 클래스가 유일하게 사용되는 장소 일 수 있습니다 .)

약간의 기능적 코드에서 getter를 사용하려는 경우 표준 getter와 Optional을 리턴하는 두 번째 getter를 갖는 것이 좋습니다.

아, 그리고 클래스를 직렬화 할 수 있어야한다면 Optional을 절대 사용해서는 안됩니다.

옵션은 a) 매우 장황하고 b) 처음에는 해당 문제를 해결하려는 의도가 없었기 때문에 API 결함에 대한 매우 나쁜 해결책입니다.

API 결함에 대한 훨씬 더 나은 솔루션은 Nullness Checker 입니다. @Nullable로 주석을 달아 어떤 매개 변수와 반환 값이 null이 될 수 있는지 지정할 수있는 주석 처리기입니다. 이런 식으로 컴파일러는 코드를 스캔하고 실제로 null 일 수있는 값이 null이 허용되지 않는 값으로 전달되는지 알아낼 수 있습니다. 기본적으로 주석이 달린 경우를 제외하고는 null이 될 수 없다고 가정합니다. 이렇게하면 null 값에 대해 걱정할 필요가 없습니다. 널값을 매개 변수에 전달하면 컴파일러 오류가 발생합니다. 널이 될 수없는 널에 대해 오브젝트를 테스트하면 컴파일러 경고가 생성됩니다. 그 결과 NullPointerException이 런타임 오류에서 컴파일 타임 오류로 변경됩니다.

이것은 모든 것을 바꿉니다.

게터에 대해서는 옵션을 사용하지 마십시오. 그리고 멤버가 null이 될 수 없도록 클래스를 디자인하십시오. 그리고 Nullness Checker를 프로젝트에 추가하고 필요한 경우 getter 및 setter 매개 변수를 @Nullable 선언하십시오. 나는 새로운 프로젝트로만 이것을했습니다. null에 대한 많은 불필요한 테스트로 작성된 기존 프로젝트에서 많은 경고를 생성하므로 개조하기가 어려울 수 있습니다. 그러나 그것은 또한 많은 버그를 잡을 것입니다. 나는 그것을 좋아한다. 내 코드는 훨씬 깨끗하고 안정적입니다.

(이 문제를 해결하는 새로운 언어도 있습니다. Java 바이트 코드로 컴파일되는 Kotlin을 사용하면 객체를 선언 할 때 객체가 null인지 여부를 지정할 수 있습니다. 더 깔끔한 방법입니다.)

원본 게시물의 부록 (버전 2)

많은 생각을 한 후에 한 가지 조건에서 Optional을 반환하는 것이 허용된다는 결론에 마지 못해왔다. 사람들이 null을 반환 할 수없는 getter에서 Optional을 정기적으로 반환하는 많은 코드를 보았습니다. 나는 이것이 코드에 복잡성을 추가하여 버그를 일으킬 가능성이 매우 나쁜 코딩 관행이라고 생각합니다. 그러나 반환 된 값이 실제로 null 일 경우 계속해서 Optional 내부에 래핑하십시오.

함수형 프로그래밍을 위해 설계되었으며 함수 참조가 필요한 메소드는 두 가지 형식으로 작성되며 그 중 하나는 선택 사항을 사용합니다. 예를 들어, Optional.map()Optional.flatMap()모두 걸릴 함수 참조. 첫 번째는 일반적인 getter에 대한 참조를 가져오고 두 번째는 Optional을 반환하는 것을 가져옵니다. 따라서 값을 null로 설정할 수없는 Optional을 반환하여 호의를 베풀지 않습니다.

모든 것을 말했지만, 여전히 Nullness Checker 가 사용하는 접근 방식 이 NullPointerExceptions을 런타임 버그에서 컴파일 타임 오류로 전환하기 때문에 null을 처리하는 가장 좋은 방법이라고 생각합니다.


2
이 대답은 나에게 가장 적합한 것 같습니다. 선택 사항은 스트림이 추가 될 때 Java 8에서만 추가되었습니다. 그리고 내가 본 한 스트림 함수 만 옵션을 반환합니다.
Archit

1
이것이 가장 좋은 대답 중 하나라고 생각합니다. 사람들은 선택 사항과 작동 방식을 알고 있습니다. 그러나 가장 혼란스러운 부분은 사용 위치와 사용 방법입니다. 이것을 읽으면 많은 의심이 사라집니다.
ParagFlume

3

경우 당신이 현대 시리얼 이해하고 다른 프레임 워크 사용하고 Optional다음을 나는이 발견 지침을 작성할 때 잘 작동 Entity콩 및 도메인 레이어 :

  1. 직렬화 계층 (일반적으로 DB) 이 table의 null열에있는 셀 의 값을 허용하는 경우 getter 는 이 값이 합리적으로 null 일 것으로 예상되어이를 처리해야 함을 개발자에게 표시 할 수 있습니다 . DB가 값이 null이 아니라고 보장하면 getter는 이 값을로 감싸서 는 안됩니다 .BARFOOFoo.getBar()OptionalOptional
  2. Foo.bar해야 private하고 하지Optional. 그것이 사실 Optional이라면 그럴 이유가 없습니다 private.
  3. 세터 Foo.setBar(String bar)의 종류해야 bar하고 하지 Optional . null인수를 사용해도 괜찮다 면 JavaDoc 주석에이를 명시하십시오. 적절한 비즈니스 로직 을 사용 null하는 것이 좋지 않은 경우 IllegalArgumentExceptionIMHO가 더 적합합니다.
  4. 생성자는 Optional인수 가 필요하지 않습니다 (포인트 3과 유사한 이유로). 일반적으로 난 단지 생성자의 인수를 포함 해야한다 직렬화 데이터베이스에 null이있다.

위에서보다 효율적으로하려면 게터를 생성하고 템플릿을 해당에 대한 귀하의 IDE 템플릿을 편집 할 수 있습니다 toString(), equals(Obj o)(대부분의 IDE 발전기가 이미 널 (null) 처리)들을 위해 직접 등 또는 사용 필드.

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