null이 올 수 있는 값을 감싸는 Wrapper 클래스
null일수도 있는 값에 optional을 이용하여 npe를 방지하거나, null체크 로직을 깔끔하게 작성할 수 있다.
자바8 부터 제공하는 함수형인터페이스중에 supplier와 Consumer를 이용하여 Optional을 따라해봤다.
class이름은 SimpleOptional로 했다.
기존 Optional에서 제공해주는 메서드중 일부 메서드만 구현 했다.
구현한 메서드는 of, ofNullable, orElseGet, ifPresent 이다.

SimpleOptional에 사용할 간단한 Member클래스를 만들었다.
이제 SimpleOptional을 구현해보자.
우선 SimpleOptional클래스를 만든다.

다양한 타입을 받을 수 있게 제네릭타입으로 구현했다.
그리고 제네릭 타입(T)를 타입으로한 t를 인스턴스 변수로 선언하고 기본생성자와, 인스턴스 변수 t를 매개변수로 받아 주입해주는 생성자를 선언했다. 그리고 외부에서 new를 이용하여 객체를 생성하는것을 막기 위하여 접근 제어자를 private으로 설정하였다.
그리고 Null값을 받을 수 없게 Objects.requireNonNull을 사용했다.
Objects.requireNonNull을 사용하면 인자로 넘겨준 값이 null이면 NPE를 발생시킨다. of메서드와 ofNullable메서드를 구분하기 위하여 사용하였다.
이제 Optional에 주로 사용되는 메서드를 구현해보자.
Optional에서 Optional을 생성하는 메서드로 of와 ofNullable을 제공한다.
차이점은 of는 null값을 인자로 넘길 경우 NPE를 발생시키고, ofNullable은 null값을 인자로 넘길 경우 빈 Optional객체를 반환시켜준다.
of메서드 부터 구현해보겠다.

간단하다. SimpleOptioanl로 감쌀 값을 매개변수로 받아 SimpleOptioanl을 생성한다.
위에서 of는 null값을 인자로 넘길경우 NPE를 발생시킨다고 했지만 별다른 null체크 로직이 없다.
그럼에도 NPE를 발생시킬 수 있는 이유가 생성자에서 t에 받은 값을 Objects.requireNonNull을 이용하여 주입받았기 때문이다. Objects.requireNonNull은 null값을 받으면 NPE를 발생시킨다.
of메서드를 깔끔하게 구현하기 위하여 생성자에서 Objects.requireNonNull을 사용하였다.
다음은 ofNullable이다.

ofNullable은 SimpleOptional로 감쌀 값을 매개변수로 받은 후에 받은 값이 null인지 체크한다.
null이면 기본 생성자를 이용하여 빈 SimpleOptional객체를 반환하고, null이 아니면 of메서드를 이용하여 매개변수로 받은 값을 SimpleOptional로 감싸 반환해준다.
다음은 of와 ofNullable로 생성한 SimpleOptional객체로 null을 처리할 수 있는 메서드들을 구현해보자.
Optional에서 Optional안에 있는 값을 꺼내는 방법은 다양하다.
이 중 가장 많이 쓰인다고 생각하는 orElseGet과 orElseThrow를 구현해보겠다.
orElseGet은 Optional 값이 null일 경우, 임의의 값을 반환해준다.
임의의 값은 매개변수로 받는 supplier 인터페이스를 이용하여 반환해준다.

orElseGet은 해당 SimpleOptional의 인스턴스 변수 t가 null일 경우, 매개변수로 받은 supplier를 이용하여 반환해준다.
null이 아닐 경우 SimpleOptional내에 있는 값을 반환해준다.
supplier의 제네릭을 <? extends T>로 한 이유는, SimpleOptional내의 클래스의 자식클래스들은 부모 클래스로 타입 선언이 가능하기 때문이다. 그래서 해당 값의 타입과 그 값의 자식 클래스외의 타입이 매개변수로 넘어와선 안되기 때문이다.
orElseThrow는 Optioanl의 값이 null일 경우, 예외를 발생시킨다.

orElseThrow는 해당 SimpleOptional의 인스턴스 변수 t가 null일 경우, 매개변수로 받은 supplier을 실행하여 예외를 던진다.
supplier의 제네릭 타입을 <? extends RuntimeException>으로 하여 비검사 예외만 받을 수 있게 하였다.
다음은 값을 꺼내는게 아닌 null이 아닐 경우 실행할 로직을 전달 할 수 있는 ifPresent를 구현해보겠다.
Optional에서 ifPresent는 consumer인터페이스를 매개 변수로 받아 해당 값이 null이 아닐 경우, 매개 변수로 받은 consumer를 실행 시킨다.

ifPresent는 해당 SimpleOptional의 인스턴스 변수 t가 null일 경우, 매개변수로 받은 consumer을 실행한다.
이 ifPresent메서드를 이용하여 SimpleOptional의 값이 null이 아닌 경우 실행할 로직을 값을 꺼내지 않고 전달 할 수 있다.
매개변수의 타입을 <? super T>를 한 이유는 만약 해당 값이 어떤 클래스의 자식 클래스인 경우, 부모 클래스의 메서드를 호출할 순 있지만, 자식클래스의 메서드는 호출 할 수 없기 때문이다. 따라서 해당 값의 부모클래스가 아닌 클래스가 매개변수로 넘어와선 안되기 때문이다.
이제 직접 구현한 SimpleOptional이 정상적으로 동작하는지 테스트해보자.
우선 of메서드에 null값을 전달할 경우 NPE가 발생하는지 테스트 해보겠다.

테스트가 성공한다.
다음은 ofNullable메서드에 null값을 전달할 경우 비어있는SimpleOptional객체가 반환되는지 테스트 해보겠다.

null값으로 SimpleOptional객체를 생성할 경우 NPE가 발생되지 않고 get메서드를 이용하여 값을 꺼낼경우 ofNullable메서드에 인자로 넘겼던 null이 반환된다.
get메서드는 단순히 SimpleOptional내의 값을 반환해주는 메서드다.
다음은 orElseGet을 테스트 해보겠다.

null로 SimpleOptional을 생성하여, 값이 null이면 해당 Member를 생성하여 반환해준다.
정상적으로 테스트가 통과한다.
다음은 orElseThrow를 테스트 해보겠다.

orElseGet과 마찬가지로 null로 SimpleOptional을 생성하여, 값이 null이면IllegalStateException을 발생시킨다.
테스트 역시 통과된다.
다음은 마지막으로 ifPresent를 테스트 해보겠다.

이번에는 Member를 생성하여 SimpleOptional에 감싼다.
이후 ifPresent메서드를 실행한다. 값이 null이 아닐 경우 setUsername을 호출하여 기존 name에서 username으로 변경이 된다.
테스트가 정상적으로 통과된다.
SimpleOptional에서 구현한 모든 메서드가 정상적으로 동작하는것을 테스트를 통해 확인했다.