Java에서 선택적 옵션을 사용하여 빈 포인터 예외 방지

14333 단어 programmingjava

1964년에 영국 컴퓨터 과학자 토니 홀은 공지침 인용을 발명했다.
빈 지침 이상은 생산 이상에서 가장 흔히 볼 수 있는 오류다.그것은 C, C++, C#, 자바스크립트, 자바 등을 포함한 많은 프로그래밍 언어로 이루어졌다.
홀은 그에 필요한 재력, 시간, 인적 자원 손실을 복구하여 이를 'a' billion-dollar mistake 라고 불렀다.
Java는 빈 포인터 참조를 구현하는 프로그래밍 언어 중 하나입니다.만약 네가 줄곧 자바로 개발하고 있다면, 나는 네가 이미 많은 것을 보았다고 믿는다.자네가 자바 초보든 10년의 경험이 있든항상 빈 포인터 오류가 발생할 수 있습니다.

Java 옵션

Optional는 Java 8에 도입된 API입니다.만약 적절하게 사용한다면, 그것은 빈 바늘의 이상한 문제를 해결할 수 있을 것이다.
옵션 API는 함수를 프로그래밍하고 함수 인터페이스를 사용합니다.
자바 함수식 프로그래밍에 대한 지식을 더 알고 싶으면, 제 다른 글 Functional Programming in Java, Explained 을 읽어 주십시오.
계속하기 전에 이 문서의 예는 Java11을 사용합니다.다른 버전의 Java를 사용하는 경우 일부 메서드가 존재하지 않거나 동작이 동일할 수 있습니다.

빈 옵션

optional API를 사용할 때 비어 있는 Optional는 빈 바늘의 이상을 피하는 주요한 방법이다.Optional의 흐름에서null은 공백Optional으로 전환됩니다.비어 있는 Optional는 더 이상 처리되지 않습니다.이것이 바로 우리가 NullPointerException를 사용할 때 Optional를 피하는 방법이다.
우리는 본문 뒤에 공Optional의 행위를 더욱 이해할 것이다.

선택적 객체 만들기


객체를 시작할 수 있는 세 가지 방법이 있습니다Optional.
  • Optional.of(T)
  • Optional.ofNullable(T)
  • Optional.empty()
  • 선택할 수 있습니다.... 에 속하다

    Optional.of 매개변수 중 빈 값이 될 수 없는 유형을 적용합니다.Optional를 사용하여 Optional.of 대상을 만들려면 매개 변수에 값을 전달해야 합니다.
        @Test
        public void initializeOptional_optionalOf() {
            Optional<String> helloWorldOptional = Optional.of("Hello, world");
            assert helloWorldOptional.isPresent();
            assert "Hello, world".equals(helloWorldOptional.get());
        }
    
    값을 Optional.of에 전달할 때는 매우 조심해야 한다.Optional.of는 매개변수의 빈 값을 허용하지 않습니다.빈 값을 전달하려고 하면 NullPointerException 을 생성합니다.
         @Test
        public void initializeOptional_optionalOf_null() {
            try {
                Optional.of(null);
            } catch (Exception e) {
                assert e instanceof NullPointerException;
            }
        }
    

    선택할 수 있습니다.남에게 말할 수 없었어

    Optional.ofNullable는 옵션과 유사합니다.그럼요.그것은 모든 종류를 받아들일 수 있다.다른 점은 Optional.ofNullable를 사용하면 빈 값을 매개 변수에 전달할 수 있다는 것이다.
        @Test
        public void initializeOptional_optionalOfNullable() {
            Optional<String> helloWorldOptional = Optional.ofNullable("Hello, world");
            assert helloWorldOptional.isPresent();
            assert "Hello, world".equals(helloWorldOptional.get());
        }
    
    빈 대상을 초기화할 때 Optional.ofNullable 빈 대상을 되돌려줍니다.
        @Test
        public void initializeOptional_optionalOfNullable_null() {
            Optional<String> helloWorldOptional = Optional.ofNullable(null);
            assert !helloWorldOptional.isPresent();
            try {
                helloWorldOptional.get();
            } catch (Exception e) {
                assert e instanceof NoSuchElementException;
            }
        }
    

    선택할 수 있습니다.텅 비었어

    Optional를 사용하여 공백Optional을 초기화할 수 있습니다.
        @Test
        public void initializeOptional_optionalEmpty() {
            Optional<String> helloWorldOptional = Optional.empty();
            assert !helloWorldOptional.isPresent();
        }
    

    액세스 옵션


    Optional 의 값을 가져올 수 있는 몇 가지 방법이 있습니다.

    얻다


    이것은 매우 간단한 방법이다.Optional.empty() 방법이 존재하면 get의 값을 되돌려주고 이 값이 존재하지 않으면 던진다Optional.
        @Test
        public void get_test() {
            Optional<String> helloWorldOptional = Optional.of("Hello, World");
            assert "Hello, World".equals(helloWorldOptional.get());
        }
    
        @Test
        public void get_null_test() {
            Optional<String> helloWorldOptional = Optional.empty();
            try {
                helloWorldOptional.get();
            } catch (Exception e) {
                assert e instanceof NoSuchElementException;
            }
        }
    

    오레르스

    NoSuchElementException가 비어 있을 때 기본값을 사용하려면 Optional 방법을 사용할 수 있습니다.
        @Test
        public void orElse_test() {
            Optional<String> helloWorldOptional = Optional.of("Hello, World");
            assert "Hello, World".equals(helloWorldOptional.orElse("default"));
        }
    
        @Test
        public void orELseNull_test() {
            Optional<String> helloWorldOptional = Optional.empty();
            assert "default".equals(helloWorldOptional.orElse("default"));
        }
    

    올레스게이트

    orElseorElseGet 방법은 매우 비슷하다.다만 orElse 그것을 매개 변수로 받아들인다.
        @Test
        public void orElseGet_test() {
            Optional<String> helloWorldOptional = Optional.of("Hello, World");
            assert "Hello, World".equals(helloWorldOptional.orElseGet(() ->"default"));
        }
    
        @Test
        public void orELseGet_Null_test() {
            Optional<String> helloWorldOptional = Optional.empty();
            assert "default".equals(helloWorldOptional.orElseGet(() ->"default"));
        }
    

    오레슬리

    orElseGetSupplier<T>의 값을 되돌려주거나 orElseThrow의 값이 비어 있을 때 이상을 일으킨다.
        @Test
        public void orElseThrow_test() {
            Optional<String> helloWorldOptional = Optional.of("Hello, World");
            assert "Hello, World".equals(helloWorldOptional.orElseThrow(NullPointerException::new));
        }
    
        @Test
        public void orELseThrow_Null_test() {
            Optional<String> helloWorldOptional = Optional.empty();
            try {
                helloWorldOptional.orElseThrow(NullPointerException::new);
            } catch (Exception e) {
                assert e instanceof NullPointerException;
            }
        }
    

    옵션 처리


    처리하고 전환할 수 있는 방법이 많다Optional.이 절에서 우리는 자주 사용하는 방법을 배울 것이다.
    내가 본문 첫머리에 쓴 바와 같이 흐름 중에는 빈 것을 처리하지 않는다Optional.우리는 이 절의 예시에서 이 점을 볼 수 있다.

    지도.

    OptionalOptional 대상을 처리할 때 가장 자주 사용하는 방법이다.이것은 map 매개 변수로 받아들여지고 Optional 되돌아옵니다. 이것은 함수를 모든 종류의 매개 변수와 함께 사용할 수 있음을 의미합니다. 되돌아오는 값은 Function<? super T, ? extends U> 방법에서 Optional<U>. 로 포장됩니다.
        @Test
        public void processingOptional_map_test() {
            Optional<String> stringOptional = Optional.of("Hello, World")
                    .map(a -> a + ", Hello");
    
            assert stringOptional.isPresent();
            assert "Hello, World, Hello".equals(stringOptional.get());
        }
    
    Optional에서 빈 값을 되돌려 보려고 시도하면 map 방법은 빈 값Function<? super T, ? extends U>을 되돌려 줍니다.
        @Test
        public void processingOptional_map_empty_test() {
            Optional<String> stringOptional = Optional.of("Hello, World")
                    .map(a -> null);
    
            assert !stringOptional.isPresent();
        }
    
    map 빈 옵션은 처리되지 않습니다.다음과 같은 테스트를 통해 이를 확인할 수 있습니다.
        @Test
        public void processingOptional_map_empty_notProcessed_test() {
            AtomicBoolean atomicBoolean = new AtomicBoolean(false);
            Optional<String> stringOptional = Optional.of("Hello, World")
                    .map(a -> null)
                    .map(a -> {
                        atomicBoolean.set(true);
                        return "won't be processed";
                    });
    
            assert !stringOptional.isPresent();
            assert atomicBoolean.get() == false;
        }
    

    평면도


    이것은 Optional와 유사하지만 map는 함수의 반환값을 map로 포장하지 않습니다.flatMap 방법은 Optional를 매개 변수로 받아들인다.이것은 어떤 종류를 받아들이고 flatMap 되돌아오는 함수를 정의해야 한다는 것을 의미합니다.
    코드가 다른 반환 Function<? super T, ? extends Optional<? extends U>> 대상의 방법을 호출할 때, 보통 Optional 방법을 사용합니다.
    
        @Test
        public void processingOptional_flatmap_test() {
            Optional<String> stringOptional = Optional.of("Hello, World")
                    .flatMap(this::getString);
    
            assert "Hello, World, Hello".equals(stringOptional.get());
        }
    
        @Test
        public void processingOptional_flatmap_randomString_test() {
            Optional<String> stringOptional = Optional.of(UUID.randomUUID().toString())
                    .flatMap(this::getString);
    
            assert !stringOptional.isPresent();
        }
    
        public Optional<String> getString(String s) {
            if ("Hello, World".equals(s)) {
                return Optional.of("Hello, World, Hello");
            }
            return Optional.empty();
        }
    

    필터

    flatMap의 이전 예에서 우리는 성명식 양식을 사용하여 Optional 방법의 반환 값을 구분한다.그러나 우리는 실제로 flatMap 방법을 사용하여 이 점을 실현할 수 있다.
       @Test
        public void processingOptional_filter_test() {
            Optional<String> stringOptional = Optional.of("Hello, World")
                    .filter(helloWorldString -> "Hello, World".equals(helloWorldString))
                    .map(helloWorldString -> helloWorldString + ", Hello");
    
            assert "Hello, World, Hello".equals(stringOptional.get());
        }
    
        @Test
        public void processingOptional_filter_randomString_test() {
            Optional<String> stringOptional = Optional.of(UUID.randomUUID().toString())
                    .filter(helloWorldString -> "Hello, World".equals(helloWorldString))
                    .map(helloWorldString -> helloWorldString + ", Hello");
    
            assert !stringOptional.isPresent();
        }
    view rawProcessingOptionalT
    

    하면, 만약, 만약...

    getString 방법은 하나filter를 받아들여 옵션이 비어 있지 않을 때만 실행됩니다.
        @Test
        public void processingOptional_ifPresent_test() {
            AtomicBoolean atomicBoolean = new AtomicBoolean(false);
            Optional.of("Hello, World")
                .ifPresent(helloWorldString -> atomicBoolean.set(true));
            assert atomicBoolean.get();
        }
    
        @Test
        public void processingOptional_ifPresent_empty_test() {
            AtomicBoolean atomicBoolean = new AtomicBoolean(false);
            Optional.empty()
                    .ifPresent(helloWorldString -> atomicBoolean.set(true));
            assert !atomicBoolean.get();
        }
    

    모면할 일


    코드ifPresent에서 사용하려면 중요한 일을 피해야 한다.

    옵션 적용 방법을 만들지 마십시오.


    매개 변수로 받아들이는 방법을 만들면 해결하고자 하는 문제를 도입할 수 있습니다. Consumer만약 Optional 파라미터를 가진 방법을 사용하는 사람이 그것을 모른다면, 그들은 Optional 대신 null을 이 방법에 전달할 수 있다.처리 aNullPointerException는 aOptional를 생성한다.
        @Test
        public void optionalAsParameter_test() {
            try {
                isPhoneNumberPresent(null);
            } catch (Exception e) {
                assert e instanceof NullPointerException;
            }
        }
    
        public boolean isPhoneNumberPresent(Optional<String> phoneNumber) {
            return phoneNumber.isPresent();
        }
    

    검사 없이 가치 실현

    Optional.empty() 를 사용하고 있다면 get 방법을 사용하지 마십시오.만약 어떤 이유로 그것을 사용해야 한다면, 우선 null 방법으로 검사를 진행하십시오. 비어 있는 NullPointerException 에서 get을 사용하면 Optional 생성되기 때문입니다.
        @Test
        public void getWithIsPresent_test() {
            Optional<String> helloWorldOptional = Optional.ofNullable(null);
            if (helloWorldOptional.isPresent()) {
                System.out.println(helloWorldOptional.get());
            }
        }
    
        @Test
        public void getWithoutIsPresent_error_test() {
            Optional<String> helloWorldOptional = Optional.ofNullable(null);
            try {
                System.out.println(helloWorldOptional.get());
            } catch (Exception e) {
                assert e instanceof NoSuchElementException;
            }
        }
    

    결론


    끝까지 읽어주셔서 감사합니다!옵션 기능은 모든 Java 개발자가 알아야 할 강력한 기능입니다.만약 당신이 정확하게 한쪽에서 다른 한쪽으로 선택할 수 있는 기능을 사용한다면, 나는 당신이 더 이상 isPresent의 요구를 만족시키지 못할 것이라고 믿습니다.
    Optional은 다른 대형 라이브러리(예를 들어 Reactor와 RXJava)의 기초로도 사용되기 때문에 Optional의 작업 원리를 이해하는 것도 이를 이해하는 데 도움이 될 것입니다.
    이 문서의 예제에서 저장소를 찾을 수 있습니다.
    https://github.com/brilianfird/java-optional
    도구책
  • Null Pointer References: The Billion Dollar Mistake
  • https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions
  • https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
  • 좋은 웹페이지 즐겨찾기