프로덕션 코드에서 assert 문으로 null을 선언하지 않아야합니까? [닫은]


32

나는 본 적이 질문을하지만의 사용에 대한 몇 가지 더 질문이 assert키워드. 나는 다른 코더들과 사용하는 것에 대해 토론했다 assert. 이 유스 케이스의 경우 특정 전제 조건이 충족되면 널을 리턴 할 수있는 메소드가있었습니다. 내가 작성한 코드는 메서드를 호출 한 다음 null을 반환하지 않고 반환 된 객체를 계속 사용한다고 주장합니다.

예:

class CustomObject {
    private Object object;

    @Nullable
    public Object getObject() {
        return (object == null) ? generateObject() : object;
    }
}

이제 다음과 같이 사용한다고 상상해보십시오.

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    assert object != null;
    // Do stuff using object, which would throw a NPE if object is null.
}

나는 프로덕션 코드에서 절대 사용 assert해서는 안되며 테스트에만 사용해야한다는 것을 제거해야한다고 들었습니다 . 그게 사실입니까?


19
기본적으로 어설 션은 비활성화되어 있습니다. 런타임시 -ea또는 이와 동등한 방식 으로 명시 적으로 활성화해야합니다 .
jarmod

소프트웨어 공학에 관련 질문 : softwareengineering.stackexchange.com/q/137158/187318 (허용 대답이 아직 도입 이후 더 이상 필요가 외부 라이브러리, 권장 그래서 꽤 오래된하지만 Objects.requireNonNull자바 8 참조).
헐크

이 질문 제목은 지나치게 광범위하지만 본문에 언급 된 질문은 훨씬 더 좁으며, 답변마다 도우미 기능에 의해 완전히 처리됩니다.
smci

분명히하기 위해 클라이언트 코드가 객체에 대해 null이 아닌 검사 / 어설 션을 구현 해야하는지 묻습니다 . ( 라이브러리 코드 자체가 객체가 null을 보장하지 않아야 하는지 , 아니면 거기에 주장 할 수 있는지를 묻는 것과는 다릅니다 ).
smci

답변:


19

Objects.requireNonNull(Object)그것을 위해 사용하십시오 .

지정된 개체 참조가 null이 아닌지 확인합니다. 이 메소드는 주로 메소드와 생성자에서 매개 변수 유효성 검증을 수행하도록 설계되었습니다. [...]

귀하의 경우 이것은 다음과 같습니다.

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    Objects.requireNonNull(object);
    // Do stuff using object, which would throw a NPE if object is null.
}

이 함수는 당신이 언급 한 목적을 위해 만들어졌습니다. 즉, 널이 아닌 것을 명시 적으로 표시합니다. 생산에서도. 가장 큰 이점은 null 값이 처음에 발생하지 않아야하는 곳에서 null 값을 찾을 수 있다는 것입니다. 원하지 않는 곳으로 전달 된 null 값으로 인해 문제를 디버깅하는 데 어려움이 줄어 듭니다.

또 다른 이점은와 달리 null 검사에 대한 추가 유연성 assert입니다. assert부울 값을 확인하기위한 키워드 인 반면 Objects.requireNonNull(Object)함수이므로 훨씬 유연하고 읽기 쉬운 코드에 포함될 수 있습니다. 예 :

Foo foo = Objects.requireNonNull(service.fetchFoo());

// You cannot write it in on line.
Bar bar = service.fetchBar();
assert bar != null;
service.foo(Objects.requireNonNull(service.getBar()));

// You cannot write it in on line.
Bar bar = service.getBar();
assert bar != null;
service.foo(bar);

명심 Objects.requireNonNull(Object)널 (null) 검사에 대한 독점적 인 assert일반화되어있다. assert이와 관련하여 약간 다른 목적, 즉 주로 테스트가 있습니다. 테스트를 위해 활성화하고 프로덕션에서 비활성화 할 수 있습니다. 어쨌든 프로덕션 코드에는 사용하지 마십시오. 테스트를위한 불필요하고 복잡한 유효성 검사로 인해 응용 프로그램 속도가 느려질 수 있습니다. 프로덕션 전용 검사에서 테스트 전용 검사를 분리하는 데 사용하십시오.

에 대한 자세한 내용은 공식 문서 를 확인하십시오 assert.


14
선언 된 사람은 누구이며 언제 레거시라고 주장합니까? 링크 / 참조? 테스트에서 쉽게 활성화하고 프로덕션에서 비활성화 할 수있는 풋 프린트 제로 검증을 가능하게하는 도구를 "레거시"로 정의한 제정신 개발자는 상상할 수 없습니다.
Dmitry Pisklov

런타임에 assert의 작동 여부를 결정할 수 있지만 requireNonNull에 의해 NPE가 발생하는지 런타임에 결정할 수없는 경우 '어설 션보다 NPE가 더 많은 제어를 갖는다'는 것은 무엇을 의미합니까?
피트 커크

@DmitryPisklov 당신이 맞습니다, 나는 그것을 편집했습니다. 테스트를위한 훌륭한 도구입니다.
akuzminykh

2
@PeteKirkham 당신 말이 맞아요, 통제 는 잘못된 단어였습니다. 구문의 유연성에 대해 더 이야기하고있었습니다. 또한 : 여기 수 있습니다 이유는 코드가 NPEs가 발생 할 이유를 발견했다.
akuzminykh

1
"프로덕션 코드에는 사용하지 마십시오. 응용 프로그램 속도가 느려질 수 있습니다."그럴 수도 있지만 그만한 가치가 있습니다. Java에는 내장 된 런타임 검사 기능이있어 어레이가 오버런되지 않도록합니다. 성능 저하에도 불구하고 프로덕션 배포에서도 사용할 수 있습니다. 우리는 그것에 대한 선택조차받지 못했습니다. 프로덕션 코드에서 런타임 검사를하는 것이 항상 잘못된 것은 아닙니다.
Max Barraclough

22

어설 션에 대해 기억해야 할 가장 중요한 사항은 비활성화 할 수 있으므로 실행되지 않는다고 가정하는 것입니다.

이전 버전과의 호환성을 위해 JVM은 기본적으로 어설 션 유효성 검사를 비활성화합니다. -enableassertions 명령 행 인수 또는 축약 형 -ea를 사용하여 명시 적으로 사용 가능해야합니다.

java -ea com.whatever.assertion.Assertion

따라서 의존하는 것은 좋은 습관이 아닙니다.

어설 션은 기본적으로 활성화되어 있지 않으므로 코드에서 사용될 때 어설 션이 실행된다고 가정 할 수 없습니다. 따라서 항상 null 값을 검사하고 선택 사항을 비워 두어야합니다. 어설 션을 사용하여 입력을 공용 메서드로 확인하지 말고 대신 확인되지 않은 예외를 사용하십시오. 일반적으로 모든 확인은 어설 션이없는 것처럼 수행합니다.


분명히 그들에게 의존하는 것은 나쁜 습관이지만, 그것을 일반적으로 사용하는 것은 나쁜 습관입니까?
Big_Bad_E

3
@Big_Bad_E 어설 션은 프로그램을 가능한 한 빨리 실패하게 만드는 디버그 도구입니다. 그런 다음 어설 션에도 불구하고 프로그램이 실패 할 수있는 방법이 없는지 확인하는 것이 테스트 스위트의 임무입니다 . 아이디어는 테스트 스위트 (100 % 적용 범위, 그렇지 않습니까?!?)가 실패없이 실행되면 어쨌든 트리거 할 수 없으므로 어설 션을 제거하여 저장하는 것입니다. 물론이 추론에는 약간의 이상주의가 있지만 그것이 바로 아이디어입니다.
cmaster-

11

분명히 당신이 말하는 것은 뻔뻔스러운 거짓말입니다. 이유는 다음과 같습니다.

독립형 jvm 만 실행하면 어설 션이 기본적으로 사용되지 않습니다. 사용하지 않으면 설치 공간이 없어 프로덕션 응용 프로그램에 영향을 미치지 않습니다. 그러나 코드를 개발하고 테스트 할 때 가장 친한 친구 일 것입니다. 대부분의 테스트 프레임 워크 러너는 어설 션을 활성화하므로 (JUnit에서 수행) 단위 테스트를 실행할 때 어설 션 코드가 실행되어 잠재적 인 버그 (예 : 일부 비즈니스 로직 경계 검사에 대한 어설 션을 추가 할 수 있으며 이는 부적절한 값을 사용하는 일부 코드를 감지하는 데 도움이됩니다.

다른 답변에서 알 수 있듯이, 정확히 그런 이유로 (항상 가능하지는 않지만) 중요한 점검을 수행하거나 (특히!) 상태를 유지하기 위해 어설 션에 의존 할 수는 없습니다.

어설트를 사용하는 방법에 대한 흥미로운 예를 보려면 여기를 살펴보십시오 . 파일 끝에 singleThreadedAccess()201 행의 어설트 명령문에서 호출되는 메소드 가 있으며 테스트에서 잠재적 인 멀티 스레드 액세스를 포착 할 수 있습니다.


4

다른 답변은 이미 충분히 다루었지만 다른 옵션이 있습니다.

예를 들어 Spring에는 정적 메소드가 있습니다.

org.springframework.util.Assert.notNull(obj)

자체 Assert.something()메소드 가있는 다른 라이브러리 도 있습니다. 직접 작성하는 것도 매우 간단합니다.

그러나 이것이 웹 서비스 인 경우 어떤 예외가 발생하는지 명심하십시오. 예를 들어 앞에서 언급 한 방법 IllegalArgumentException은 Spring에서 기본적으로 500을 반환 하는 것을 던집니다 .

웹 서비스의 경우, 이것은 종종 하지 내부 서버 오류 및 500이어야한다, 오히려 잘못된 요청을하는 (400).


1
"Assert"메소드가 프로세스를 즉시 중단시키지 않고 대신 어떤 종류의 예외를 발생 시키면 어설 션이 아닙니다. 요점은 assert생성 된 코어 파일로 즉시 충돌을 일으켜 조건을 위반 한 지점에서 원하는 디버거로 사후 작업을 수행 할 수 있다는 것입니다.
cmaster-

어설 션은 프로세스를 즉시 중단시키지 않습니다. 오류가 발생하여 잡힐 수 있습니다. 처리되지 않은 예외는 오류뿐만 아니라 테스트를 중단시킵니다. 원하는 경우 의미론을 주장 할 수 있습니다. 원하는 경우 예외 대신 오류를 발생시키는 사용자 지정 어설 ​​션을 작성하십시오. 사실 대다수의 경우 적절한 조치는 오류가 아닌 예외를 처리하는 것입니다.
크리스토퍼 슈나이더

아, 자바는 실제로 assert던지기로 정의 합니다. 흥미 롭군 C / C ++ 버전은 그런 일을하지 않습니다. a) 프로세스를 종료하고 b) 코어 덤프를 생성한다는 신호를 즉시 발생시킵니다. 사용 가능한 호출 스택에 대한 전체 정보가 여전히 남아 있기 때문에 이러한 어설 션 오류를 디버깅하는 것은 매우 간단합니다. assert대신 의도적으로 프로그래밍 방식으로 잡힐 수있는 예외를 throw하도록 정의 하면 목적이 무효화됩니다.
cmaster-

C와 C ++에 이상한 (또는 많은) 이상한 것들이있는 것처럼 Java에도 몇 가지 이상한 것들이 있습니다. 그들 중 하나는입니다 Throwable. 그들은 항상 잡힐 수 있습니다. 그게 좋은지 아닌지 모르겠습니다. 나는 그것이 달려 있다고 생각합니다. 많은 Java 프로그램은 웹 서비스이므로 거의 모든 이유로 충돌하는 것이 바람직하지 않으므로 거의 모든 것이 포착되어 기록됩니다. 가장 큰 장점 중 하나는 스택 추적이며, 일반적으로 예외 또는 오류의 원인을 자체적으로 진단하기에 충분합니다.
크리스토퍼 슈나이더

3

프로그래밍 오류 (예 : 버그)를 잡는 데 도움이 될 때마다 어설 션을 자유롭게 사용하십시오 .

논리적으로 발생할 수있는 잘못된 형식의 입력을 잡기 위해 assert를 사용하지 마십시오. 오류를 복구 할 수없는 경우에만 assert를 사용하십시오.

어설 션을 확인할 때 실행되는 코드에 프로덕션 로직을 두지 마십시오. 소프트웨어가 제대로 작성된 경우 이는 사실이 아니지만, 그렇지 않은 경우 어설 션을 사용하거나 사용하지 않도록 설정하면 미묘한 부작용과 다른 전체 동작이 발생할 수 있습니다.

귀사에 "테스트 코드"와 "제작 코드"가 동일하지만 다른 코드 기반 (또는 다른 편집 단계)을 수행하는 경우에는 빠져 나와 다시는 돌아 오지 마십시오. 무능한 수준을 고치려고하는 것은 아마도 시간 낭비 일 것입니다. 회사가 테스트 코드 외부에 주장 진술을하지 않으면 생산 빌드에서 주장이 비활성화되어 있고 그렇지 않은 경우 해당 실수를 수정하는 것이 우선 순위라고 말합니다.

어설 션의 가치는 테스트 스위트뿐만 아니라 비즈니스 로직 내에서 정확하게 사용되어야합니다. 이를 통해 많은 양의 코드를 통과하기 위해 많은 것을 명시 적으로 테스트 할 필요가없는 많은 고급 테스트를 쉽게 수행 할 수 있으며 이러한 모든 주장을 트리거 할 수 있습니다. 내 프로젝트 중 일부에서 일반적인 테스트는 실제로 아무 것도 주장하지 않았으며 특정 입력을 기반으로 계산을 주문했기 때문에 수백 개의 어설 션을 확인하고 작은 논리 부분에서도 문제가 발견되었습니다.


2

언제든 assert를 사용할 수 있습니다. 토론은 언제 사용되는지에 관한 것입니다. 예를 들어 가이드에서 :

  • 공개 메소드에서 인수 확인에 어설 션을 사용하지 마십시오.
  • 어설 션을 사용하여 응용 프로그램에서 올바른 작업을 수행하는 데 필요한 작업을 수행하지 마십시오.

4
좋은 규칙. 그러나 몇 가지 이유가 추가되면 대답이 더 나을 것입니다.
cmaster-
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.