SlideShare a Scribd company logo
1 of 112
Goodbye Null
LazySoul
우명인
null ?
null?
• ‘값이 없음’을 나타내는 영단어, 값이 없다는 것은 그 값이 0
조차 아니라는 뜻이다. 독일어 에선 숫자 0을 뜻한다.
• ASCII코드는 0, 유니 코드는 U+0000 이 널 문자다.
• C 프로그래밍에서는 ‘존재하지 않는 메모리 주소’를 NULL
로 나타낸다.
• 존재 하지 않는 값 을 나타낸다.
null 이 왜?
• null 을 고안한 Tony Hore는 Billion Dollar Mistake 라고 후
회 함
null 이 왜?
null 이 왜?
class Person {
long id;
String name;
Dog dog;
Person(long id, String name, Dog dog) {
this.id = id;
this.name = name;
this.dog = dog;
}
}
null 이 왜?
class Dog {
String name;
Something something;
Dog(String name, Something something) {
this.name = name;
this.something = something;
}
}
null 이 왜?
class Something {
String name;
Something(String name) {
this.name = name;
}
}
null 이 왜?
Person
Dog
Something
public Person findPersonByName(String name) {
long id = db.findPersonIdByName(name);
if (id == -1) {
return null;
} else {
return new Person(id, name, null);
}
}
null 이 왜?
null 이 왜?
Person person = findPersonByName("lazysoul");
/* person is nonNull */
String name = person.name;
null 이 왜?
Person person = findPersonByName("lazysoul");
/* person is null */
String name = person.name;
null 이 왜?
Person person = findPersonByName("lazysoul");
/* person is null */
String name = person.name; // NullPointerException
null 이 왜?
Person person = findPersonByName("lazysoul");
/* person is null */
String name = person.name; // NullPointerException
• 런타임에 의도치 않은 NPE 발생시킨다.
어떻게 NPE를 피할까?
어떻게 NPE를 피할까?
• 명령형 프로그래밍 회피
• 추상화를 통한 회피
• 언어적 회피 - Kotlin
• FP 적 회피
명령형 프로그래밍 회
피
명령형 프로그래밍 회피
Person person = findPersonByName("lazysoul");
/* person is null */
String name = person.name; // NullPointerException
명령형 프로그래밍 회피
Person person = findPersonByName(“lazysoul");
/* person is null */
String name = "default";
if (person != null) {
name = person.name;
}
명령형 프로그래밍 회피
Person person = findPersonByName(“lazysoul");
/* person is null */
String name = "default";
if (person != null) {
name = person.name;
} Dog 의 name 을 가져오고 싶다면?
명령형 프로그래밍 회피
Person person = findPersonByName("lazysoul");
/* person is null */
String name = “default";
if (person != null) {
Dog dog = person.dog;
if (dog != null) {
name = dog.name;
}
}
Person person = findPersonByName("lazysoul");
/* person is null */
String name = "default";
if (person != null) {
Dog dog = person.dog;
if (dog != null) {
name = dog.name;
}
}
명령형 프로그래밍 회피
Something name을 가져오고 싶다면?
Person person = findPersonByName("lazysoul");
/* person is null */
String name = "default";
if (person != null) {
Dog dog = person.dog;
if (dog != null) {
Something something = dog.something;
if(something != null){
name = something.name;
}
}
}
명령형 프로그래밍 회피
명령형 프로그래밍 회피
• NPE 를 방지 할 수 있다.
명령형 프로그래밍 회피
• NPE 를 방지 할 수 있다.
• 깊이가 깊어질수록 가독성이 좋지 않다.
명령형 프로그래밍 회피
• NPE 를 방지 할 수 있다.
• 깊이가 깊어질수록 가독성이 좋지 않다.
• 보일러 플레이트
추상화를 통한 회피
추상화를 통한 회피
Person
Dog
Something
AbstractSomething
Something NullSomething
추상화를 통한 회피
abstract class AbstractSomething {
String name;
abstract boolean isNull();
}
class Something extends AbstractSomething {
Something(String name) {
this.name = name;
}
@Override
boolean isNull() {
return false;
}
}
class NullSomething extends AbstractSomething {
NullSomething() {
name = “something is null";
}
@Override
boolean isNull() {
return true;
}
}
추상화를 통한 회피
abstract class AbstractSomething {
String name;
abstract boolean isNull();
}
class Something extends AbstractSomething {
Something(String name) {
this.name = name;
}
@Override
boolean isNull() {
return false;
}
}
class NullSomething extends AbstractSomething {
NullSomething() {
name = “something is null";
}
@Override
boolean isNull() {
return true;
}
}
추상화를 통한 회피
abstract class AbstractSomething {
String name;
abstract boolean isNull();
}
class Something extends AbstractSomething {
Something(String name) {
this.name = name;
}
@Override
boolean isNull() {
return false;
}
}
class NullSomething extends AbstractSomething {
NullSomething() {
name = “something is null";
}
@Override
boolean isNull() {
return true;
}
}
추상화를 통한 회피
abstract class AbstractSomething {
String name;
abstract boolean isNull();
}
class Something extends AbstractSomething {
Something(String name) {
this.name = name;
}
@Override
boolean isNull() {
return false;
}
}
class NullSomething extends AbstractSomething {
NullSomething() {
name = “something is null";
}
@Override
boolean isNull() {
return true;
}
}
추상화를 통한 회피
AbstractDog
Dog NullDog
추상화를 통한 회피
abstract class AbstractDog {
String name;
AbstractSomething something;
abstract boolean isNull();
}
class Dog extends AbstractDog {
Dog(String name, AbstractSomething something) {
this.name = name;
this.something = something;
}
@Override
boolean isNull() {
return false;
}
}
class NullDog extends AbstractDog {
NullDog() {
name = "dog is null";
something = new NullSomething();
}
@Override
boolean isNull() {
return true;
추상화를 통한 회피
abstract class AbstractDog {
String name;
AbstractSomething something;
abstract boolean isNull();
}
class Dog extends AbstractDog {
Dog(String name, AbstractSomething something) {
this.name = name;
this.something = something;
}
@Override
boolean isNull() {
return false;
}
}
class NullDog extends AbstractDog {
NullDog() {
name = "dog is null";
something = new NullSomething();
}
@Override
boolean isNull() {
return true;
추상화를 통한 회피
abstract class AbstractDog {
String name;
AbstractSomething something;
abstract boolean isNull();
}
class Dog extends AbstractDog {
Dog(String name, AbstractSomething something) {
this.name = name;
this.something = something;
}
@Override
boolean isNull() {
return false;
}
}
class NullDog extends AbstractDog {
NullDog() {
name = "dog is null";
something = new NullSomething();
}
@Override
boolean isNull() {
return true;
추상화를 통한 회피
abstract class AbstractDog {
String name;
AbstractSomething something;
abstract boolean isNull();
}
class Dog extends AbstractDog {
Dog(String name, AbstractSomething something) {
this.name = name;
this.something = something;
}
@Override
boolean isNull() {
return false;
}
}
class NullDog extends AbstractDog {
NullDog() {
name = "dog is null";
something = new NullSomething();
}
@Override
boolean isNull() {
return true;
추상화를 통한 회피
AbstractPerson
Person NullPerson
추상화를 통한 회피
abstract class AbstractPerson {
long id;
String name;
AbstractDog dog;
abstract boolean isNull();
}
class Person extends AbstractPerson {
Person(long id, String name, AbstractDog dog) {
this.id = id;
this.name = name;
this.dog = dog;
}
@Override
boolean isNull() {
return false;
}
}
class NullPerson extends AbstractPerson {
NullPerson() {
this.id = -1;
this.name = "person is null";
this.dog = new NullDog();
}
@Override
boolean isNull() {
return true;
}
추상화를 통한 회피
abstract class AbstractPerson {
long id;
String name;
AbstractDog dog;
abstract boolean isNull();
}
class Person extends AbstractPerson {
Person(long id, String name, AbstractDog dog) {
this.id = id;
this.name = name;
this.dog = dog;
}
@Override
boolean isNull() {
return false;
}
}
class NullPerson extends AbstractPerson {
NullPerson() {
this.id = -1;
this.name = "person is null";
this.dog = new NullDog();
}
@Override
boolean isNull() {
return true;
}
추상화를 통한 회피
abstract class AbstractPerson {
long id;
String name;
AbstractDog dog;
abstract boolean isNull();
}
class Person extends AbstractPerson {
Person(long id, String name, AbstractDog dog) {
this.id = id;
this.name = name;
this.dog = dog;
}
@Override
boolean isNull() {
return false;
}
}
class NullPerson extends AbstractPerson {
NullPerson() {
this.id = -1;
this.name = "person is null";
this.dog = new NullDog();
}
@Override
boolean isNull() {
return true;
}
추상화를 통한 회피
abstract class AbstractPerson {
long id;
String name;
AbstractDog dog;
abstract boolean isNull();
}
class Person extends AbstractPerson {
Person(long id, String name, AbstractDog dog) {
this.id = id;
this.name = name;
this.dog = dog;
}
@Override
boolean isNull() {
return false;
}
}
class NullPerson extends AbstractPerson {
NullPerson() {
this.id = -1;
this.name = "person is null";
this.dog = new NullDog();
}
@Override
boolean isNull() {
return true;
}
추상화를 통한 회피
public Person findPersonByName(String name) {
long id = db.findPersonIdByName(name);
if (id == -1) {
return null;
} else {
return new Person(id, name, null);
}
}
추상화를 통한 회피
public AbstractPerson findPersonByName(String name) {
long id = db.findPersonIdByName(name);
if (id == -1) {
return new NullPerson();
} else {
return new Person(id, name, null);
}
}
추상화를 통한 회피
추상화를 통한 회피
Person person = findPersonByName("lazysoul");
/* person is null */
String name = person.name; // NullPointerException
AbstractPerson person = findPersonByName(“lazysoul");
/* person is null */
String name = person.name; // Nothing
추상화를 통한 회피
Dog 의 name 을 가져오고 싶다면?
추상화를 통한 회피
AbstractPerson person = findPersonByName(“lazysoul");
/* person is null */
String name = person.name; // Nothing
AbstractPerson person = findPersonByName("lazysoul");
/* person is null */
String name = "default";
AbstractDog dog = person.dog;
name = dog.name;
추상화를 통한 회피
Something 의 name 을 가져오고 싶다면?
추상화를 통한 회피
AbstractPerson person = findPersonByName("lazysoul");
/* person is null */
String name = "default";
AbstractDog dog = person.dog;
name = dog.name;
추상화를 통한 회피
AbstractPerson person = findPersonByName("lazysoul");
/* person is null */
String name = "default";
AbstractDog dog = person.dog;
AbstractSomething something = dog.something;
name = something.name;
• 런타임에 NPE가 발생하지 않는다.
추상화를 통한 회피
AbstractPerson person = findPersonByName("lazysoul");
/* person is null */
String name = "default";
AbstractDog dog = person.dog;
AbstractSomething something = dog.something;
name = something.name;
• 런타임에 NPE가 발생하지 않는다.
• 가독성이 좋다.
추상화를 통한 회피
AbstractPerson person = findPersonByName("lazysoul");
/* person is null */
String name = "default";
AbstractDog dog = person.dog;
AbstractSomething something = dog.something;
name = something.name;
• 런타임에 NPE가 발생하지 않는다.
• 가독성이 좋다.
• 의미 없는 값을 출력 한다. (crash 가 좋을 수도 있다.)
추상화를 통한 회피
AbstractPerson person = findPersonByName("lazysoul");
/* person is null */
String name = "default";
AbstractDog dog = person.dog;
AbstractSomething something = dog.something;
name = something.name;
nonNull인 경우에만 특정 비지니스 로직을 수행하려면?
추상화를 통한 회피
AbstractPerson person = findPersonByName("lazysoul");
/* person is null */
String name = "default";
AbstractDog dog = person.dog;
AbstractSomething something = dog.something;
name = something.name;
추상화를 통한 회피
AbstractPerson person = findPersonByName("lazysoul");
String name = "default";
if (!person.isNull()) {
AbstractDog dog = person.dog;
if (!dog.isNull()) {
AbstractSomething something = dog.something;
if (!something.isNull()) {
name = something.name;
}
}
}
AbstractPerson person = findPersonByName("lazysoul");
String name = "default";
if (!person.isNull()) {
AbstractDog dog = person.dog;
if (!dog.isNull()) {
AbstractSomething something = dog.something;
if (!something.isNull()) {
name = something.name;
}
}
}
추상화를 통한 회피
AbstractPerson person = findPersonByName("lazysoul");
String name = "default";
if (!person.isNull()) {
AbstractDog dog = person.dog;
if (!dog.isNull()) {
AbstractSomething something = dog.something;
if (!something.isNull()) {
name = something.name;
}
}
}
• nonNull을 의미하는 값에 접근을 하려면 어차피 null check
를 해야 한다.
추상화를 통한 회피
• 런타임에 NPE가 발생하지 않는다.
추상화를 통한 회피
• 런타임에 NPE가 발생하지 않는다.
• 가독성이 좋다.
추상화를 통한 회피
• 런타임에 NPE가 발생하지 않는다.
• 가독성이 좋다.
• 의미 없는 값을 출력 한다. (crash 가 좋을 수도 있다.)
추상화를 통한 회피
• 런타임에 NPE가 발생하지 않는다.
• 가독성이 좋다.
• 의미 없는 값을 출력 한다. (crash 가 좋을 수도 있다.)
• nonNull을 의미하는 값에 접근을 하려면 어차피 null check
를 해야 한다.
추상화를 통한 회피
• 런타임에 NPE가 발생하지 않는다.
• 가독성이 좋다.
• 의미 없는 값을 출력 한다. (crash 가 좋을 수도 있다.)
• nonNull을 의미하는 값에 접근을 하려면 어차피 null check
를 해야 한다.
• Abstract class, 실제 class, nullableClass 를 만들어줘야 하
기 때문에 보일러 플레이트들이 많다.
추상화를 통한 회피
언어적 회피 - Kotlin
언어적 회피 - Kotlin
data class Person(val id: Long, val name: String, val dog: Dog?)
data class Dog(val name: String, val something: Something?)
data class Something(val name: String)
언어적 회피 - Kotlin
data class Person(val id: Long, val name: String, val dog: Dog?)
data class Dog(val name: String, val something: Something?)
data class Something(val name: String)
언어적 회피 - Kotlin
val nullable: String? = null
언어적 회피 - Kotlin
val nullable: String? = null
val nonNull: String = null
//Null can not be a value of a non-null type String
언어적 회피 - Kotlin
val nullable: String? = null
val result: String = nullable ?: "default"
언어적 회피 - Kotlin
val nullable: String? = null
val result: String = nullable ?: "default"
언어적 회피 - Kotlin
Person person = findPersonByName("lazysoul");
/* person is null */
String name = person.name; // NullPointerException
언어적 회피 - Kotlin
/* person is null */
val name: String? = findPersonByName("lazysoul")
?.name ?: "default"
언어적 회피 - Kotlin
Dog 의 name 을 가져오고 싶다면?
/* person is null */
val name: String? = findPersonByName("lazysoul")
?.name ?: "default"
언어적 회피 - Kotlin
/* person is null */
val name: String? = findPersonByName("lazysoul")
?.dog
?.name ?: "default"
언어적 회피 - Kotlin
Something 의 name 을 가져오고 싶다면?
/* person is null */
val name: String? = findPersonByName("lazysoul")
?.dog
?.name ?: "default"
언어적 회피 - Kotlin
/* person is null */
val name: String? = findPersonByName("lazysoul")
?.dog
?.something
?.name ?: "default"
언어적 회피 - Kotlin
Person person = findPersonByName("lazysoul");
/* person is null */
String name = “default";
if (person != null) {
Dog dog = person.dog;
if (dog != null) {
Something something = dog.something;
if(something != null){
name = something.name;
}
}
}
/* person is null */
val name: String? = findPersonByName("lazysoul")
?.dog
?.something
?.name ?: "default"
언어적 회피 - Kotlin
• 런타임에 NPE 를 차단 할수 있다.
언어적 회피 - Kotlin
• 런타임에 NPE 를 차단 할수 있다.
• 가독성이 좋다.
언어적 회피 - Kotlin
• 런타임에 NPE 를 차단 할수 있다.
• 가독성이 좋다.
• 보일러플레이트가 적다.
언어적 회피 - Kotlin
• 런타임에 NPE 를 차단 할수 있다.
• 가독성이 좋다.
• 보일러플레이트가 적다.
• 언어에 의존적이다.
FP 적 회피
FP 적 회피
Optional
FP 적 회피
String value = “lazysoul”
String value = null
FP 적 회피
String value = “lazysoul”
String value = null
Optional.of(value)
Optional.of(value)
FP 적 회피
String value = “lazysoul”
String value = null
Optional.of(value)
Optional.of(value)
lazysoul
Optional
FP 적 회피
NPE
String value = “lazysoul”
String value = null
Optional.of(value)
Optional.of(value)
lazysoul
Optional
FP 적 회피
String value = “lazysoul”
String value = null
FP 적 회피
String value = “lazysoul”
String value = null
Optional.ofNullable(value)
Optional.ofNullable(value)
FP 적 회피
lazysoul
Optional
String value = “lazysoul”
String value = null
Optional.ofNullable(value)
Optional.ofNullable(value)
FP 적 회피
lazysoul
Optional
String value = “lazysoul”
String value = null
Optional.ofNullable(value)
Optional.ofNullable(value)
empty
Optional
FP 적 회피
class Something {
String name;
Something(String name) {
this.name = name;
}
}
FP 적 회피
class Dog {
String name;
private Something something;
Dog(String name, Something something) {
this.name = name;
this.something = something;
}
public Optional<Something> getSomething() {
return Optional.ofNullable(something);
}
}
FP 적 회피
class Dog {
String name;
private Something something;
Dog(String name, Something something) {
this.name = name;
this.something = something;
}
public Optional<Something> getSomething() {
return Optional.ofNullable(something);
}
}
FP 적 회피
class Person {
long id;
String name;
private Dog dog;
Person(long id, String name, Dog dog) {
this.id = id;
this.name = name;
this.dog = dog;
}
public Optional<Dog> getDog() {
return Optional.ofNullable(dog);
}
}
FP 적 회피
class Person {
long id;
String name;
private Dog dog;
Person(long id, String name, Dog dog) {
this.id = id;
this.name = name;
this.dog = dog;
}
public Optional<Dog> getDog() {
return Optional.ofNullable(dog);
}
}
FP 적 회피
public Optional<Person> findPersonByName(String name) {
long id = db.findPersonIdByName(name);
if (id == -1) {
return Optional.empty();
} else {
return Optional.of(new Person(id, name, null));
}
}
FP 적 회피
Optional<Person> person = findPersonByName("lazysoul");
/* person is empty */
String name = “default";
if (person.isPresent()) {
name = person.get().name;
}
FP 적 회피
Optional<Person> person = findPersonByName("lazysoul");
/* person is empty */
String name = “default";
if (person.isPresent()) {
name = person.get().name;
}
FP 적 회피
Dog 의 name 을 가져오고 싶다면?
Optional<Person> person = findPersonByName("lazysoul");
/* person is empty */
String name = “default";
if (person.isPresent()) {
name = person.get().name;
}
FP 적 회피
Optional<Person> person = findPersonByName("lazysoul");
/* person is empty */
String name = "default";
if (person.isPresent()) {
Optional<Dog> dog = person.get().getDog();
if (dog.isPresent()) {
name = dog.get().name;
}
}
FP 적 회피
Something 의 name 을 가져오고 싶다면?
Optional<Person> person = findPersonByName("lazysoul");
/* person is empty */
String name = "default";
if (person.isPresent()) {
Optional<Dog> dog = person.get().getDog();
if (dog.isPresent()) {
name = dog.get().name;
}
}
FP 적 회피
Optional<Person> person = findPersonByName("lazysoul");
/* person is empty */
String name = “default";
if (person.isPresent()) {
Optional<Dog> dog = person.get().getDog();
if (dog.isPresent()) {
Optional<Something> something = dog.get().getSomething();
if(something.isPresent()){
name = something.get().name;
}
}
}
FP 적 회피
뭐가 이렇게 복잡해??
Optional<Person> person = findPersonByName("lazysoul");
/* person is empty */
String name = “default";
if (person.isPresent()) {
Optional<Dog> dog = person.get().getDog();
if (dog.isPresent()) {
Optional<Something> something = dog.get().getSomething();
if(something.isPresent()){
name = something.get().name;
}
}
}
FP 적 회피
flatMap
Map
FP 적 회피
flatMap
value<T> <R>
Optional<R>
value<T>
Optional<T>
FP 적 회피
map
value<T> value<R> <R>
Optional<R>
value<T>
Optional<T>
FP 적 회피
String name = findPersonByName("lazysoul")
.flatMap(Person::getDog)
.flatMap(Dog::getSomething)
.map((something) -> something.name)
.orElse("default");
Person person = findPersonByName("lazysoul");
/* person is null */
String name = "default";
if (person != null) {
Dog dog = person.dog;
if (dog != null) {
Something something = dog.something;
if(something != null) {
name = something.name;
}
}
}
FP 적 회피
• 런타임에 NPE가 발생하지 않는다.
FP 적 회피
• 런타임에 NPE가 발생하지 않는다.
• 가독성이 좋다.
FP 적 회피
• 런타임에 NPE가 발생하지 않는다.
• 가독성이 좋다.
• Optional 을 재사용 할수 있다.
Optional<QnA>

More Related Content

More from Myeongin Woo

DroidKnight 2018 State machine by Selaed class
DroidKnight 2018 State machine by Selaed classDroidKnight 2018 State machine by Selaed class
DroidKnight 2018 State machine by Selaed classMyeongin Woo
 
Lezhin kotlin jetbrain
Lezhin kotlin jetbrainLezhin kotlin jetbrain
Lezhin kotlin jetbrainMyeongin Woo
 
Kotlin collections
Kotlin collectionsKotlin collections
Kotlin collectionsMyeongin Woo
 
토이 프로젝트를 하자.Pptx
토이 프로젝트를 하자.Pptx토이 프로젝트를 하자.Pptx
토이 프로젝트를 하자.PptxMyeongin Woo
 

More from Myeongin Woo (10)

Fp basic-kotlin
Fp basic-kotlinFp basic-kotlin
Fp basic-kotlin
 
DroidKnight 2018 State machine by Selaed class
DroidKnight 2018 State machine by Selaed classDroidKnight 2018 State machine by Selaed class
DroidKnight 2018 State machine by Selaed class
 
Lezhin kotlin jetbrain
Lezhin kotlin jetbrainLezhin kotlin jetbrain
Lezhin kotlin jetbrain
 
Kotlin collections
Kotlin collectionsKotlin collections
Kotlin collections
 
Kotlin standard
Kotlin standardKotlin standard
Kotlin standard
 
Kotlin class
Kotlin classKotlin class
Kotlin class
 
Kotlin expression
Kotlin expressionKotlin expression
Kotlin expression
 
Kotlin with fp
Kotlin with fpKotlin with fp
Kotlin with fp
 
토이 프로젝트를 하자.Pptx
토이 프로젝트를 하자.Pptx토이 프로젝트를 하자.Pptx
토이 프로젝트를 하자.Pptx
 
Kotlin.md
Kotlin.mdKotlin.md
Kotlin.md
 

Goodbye null

  • 3. null? • ‘값이 없음’을 나타내는 영단어, 값이 없다는 것은 그 값이 0 조차 아니라는 뜻이다. 독일어 에선 숫자 0을 뜻한다. • ASCII코드는 0, 유니 코드는 U+0000 이 널 문자다. • C 프로그래밍에서는 ‘존재하지 않는 메모리 주소’를 NULL 로 나타낸다. • 존재 하지 않는 값 을 나타낸다.
  • 4. null 이 왜? • null 을 고안한 Tony Hore는 Billion Dollar Mistake 라고 후 회 함
  • 6. null 이 왜? class Person { long id; String name; Dog dog; Person(long id, String name, Dog dog) { this.id = id; this.name = name; this.dog = dog; } }
  • 7. null 이 왜? class Dog { String name; Something something; Dog(String name, Something something) { this.name = name; this.something = something; } }
  • 8. null 이 왜? class Something { String name; Something(String name) { this.name = name; } }
  • 10. public Person findPersonByName(String name) { long id = db.findPersonIdByName(name); if (id == -1) { return null; } else { return new Person(id, name, null); } } null 이 왜?
  • 11. null 이 왜? Person person = findPersonByName("lazysoul"); /* person is nonNull */ String name = person.name;
  • 12. null 이 왜? Person person = findPersonByName("lazysoul"); /* person is null */ String name = person.name;
  • 13. null 이 왜? Person person = findPersonByName("lazysoul"); /* person is null */ String name = person.name; // NullPointerException
  • 14. null 이 왜? Person person = findPersonByName("lazysoul"); /* person is null */ String name = person.name; // NullPointerException • 런타임에 의도치 않은 NPE 발생시킨다.
  • 16. 어떻게 NPE를 피할까? • 명령형 프로그래밍 회피 • 추상화를 통한 회피 • 언어적 회피 - Kotlin • FP 적 회피
  • 18. 명령형 프로그래밍 회피 Person person = findPersonByName("lazysoul"); /* person is null */ String name = person.name; // NullPointerException
  • 19. 명령형 프로그래밍 회피 Person person = findPersonByName(“lazysoul"); /* person is null */ String name = "default"; if (person != null) { name = person.name; }
  • 20. 명령형 프로그래밍 회피 Person person = findPersonByName(“lazysoul"); /* person is null */ String name = "default"; if (person != null) { name = person.name; } Dog 의 name 을 가져오고 싶다면?
  • 21. 명령형 프로그래밍 회피 Person person = findPersonByName("lazysoul"); /* person is null */ String name = “default"; if (person != null) { Dog dog = person.dog; if (dog != null) { name = dog.name; } }
  • 22. Person person = findPersonByName("lazysoul"); /* person is null */ String name = "default"; if (person != null) { Dog dog = person.dog; if (dog != null) { name = dog.name; } } 명령형 프로그래밍 회피 Something name을 가져오고 싶다면?
  • 23. Person person = findPersonByName("lazysoul"); /* person is null */ String name = "default"; if (person != null) { Dog dog = person.dog; if (dog != null) { Something something = dog.something; if(something != null){ name = something.name; } } } 명령형 프로그래밍 회피
  • 24. 명령형 프로그래밍 회피 • NPE 를 방지 할 수 있다.
  • 25. 명령형 프로그래밍 회피 • NPE 를 방지 할 수 있다. • 깊이가 깊어질수록 가독성이 좋지 않다.
  • 26. 명령형 프로그래밍 회피 • NPE 를 방지 할 수 있다. • 깊이가 깊어질수록 가독성이 좋지 않다. • 보일러 플레이트
  • 30. abstract class AbstractSomething { String name; abstract boolean isNull(); } class Something extends AbstractSomething { Something(String name) { this.name = name; } @Override boolean isNull() { return false; } } class NullSomething extends AbstractSomething { NullSomething() { name = “something is null"; } @Override boolean isNull() { return true; } } 추상화를 통한 회피
  • 31. abstract class AbstractSomething { String name; abstract boolean isNull(); } class Something extends AbstractSomething { Something(String name) { this.name = name; } @Override boolean isNull() { return false; } } class NullSomething extends AbstractSomething { NullSomething() { name = “something is null"; } @Override boolean isNull() { return true; } } 추상화를 통한 회피
  • 32. abstract class AbstractSomething { String name; abstract boolean isNull(); } class Something extends AbstractSomething { Something(String name) { this.name = name; } @Override boolean isNull() { return false; } } class NullSomething extends AbstractSomething { NullSomething() { name = “something is null"; } @Override boolean isNull() { return true; } } 추상화를 통한 회피
  • 33. abstract class AbstractSomething { String name; abstract boolean isNull(); } class Something extends AbstractSomething { Something(String name) { this.name = name; } @Override boolean isNull() { return false; } } class NullSomething extends AbstractSomething { NullSomething() { name = “something is null"; } @Override boolean isNull() { return true; } } 추상화를 통한 회피
  • 35. abstract class AbstractDog { String name; AbstractSomething something; abstract boolean isNull(); } class Dog extends AbstractDog { Dog(String name, AbstractSomething something) { this.name = name; this.something = something; } @Override boolean isNull() { return false; } } class NullDog extends AbstractDog { NullDog() { name = "dog is null"; something = new NullSomething(); } @Override boolean isNull() { return true; 추상화를 통한 회피
  • 36. abstract class AbstractDog { String name; AbstractSomething something; abstract boolean isNull(); } class Dog extends AbstractDog { Dog(String name, AbstractSomething something) { this.name = name; this.something = something; } @Override boolean isNull() { return false; } } class NullDog extends AbstractDog { NullDog() { name = "dog is null"; something = new NullSomething(); } @Override boolean isNull() { return true; 추상화를 통한 회피
  • 37. abstract class AbstractDog { String name; AbstractSomething something; abstract boolean isNull(); } class Dog extends AbstractDog { Dog(String name, AbstractSomething something) { this.name = name; this.something = something; } @Override boolean isNull() { return false; } } class NullDog extends AbstractDog { NullDog() { name = "dog is null"; something = new NullSomething(); } @Override boolean isNull() { return true; 추상화를 통한 회피
  • 38. abstract class AbstractDog { String name; AbstractSomething something; abstract boolean isNull(); } class Dog extends AbstractDog { Dog(String name, AbstractSomething something) { this.name = name; this.something = something; } @Override boolean isNull() { return false; } } class NullDog extends AbstractDog { NullDog() { name = "dog is null"; something = new NullSomething(); } @Override boolean isNull() { return true; 추상화를 통한 회피
  • 40. abstract class AbstractPerson { long id; String name; AbstractDog dog; abstract boolean isNull(); } class Person extends AbstractPerson { Person(long id, String name, AbstractDog dog) { this.id = id; this.name = name; this.dog = dog; } @Override boolean isNull() { return false; } } class NullPerson extends AbstractPerson { NullPerson() { this.id = -1; this.name = "person is null"; this.dog = new NullDog(); } @Override boolean isNull() { return true; } 추상화를 통한 회피
  • 41. abstract class AbstractPerson { long id; String name; AbstractDog dog; abstract boolean isNull(); } class Person extends AbstractPerson { Person(long id, String name, AbstractDog dog) { this.id = id; this.name = name; this.dog = dog; } @Override boolean isNull() { return false; } } class NullPerson extends AbstractPerson { NullPerson() { this.id = -1; this.name = "person is null"; this.dog = new NullDog(); } @Override boolean isNull() { return true; } 추상화를 통한 회피
  • 42. abstract class AbstractPerson { long id; String name; AbstractDog dog; abstract boolean isNull(); } class Person extends AbstractPerson { Person(long id, String name, AbstractDog dog) { this.id = id; this.name = name; this.dog = dog; } @Override boolean isNull() { return false; } } class NullPerson extends AbstractPerson { NullPerson() { this.id = -1; this.name = "person is null"; this.dog = new NullDog(); } @Override boolean isNull() { return true; } 추상화를 통한 회피
  • 43. abstract class AbstractPerson { long id; String name; AbstractDog dog; abstract boolean isNull(); } class Person extends AbstractPerson { Person(long id, String name, AbstractDog dog) { this.id = id; this.name = name; this.dog = dog; } @Override boolean isNull() { return false; } } class NullPerson extends AbstractPerson { NullPerson() { this.id = -1; this.name = "person is null"; this.dog = new NullDog(); } @Override boolean isNull() { return true; } 추상화를 통한 회피
  • 44. public Person findPersonByName(String name) { long id = db.findPersonIdByName(name); if (id == -1) { return null; } else { return new Person(id, name, null); } } 추상화를 통한 회피
  • 45. public AbstractPerson findPersonByName(String name) { long id = db.findPersonIdByName(name); if (id == -1) { return new NullPerson(); } else { return new Person(id, name, null); } } 추상화를 통한 회피
  • 46. 추상화를 통한 회피 Person person = findPersonByName("lazysoul"); /* person is null */ String name = person.name; // NullPointerException
  • 47. AbstractPerson person = findPersonByName(“lazysoul"); /* person is null */ String name = person.name; // Nothing 추상화를 통한 회피
  • 48. Dog 의 name 을 가져오고 싶다면? 추상화를 통한 회피 AbstractPerson person = findPersonByName(“lazysoul"); /* person is null */ String name = person.name; // Nothing
  • 49. AbstractPerson person = findPersonByName("lazysoul"); /* person is null */ String name = "default"; AbstractDog dog = person.dog; name = dog.name; 추상화를 통한 회피
  • 50. Something 의 name 을 가져오고 싶다면? 추상화를 통한 회피 AbstractPerson person = findPersonByName("lazysoul"); /* person is null */ String name = "default"; AbstractDog dog = person.dog; name = dog.name;
  • 51. 추상화를 통한 회피 AbstractPerson person = findPersonByName("lazysoul"); /* person is null */ String name = "default"; AbstractDog dog = person.dog; AbstractSomething something = dog.something; name = something.name;
  • 52. • 런타임에 NPE가 발생하지 않는다. 추상화를 통한 회피 AbstractPerson person = findPersonByName("lazysoul"); /* person is null */ String name = "default"; AbstractDog dog = person.dog; AbstractSomething something = dog.something; name = something.name;
  • 53. • 런타임에 NPE가 발생하지 않는다. • 가독성이 좋다. 추상화를 통한 회피 AbstractPerson person = findPersonByName("lazysoul"); /* person is null */ String name = "default"; AbstractDog dog = person.dog; AbstractSomething something = dog.something; name = something.name;
  • 54. • 런타임에 NPE가 발생하지 않는다. • 가독성이 좋다. • 의미 없는 값을 출력 한다. (crash 가 좋을 수도 있다.) 추상화를 통한 회피 AbstractPerson person = findPersonByName("lazysoul"); /* person is null */ String name = "default"; AbstractDog dog = person.dog; AbstractSomething something = dog.something; name = something.name;
  • 55. nonNull인 경우에만 특정 비지니스 로직을 수행하려면? 추상화를 통한 회피 AbstractPerson person = findPersonByName("lazysoul"); /* person is null */ String name = "default"; AbstractDog dog = person.dog; AbstractSomething something = dog.something; name = something.name;
  • 56. 추상화를 통한 회피 AbstractPerson person = findPersonByName("lazysoul"); String name = "default"; if (!person.isNull()) { AbstractDog dog = person.dog; if (!dog.isNull()) { AbstractSomething something = dog.something; if (!something.isNull()) { name = something.name; } } }
  • 57. AbstractPerson person = findPersonByName("lazysoul"); String name = "default"; if (!person.isNull()) { AbstractDog dog = person.dog; if (!dog.isNull()) { AbstractSomething something = dog.something; if (!something.isNull()) { name = something.name; } } } 추상화를 통한 회피
  • 58. AbstractPerson person = findPersonByName("lazysoul"); String name = "default"; if (!person.isNull()) { AbstractDog dog = person.dog; if (!dog.isNull()) { AbstractSomething something = dog.something; if (!something.isNull()) { name = something.name; } } } • nonNull을 의미하는 값에 접근을 하려면 어차피 null check 를 해야 한다. 추상화를 통한 회피
  • 59. • 런타임에 NPE가 발생하지 않는다. 추상화를 통한 회피
  • 60. • 런타임에 NPE가 발생하지 않는다. • 가독성이 좋다. 추상화를 통한 회피
  • 61. • 런타임에 NPE가 발생하지 않는다. • 가독성이 좋다. • 의미 없는 값을 출력 한다. (crash 가 좋을 수도 있다.) 추상화를 통한 회피
  • 62. • 런타임에 NPE가 발생하지 않는다. • 가독성이 좋다. • 의미 없는 값을 출력 한다. (crash 가 좋을 수도 있다.) • nonNull을 의미하는 값에 접근을 하려면 어차피 null check 를 해야 한다. 추상화를 통한 회피
  • 63. • 런타임에 NPE가 발생하지 않는다. • 가독성이 좋다. • 의미 없는 값을 출력 한다. (crash 가 좋을 수도 있다.) • nonNull을 의미하는 값에 접근을 하려면 어차피 null check 를 해야 한다. • Abstract class, 실제 class, nullableClass 를 만들어줘야 하 기 때문에 보일러 플레이트들이 많다. 추상화를 통한 회피
  • 65. 언어적 회피 - Kotlin data class Person(val id: Long, val name: String, val dog: Dog?) data class Dog(val name: String, val something: Something?) data class Something(val name: String)
  • 66. 언어적 회피 - Kotlin data class Person(val id: Long, val name: String, val dog: Dog?) data class Dog(val name: String, val something: Something?) data class Something(val name: String)
  • 67. 언어적 회피 - Kotlin val nullable: String? = null
  • 68. 언어적 회피 - Kotlin val nullable: String? = null val nonNull: String = null //Null can not be a value of a non-null type String
  • 69. 언어적 회피 - Kotlin val nullable: String? = null val result: String = nullable ?: "default"
  • 70. 언어적 회피 - Kotlin val nullable: String? = null val result: String = nullable ?: "default"
  • 71. 언어적 회피 - Kotlin Person person = findPersonByName("lazysoul"); /* person is null */ String name = person.name; // NullPointerException
  • 72. 언어적 회피 - Kotlin /* person is null */ val name: String? = findPersonByName("lazysoul") ?.name ?: "default"
  • 73. 언어적 회피 - Kotlin Dog 의 name 을 가져오고 싶다면? /* person is null */ val name: String? = findPersonByName("lazysoul") ?.name ?: "default"
  • 74. 언어적 회피 - Kotlin /* person is null */ val name: String? = findPersonByName("lazysoul") ?.dog ?.name ?: "default"
  • 75. 언어적 회피 - Kotlin Something 의 name 을 가져오고 싶다면? /* person is null */ val name: String? = findPersonByName("lazysoul") ?.dog ?.name ?: "default"
  • 76. 언어적 회피 - Kotlin /* person is null */ val name: String? = findPersonByName("lazysoul") ?.dog ?.something ?.name ?: "default"
  • 77. 언어적 회피 - Kotlin Person person = findPersonByName("lazysoul"); /* person is null */ String name = “default"; if (person != null) { Dog dog = person.dog; if (dog != null) { Something something = dog.something; if(something != null){ name = something.name; } } } /* person is null */ val name: String? = findPersonByName("lazysoul") ?.dog ?.something ?.name ?: "default"
  • 78. 언어적 회피 - Kotlin • 런타임에 NPE 를 차단 할수 있다.
  • 79. 언어적 회피 - Kotlin • 런타임에 NPE 를 차단 할수 있다. • 가독성이 좋다.
  • 80. 언어적 회피 - Kotlin • 런타임에 NPE 를 차단 할수 있다. • 가독성이 좋다. • 보일러플레이트가 적다.
  • 81. 언어적 회피 - Kotlin • 런타임에 NPE 를 차단 할수 있다. • 가독성이 좋다. • 보일러플레이트가 적다. • 언어에 의존적이다.
  • 84. FP 적 회피 String value = “lazysoul” String value = null
  • 85. FP 적 회피 String value = “lazysoul” String value = null Optional.of(value) Optional.of(value)
  • 86. FP 적 회피 String value = “lazysoul” String value = null Optional.of(value) Optional.of(value) lazysoul Optional
  • 87. FP 적 회피 NPE String value = “lazysoul” String value = null Optional.of(value) Optional.of(value) lazysoul Optional
  • 88. FP 적 회피 String value = “lazysoul” String value = null
  • 89. FP 적 회피 String value = “lazysoul” String value = null Optional.ofNullable(value) Optional.ofNullable(value)
  • 90. FP 적 회피 lazysoul Optional String value = “lazysoul” String value = null Optional.ofNullable(value) Optional.ofNullable(value)
  • 91. FP 적 회피 lazysoul Optional String value = “lazysoul” String value = null Optional.ofNullable(value) Optional.ofNullable(value) empty Optional
  • 92. FP 적 회피 class Something { String name; Something(String name) { this.name = name; } }
  • 93. FP 적 회피 class Dog { String name; private Something something; Dog(String name, Something something) { this.name = name; this.something = something; } public Optional<Something> getSomething() { return Optional.ofNullable(something); } }
  • 94. FP 적 회피 class Dog { String name; private Something something; Dog(String name, Something something) { this.name = name; this.something = something; } public Optional<Something> getSomething() { return Optional.ofNullable(something); } }
  • 95. FP 적 회피 class Person { long id; String name; private Dog dog; Person(long id, String name, Dog dog) { this.id = id; this.name = name; this.dog = dog; } public Optional<Dog> getDog() { return Optional.ofNullable(dog); } }
  • 96. FP 적 회피 class Person { long id; String name; private Dog dog; Person(long id, String name, Dog dog) { this.id = id; this.name = name; this.dog = dog; } public Optional<Dog> getDog() { return Optional.ofNullable(dog); } }
  • 97. FP 적 회피 public Optional<Person> findPersonByName(String name) { long id = db.findPersonIdByName(name); if (id == -1) { return Optional.empty(); } else { return Optional.of(new Person(id, name, null)); } }
  • 98. FP 적 회피 Optional<Person> person = findPersonByName("lazysoul"); /* person is empty */ String name = “default"; if (person.isPresent()) { name = person.get().name; }
  • 99. FP 적 회피 Optional<Person> person = findPersonByName("lazysoul"); /* person is empty */ String name = “default"; if (person.isPresent()) { name = person.get().name; }
  • 100. FP 적 회피 Dog 의 name 을 가져오고 싶다면? Optional<Person> person = findPersonByName("lazysoul"); /* person is empty */ String name = “default"; if (person.isPresent()) { name = person.get().name; }
  • 101. FP 적 회피 Optional<Person> person = findPersonByName("lazysoul"); /* person is empty */ String name = "default"; if (person.isPresent()) { Optional<Dog> dog = person.get().getDog(); if (dog.isPresent()) { name = dog.get().name; } }
  • 102. FP 적 회피 Something 의 name 을 가져오고 싶다면? Optional<Person> person = findPersonByName("lazysoul"); /* person is empty */ String name = "default"; if (person.isPresent()) { Optional<Dog> dog = person.get().getDog(); if (dog.isPresent()) { name = dog.get().name; } }
  • 103. FP 적 회피 Optional<Person> person = findPersonByName("lazysoul"); /* person is empty */ String name = “default"; if (person.isPresent()) { Optional<Dog> dog = person.get().getDog(); if (dog.isPresent()) { Optional<Something> something = dog.get().getSomething(); if(something.isPresent()){ name = something.get().name; } } }
  • 104. FP 적 회피 뭐가 이렇게 복잡해?? Optional<Person> person = findPersonByName("lazysoul"); /* person is empty */ String name = “default"; if (person.isPresent()) { Optional<Dog> dog = person.get().getDog(); if (dog.isPresent()) { Optional<Something> something = dog.get().getSomething(); if(something.isPresent()){ name = something.get().name; } } }
  • 106. FP 적 회피 flatMap value<T> <R> Optional<R> value<T> Optional<T>
  • 107. FP 적 회피 map value<T> value<R> <R> Optional<R> value<T> Optional<T>
  • 108. FP 적 회피 String name = findPersonByName("lazysoul") .flatMap(Person::getDog) .flatMap(Dog::getSomething) .map((something) -> something.name) .orElse("default"); Person person = findPersonByName("lazysoul"); /* person is null */ String name = "default"; if (person != null) { Dog dog = person.dog; if (dog != null) { Something something = dog.something; if(something != null) { name = something.name; } } }
  • 109. FP 적 회피 • 런타임에 NPE가 발생하지 않는다.
  • 110. FP 적 회피 • 런타임에 NPE가 발생하지 않는다. • 가독성이 좋다.
  • 111. FP 적 회피 • 런타임에 NPE가 발생하지 않는다. • 가독성이 좋다. • Optional 을 재사용 할수 있다.

Editor's Notes

  1. name을 통해 person을 가져오는 일반적인 코드 입니다. 만약 해당 name에 해당하는 아이디가 없다면 null을 반환하고 아이디가 있다면 Person을 반환합니다.
  2. person이 null 이 아니라면 문제없다 .하지만 person이 null이라면?
  3. 뎁스가 계속해서 깊어진다. 따라서 가독성이 좋지 않다. 보일러 플레이트 들이 너무 많다. 뎁스가 길면 더 보기 힘들다.
  4. 뎁스가 계속해서 깊어진다. 따라서 가독성이 좋지 않다. 보일러 플레이트 들이 너무 많다.
  5. 뎁스가 계속해서 깊어진다. 따라서 가독성이 좋지 않다. 보일러 플레이트 들이 너무 많다.
  6. 뎁스가 계속해서 깊어진다. 따라서 가독성이 좋지 않다. 보일러 플레이트 들이 너무 많다.
  7. 아무일도 일어 나지 않는다.
  8. null아닌 값은 of, nullable 한 값은 ofNullable을 사용
  9. null아닌 값은 of, nullable 한 값은 ofNullable을 사용
  10. 객체지향 회피에서는 abstract class와, nullClass를 계속 만들어줘야 한다.
  11. 객체지향 회피에서는 abstract class와, nullClass를 계속 만들어줘야 한다.
  12. 객체지향 회피에서는 abstract class와, nullClass를 계속 만들어줘야 한다.
  13. 객체지향 회피에서는 abstract class와, nullClass를 계속 만들어줘야 한다.