null은 변수가 가리키는 주소값이 없다는 것을 표현하기 위한 키워드이다.
하지만 자바에서는 int나 char와 같은 primitive 타입에는 초기화 이전에도 0이나 공백같은 원시값을 저장하고 있기 때문에 null을 대입할 수 없다.
반면에 reference 타입에는 값이 없거나 기본값을 null로 지정한다.
자료형 | 기본값 |
---|---|
boolean | false |
char | '\u0000' |
byte,short,int | 0 |
long | 0 |
float | 0.0f |
double | 0.0d |
reference value | null |
에러 메시지 NullPointerException은 null 참조로 인해 자바 개발자들이 가장 골치아프게하는 1등 공신이다.
자바에서의 null은 참조가 없는 경우를 뜻하는데, 만약 null을 참조하는 레퍼런스 변수로 객체의 인스턴스 메서드를 호출하는 등의 객체 코드를 실행하려 할 때 NPE가 발생한다.
public class Person {
private Game game;
private String name;
public Person(String name) {
this.name = name;
}
public Game getGame() {
return game;
}
}
public class Game {
private Weapon weapon;
public Weapon getWeapon() {
return weapon;
}
}
public class Weapon {
public String printWeapon() {
return "sword";
}
}
public static void main(String[] args) {
Person person = new Person("aaa");
person.getGame().getWeapon().printWeapon();
}
코드를 실행해보면 NPE가 발생하게 된다.
new Person()이 초기화 될 때, 인스턴스 객체인 Game에 값이 들어가지 않아 person.getGame()의 반환값이 null이기 때문에 null.getWeapon() 메서드가 작동하지 않기 때문이다.
Java8 이전에는 중첩 조건문을 사용해 회피했다.
public static void main(String[] args) {
Person person = new Person("aaa");
Game game = person.getGame();
if (game != null) {
Weapon weapon = game.getWeapon();
if (weapon != null) {
String w = weapon.printWeapon();
}
}
}
이러한 방식은 메서드가 많아질수록 if문 역시 증가되어 코드 가독성 측면에서 좋지 않은 방식이라 할 수 있다.
Java8이 등장하며 null 처리를 정식으로 지원하는 java.util.Optional 클래스가 추가되었다.
public final class Optional<T> {
/**
*if non-null, the value, if null, indicates no value is present
*/
private final T value;
}
Optional 클래스는 존재할수도 하지않을수도 있는 객체, null이 될 수도 있는 객체를 감싸는 래퍼 클래스이다.
public static void main(String[] args) {
Person person = new Person("aaa");
Optional<String> optionalWeapon = Optional.ofNullable(person)
.map(Person::getGame)
.map(Game::getWeapon)
.map(Weapon::printWeapon);
}
Optional을 사용하면 이전의 중첩 조건문을 위와 같이 간단하게 바꿀 수 있다.