var a : String? = null
우리가 이러한 변수처럼 nullable한 변수를 선언했을 때 함수에 적용 시키려면 대부분 if문을 통해 null이 아닐경우에 작동하게끔 할 것이다. 하지만 if문 아니더라도 더 효율적으로 할 방법이 있다.

: 참조연산자를 실행하기 전에 먼저 객체가 null인지 확인부터 하고, 객체가 null이라면 뒤에 따라오는 구문을 실행하지 않는 것이다.
: 객체가 null이 아니라면 그대로 사용하지만 null이라면 우측의 객체로 대체된다.
: 참고로 왜 elvis냐면 연산자의 모양이 엘비스 프레슬리와 비슷하다고 하여 붙여진 이름이다. ㅋㅋㅋㅋ
: 참조연산자를 사용할 때 null 여부를 컴파일 시 확인하지 않도록 하여 런타임시 null pointer exception이 나도록 의도적으로 방치하는 연산자

보는 바와 같이 null safe operator 경우 null이라면 toUpperCase 실행하지 않아 a의 값 null그대로 반환한다.
elvis의 경우 null일 경우 뒤에 따라오는 "default"를 반환하고,
non-null assertion 같은 경우 의도적으로 방치하기에 NullPointerException을 발생한다.

null safe operator는 스코프 함수와 동반해서 사용하게되면 더욱 유용하다. null값인지 체크 후 같은 스코프 내 a에 대하여 함수를 실행하기 때문에 안전성 및 코드의 가독성 또한 좋아진다.
: 메모리 상에 서로 다른 곳에 할당된 객체라고 해도 그 내용이 같다면 동일하다고 판단

open fun equals(other: Any?): Boolean
이러한 형태처럼 equals를 상속받아 동일성을 확인해주는 구문을 따로 구현해줘야 한다.
: 서로 다른 변수가 메모리 상에 같은 객체를 가리키고 있을때만 동일하다고 판단


a, b는 내용은 같지만 객체는 서로 별개임을 알 수 있고,
a, c는 내용과 객체가 서로 같음을 알 수 있고,
a, d는 아예 다른 객체임을 알 수 있다.