[java] 인수에 Java 8의 Optional을 사용하지 않아야하는 이유

많은 웹 사이트에서 읽었습니다. Optional은 반환 유형으로 만 사용해야하며 메서드 인수에는 사용하지 않아야합니다. 나는 논리적 인 이유를 찾기 위해 고심하고 있습니다. 예를 들어 2 개의 선택적 매개 변수가있는 논리가 있습니다. 따라서 다음과 같이 메서드 서명을 작성하는 것이 좋습니다 (솔루션 1).

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {
    // my logic
}

많은 웹 페이지는 Optional을 메소드 인수로 사용해서는 안된다고 지정합니다. 이를 염두에두고 다음 메소드 서명을 사용하고 명확한 Javadoc 주석을 추가하여 인수가 null 일 수 있음을 지정하면 향후 관리자가 Javadoc을 읽고 인수를 사용하기 전에 항상 null 검사를 수행하기를 희망합니다 (솔루션 2) :

public int calculateSomething(String p1, BigDecimal p2) {
    // my logic
}

또는 더 나은 인터페이스를 제공하고 p1과 p2가 선택 사항이되도록 분명하게하기 위해 메서드를 4 개의 공용 메서드로 대체 할 수 있습니다 (솔루션 3).

public int calculateSomething() {
    calculateSomething(null, null);
}

public int calculateSomething(String p1) {
    calculateSomething(p1, null);
}

public int calculateSomething(BigDecimal p2) {
    calculateSomething(null, p2);
}

public int calculateSomething(String p1, BigDecimal p2) {
    // my logic
}

이제 각 접근 방식 마다이 논리를 호출하는 클래스 코드를 작성하려고합니다. 먼저 Optionals 를 반환하는 다른 객체에서 두 개의 입력 매개 변수를 검색 한 다음 invoke calculateSomething합니다. 따라서 솔루션 1을 사용하면 호출 코드는 다음과 같습니다.

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1, p2);

솔루션 2를 사용하는 경우 호출 코드는 다음과 같습니다.

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));

솔루션 3을 적용하면 위의 코드를 사용하거나 다음을 사용할 수 있습니다 (그러나 훨씬 더 많은 코드).

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result;
if (p1.isPresent()) {
    if (p2.isPresent()) {
        result = myObject.calculateSomething(p1, p2);
    } else {
        result = myObject.calculateSomething(p1);
    }
} else {
    if (p2.isPresent()) {
        result = myObject.calculateSomething(p2);
    } else {
        result = myObject.calculateSomething();
    }
}

그래서 내 질문은 : 왜 Optional메소드 인수로 s 를 사용하는 것이 나쁜 습관으로 간주 됩니까? 그것은 나에게 가장 읽기 쉬운 솔루션처럼 보이며 매개 변수가 미래 관리자에게 비어 있거나 null 일 수 있음을 가장 분명하게 만듭니다. (디자이너가 Optional반환 유형으로 만 사용하도록 의도했지만이 시나리오에서 사용하지 않는 논리적 이유는 없습니다.)



답변

아, 그 코딩 스타일은 약간의 소금으로 찍어야합니다.

  1. (+) 의미 론적 분석없이 선택적인 결과를 다른 방법으로 전달; 그 방법으로 그것을 떠나는 것은 꽤 괜찮습니다.
  2. (-) 메소드 내부에서 조건부 논리를 유발하는 선택적 매개 변수를 사용하는 것은 문자 그대로 비생산적입니다.
  3. (-) 선택 사항에서 인수를 압축해야하는 것은 컴파일러에게는 적합하지 않으며 불필요한 랩핑을 수행합니다.
  4. (-) nullable 매개 변수와 비교하여 선택 사항이 더 비쌉니다.

일반적으로 : 옵션은 해제해야하는 두 가지 상태를 통합합니다. 따라서 데이터 흐름의 복잡성 때문에 입력보다 결과에 더 적합합니다.


답변

최고의 포스트 나는 주제에서 본이 쓴 다니엘 Olszewski :

필수 메소드 매개 변수가 아닌 경우 선택 사항을 고려하고 싶은 유혹이있을 수 있지만, 그러한 대안은 다른 대안과 비교하여 창백합니다. 문제를 설명하려면 다음 생성자 선언을 검사하십시오.

public SystemMessage(String title, String content, Optional<Attachment> attachment) {
    // assigning field values
}

언뜻보기에 올바른 디자인 결정으로 보일 수 있습니다. 결국 첨부 파일 매개 변수를 선택적으로 표시했습니다. 그러나 생성자를 호출하는 경우 클라이언트 코드가 약간 어색해질 수 있습니다.

SystemMessage withoutAttachment = new SystemMessage("title", "content", Optional.empty());
Attachment attachment = new Attachment();
SystemMessage withAttachment = new SystemMessage("title", "content", Optional.ofNullable(attachment));

명확성을 제공하는 대신 선택적 클래스의 팩토리 메소드는 독자를 혼란스럽게합니다. 선택적 매개 변수는 하나 뿐이지 만 2 ~ 3 개가 있다고 상상해보십시오. 밥 삼촌은 분명히 그런 코드를 자랑스럽게 생각하지 않을 것입니다.

메소드가 선택적 매개 변수를 승인 할 수있는 경우, 검증 된 접근 방식을 채택하고 메소드 오버로드를 사용하여 이러한 경우를 설계하는 것이 좋습니다. SystemMessage 클래스의 예에서 두 개의 개별 생성자를 선언하는 것이 Optional을 사용하는 것보다 우수합니다.

public SystemMessage(String title, String content) {
    this(title, content, null);
}

public SystemMessage(String title, String content, Attachment attachment) {
    // assigning field values
}

이러한 변경으로 인해 클라이언트 코드가 훨씬 간단하고 읽기 쉽습니다.

SystemMessage withoutAttachment = new SystemMessage("title", "content");
Attachment attachment = new Attachment();
SystemMessage withAttachment = new SystemMessage("title", "content", attachment);

답변

Optional매개 변수로 사용하지 않는 이유는 거의 없습니다 . 이것에 대한 주장은 권위의 주장에 의존합니다 (Brian Goetz-그의 주장은 우리가 null이 아닌 선택 사항을 강제 할 수 없다는 것입니다 Optional). 물론 Java의 모든 참조는 null 일 수 있으므로 프로그래머 메모리가 아닌 컴파일러가 규칙을 시행하도록 권장해야합니다 (문제가 있고 확장되지 않습니다).

기능적 프로그래밍 언어는 Optional매개 변수를 권장 합니다. 이것을 사용하는 가장 좋은 방법 중 하나는 여러 개의 선택적 매개 변수를 사용 liftM2하고 매개 변수가 비어 있지 않은 것으로 가정하고 함수를 사용하는 것입니다 ( http://www.functionaljava.org/javadoc/4.4/functionaljava/fj/ 참조) . data / Option.html # liftM2-fj.F- ). Java 8은 불행히도 선택적을 지원하는 매우 제한된 라이브러리를 구현했습니다.

Java 프로그래머로서 레거시 라이브러리와 상호 작용하기 위해 null 만 사용해야합니다.


답변

이 조언은 “입력에 대해서는 가능한 한 구체적이지 않고 출력에 대해서는 가능한 구체적이지 않다”는 규칙의 변형입니다.

일반적으로 널이 아닌 일반 값을 취하는 메소드가있는 경우이를 널에 맵핑 할 수 Optional있으므로 일반 버전은 입력과 관련하여 더 구체적이지 않습니다. 그러나Optional 그럼에도 불구하고 논쟁 이 필요한 여러 가지 이유가 있습니다 .

  • 함수를 반환하는 다른 API와 함께 사용하기를 원합니다. Optional
  • Optional주어진 값이 비어 있으면 함수는 비어있는 것 이외의 것을 반환해야합니다
  • 당신은 생각 Optional합니다 누구든지 당신의 API가 그것에 대해 배울해야한다 너무 굉장합니다 😉

답변

with 패턴 Optional리턴 을 피하기위한 것 null입니다. null메소드 에 전달하는 것은 여전히 ​​가능합니다 .

이것들은 아직 공식적이지는 않지만 JSR-308 스타일 주석을 사용 null하여 함수에 값을 수락할지 여부를 나타낼 수 있습니다. 실제로이를 식별하려면 올바른 툴링이 있어야하며 실행 가능한 런타임 정책보다 더 많은 정적 검사를 제공하지만 도움이 될 것입니다.

public int calculateSomething(@NotNull final String p1, @NotNull final String p2) {}


답변

이것은 나에게 약간 어리석은 것처럼 보이지만 내가 생각할 수있는 유일한 이유는 메소드 매개 변수의 객체 인수가 이미 선택적인 방법이기 때문에 null 일 수 있다는 것입니다. 따라서 누군가 기존 객체를 가져 와서 선택 사항으로 감싸도록 강요하는 것은 무의미합니다.

즉, 선택 사항을 가져 오거나 반환하는 체인 방법을 함께 묶는 것이 합리적 일입니다 (예 : 아마도 모나드).


답변

JDK10 ( https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html) 에서 JavaDoc을 확인하십시오 . API 메모가 추가되었습니다.

API 참고 : 선택 사항은 기본적으로 “결과 없음”을 표시해야하며 널을 사용하면 오류가 발생할 수있는 메소드 리턴 유형으로 사용하기위한 것입니다.