안전 탐색 연산자

객체 지향 프로그래밍에서 안전 탐색 연산자(safe navigation operator), 옵셔널 체이닝 연산자, 안전 호출 연산자, 널-조건부 연산자, 널-전파 연산자는 첫 번째 인수가 null이면 null을 반환하는 이항 연산자이다. 그렇지 않으면 두 번째 인수에 따라 역참조 작업을 수행한다(일반적으로 객체 멤버 접근, 배열 인덱스 또는 람다 호출).

순차적인 명시적 null 검사와 할당을 피하고 이를 메서드/속성 체이닝으로 대체하는 데 사용된다. 탐색 연산자(예: ".")가 null 객체에 적용될 때 오류를 발생시키는 프로그래밍 언어에서 안전 탐색 연산자는 메서드/필드 체인의 평가를 중지하고 체인 표현식의 값으로 null을 반환한다. 2007년 Groovy 1.0에서 처음 사용되었으며 현재 다음과 같은 언어에서 지원된다.
C 샤프, 스위프트, 타입스크립트, 루비, 코틀린, 러스트, 자바스크립트,
등. 이 연산자에 대한 공통 명명 규칙은 현재 없지만 안전 탐색 연산자가 가장 널리 사용되는 용어이다.

이 연산자를 사용하는 주요 장점은 파멸의 피라미드를 피할 수 있다는 것이다. 프로그래머는 여러 중첩된 if 문을 작성하는 대신 일반적인 체이닝을 사용하고 점(또는 체이닝에 사용되는 다른 문자) 앞에 물음표 기호를 추가할 수 있다.

안전 탐색 연산자와 널 병합 연산자는 모두 널-인식 연산자이지만, 작동 방식은 다르다.

예시

Apex
Apex의 안전 탐색 연산자 예시:a[x]?.aMethod().aField // a[x] == null 이면 null로 평가된다
a[x].aMethod()?.aField // a[x].aMethod()가 null로 평가되면 null을 반환한다
String profileUrl = user.getProfileUrl()?.toExternalForm();
return [SELECT Name FROM Account WHERE Id = :accId]?.Name;

C#
C# 6.0 이상에는 ?. (널-조건부 멤버 접근 연산자, Microsoft에서는 엘비스 연산자라고도 부르지만, C#에서 널 병합 연산자인 ??와 동일한 일반적인 엘비스 연산자의 사용과는 혼동해서는 안 된다)와 인덱서 get 접근자의 널-안전 호출을 수행하는 ?[] (널-조건부 요소 접근 연산자)가 있다. 멤버 접근 결과의 유형이 값 형식인 경우, 해당 멤버에 대한 널-조건부 접근 결과의 유형은 해당 값 형식의 널러블 버전이다.

다음 예제는 문서 배열에서 첫 번째 문서의 저자 이름을 검색한다(각 문서에 Author 멤버가 있고 각 저자에 Name 멤버가 있다고 가정). 배열이 null이거나, 첫 번째 요소가 null이거나, 해당 문서의 Author 멤버가 null이거나, 해당 저자의 Name 멤버가 null이면 결과는 null이다. 배열이 null이 아니지만 비어 있는 경우(즉, 길이가 0인 경우) IndexOutOfRangeException이 여전히 발생한다는 점에 유의하라.

string name = articles?[0]?.Author?.Name;

람다를 호출하려면 callback?.Invoke()가 필요하다. 널-조건부 호출(callback?()는 허용되지 않는다)이 없기 때문이다.

Func<int, int> callback = x => x * x; // x를 제곱한다
int result = callback?.Invoke(5);

클로저 (프로그래밍 언어)
클로저는 다른 언어에서 사용하는 의미의 진정한 연산자를 가지고 있지 않지만, 자바와 상호 운용되고 객체 탐색을 수행해야 하므로 some-> 매크로를 사용하여 안전 탐색을 수행할 수 있다. 확장된 매크로 safe->는 nil 값 호출을 방지한다.

(some-> article .author .name)

CoffeeScript
실존 연산자:
zip = lottery.drawWinner?().address?.zipcode

Crystal
Crystal은 try 안전 탐색 메서드를 지원한다

name = article.try &.author.try &.name

Dart
조건부 멤버 접근 연산자:var name = article?.author?.name

Gosu
널 안전 호출 연산자:

var name = article?.author?.name

널-안전 호출 연산자는 Gosu 속성으로 선언된 클래스 속성에는 필요하지 않다.

class Foo {
    var _bar: String as Bar
}

var foo: Foo = null

// 아래 코드는 null로 평가되며 NullPointerException을 반환하지 않는다
var bar = foo.Bar

Groovy
안전 탐색 연산자 및 안전 인덱스 연산자:

def name = article?.authors?[0].name

자바스크립트
ECMA스크립트 2020에 추가된 선택적 체이닝 연산자는 참조나 함수가 undefined 또는 null일 가능성이 있을 때 연결된 객체를 통해 값에 접근하는 것을 간소화하는 방법을 제공한다. 주요 데스크톱 브라우저는 2020년부터 이를 지원했으며, 대부분의 모바일 브라우저는 2024년까지 지원을 추가했다.
const name = article?.authors?.[0]?.name
const result = callback?.()

이 연산자는 오른쪽에 있는 전체 호출 체인을 단락한다. 다음 예시에서는 bar가 "접근"되지 않는다.
null?.foo.bar

Kotlin
안전 호출 연산자:

val name = article?.author?.name

Objective-C
대부분의 경우 NULL에 대해 걱정할 필요 없이 일반적인 탐색 구문을 사용할 수 있다. 이는 NULL로 메시지가 전송될 때 아무런 부작용 없이 폐기되기 때문이다.
NSString *name = article.author[0].name;

Perl 5
Perl 5에는 이러한 종류의 연산자가 없지만, 다음 구문으로 포함 제안이 수락되었다.
my $name = $article?->author?->name;

PHP
널 안전 연산자는 PHP 8에 채택되었다.

$name = $article?->author?->name;

Raku (Perl 6)
안전한 메서드 호출:

my $name = $article.?author.?name;

루비 (프로그래밍 언어)
루비는 버전 2.3.0부터 &. 안전 탐색 연산자(외로운 연산자라고도 함)를 지원한다.

name = article&.author&.name

러스트 (프로그래밍 언어)
러스트는 안전 탐색 연산자처럼 보일 수 있는 ? 연산자를 제공한다. 그러나 주요 차이점은 ?가 None 값을 만날 때 None으로 평가되지 않는다는 것이다. 대신 return 문처럼 동작하여 둘러싼 함수나 클로저가 즉시 None을 반환하게 한다.

Option 메서드 map() 및 and_then()을 안전 탐색에 사용할 수 있지만, 이 옵션은 안전 탐색 연산자보다 장황하다.
fn print_author(article: Option<Article>) {
    println!(
        "Author: {}",
        article.and_then(|y| y.author)
            .map(|z| z.name)
            .unwrap_or("Unknown".to_owned())
    );
}

?를 사용하는 구현은 article이 None이거나 article.unwrap().author가 None이면 아무것도 출력하지 않는다("Author:"조차도). ?가 None을 보는 즉시 함수는 반환된다.
fn try_print_author(article: Option<Article>) -> Option<()>{
    println!("Author: {}", article?.author?.name);
    Some(())
}

Scala

Scala의 null-safe 연산자는 Dsl.scala 라이브러리에서 제공한다.

val name = article.?.author.?.name : @ ?

@ ? 어노테이션은 null을 허용하는 값을 나타내는 데 사용될 수 있다.

case class Tree(left: Tree @ ? = null, right: Tree @ ? = null, value: String @ ? = null)

val root: Tree @ ? = Tree(
  left = Tree(
    left = Tree(value = "left-left"),
    right = Tree(value = "left-right")
  ),
  right = Tree(value = "right")
)

Scala의 일반적인 .는 null 값에서 메서드를 수행할 때 null-safe하지 않다.

a[NullPointerException] should be thrownBy {
root.right.left.right.value // root.right.left는 null이다!
}

대신 널 허용 값에 ? 연산자를 사용하여 예외를 피할 수 있다.

root.?.right.?.left.?.value should be(null)

? 중 하나가 null 값에서 수행되면 전체 표현식은 null이다.

null 안전 연산자 ?의 경계는 @ ?로 타입이 주석 처리된 가장 가까운 바깥쪽 표현식이다.

("Hello " + ("world " + root.?.right.?.left.?.value)) should be("Hello world null")
("Hello " + (("world " + root.?.right.?.left.?.value.?): @ ?)) should be("Hello null")
(("Hello " + ("world " + root.?.right.?.left.?.value.?)): @ ?) should be(null)

스위프트 (프로그래밍 언어)
옵셔널 체이닝 연산자, 서브스크립트 연산자, 그리고 호출:let name = article?.authors?[0].name
let result = protocolVar?.optionalRequirement?()

타입스크립트
옵셔널 체이닝 연산자는 타입스크립트 3.7 릴리스에 포함되었다.
let x = foo?.bar?.[0]?.baz();

Visual Basic .NET
Visual Basic 14 이상에는 C#과 유사한 ?.(null-조건부 멤버 접근 연산자) 및 ?()(null-조건부 인덱스 연산자)가 있다. 이들은 C#의 해당 연산자와 동일한 동작을 한다.

다음 문은 위의 C# 예시와 동일하게 동작한다.
Dim name = articles?(0)?.Author?.Name

 같이 보기 
 엘비스 연산자
 널 병합 연산자

 각주 

 외부 링크 
 PEP 505, 파이썬용 안전 탐색 연산자 가능성 논의

분류:객체 지향 프로그래밍
분류:코드 예시에 관한 문서
분류:조건문
분류:연산자 (프로그래밍)