SlideShare a Scribd company logo
1 of 67
Download to read offline
null 서바이벌 가이드
스프링 사용자 모임: 박성철
자바한정
코드에 책임을 지나요?
전문가로서 자신의 코드에 자부심을 가지시나요?
차례
Part 0: null과 null안전성
Part 1: null을 안전한 코딩법
Part 2: null에 안전하다고 점검해주는 도구
Part 0: null과 null안전성
“나는 그것을 10억 달러짜리 실수라고 부릅니다. null 참조를 창안한
것이 1965년이었습니다.”
- 토니 호어
null 참조의 기원: 레코드 핸들링
● 레코드 핸들링(Record Handling)
● 토니 호어 경, 64년 창안, 65년 발표
● 레코드와 객체지향의 효시가 된 논문
● 복잡한 구조의 자료를 표현하고 다루는
법을 설명
● 시뮬라 67, 스몰톡 같은 OOP 언어와 알골
계열 언어의 복합 데이터 타입(레코드,
구조체 등)에 영향을 끼침
null 참조의 기원: 레코드 핸들링의 널 참조
● 참조(Reference) - 레코드 간의 관계를 표현
● null - 관계가 없음을 나타내는 특수 값
● null인 값을 사용하려고 하면 오류
● 두 참조값이 null일 때 두 참조는 동일하다고 판단
null 참조의 기원: 레코드 핸들링 예
record class person
begin
integer date of birth;
Boolean male;
reference father, mother,
youngest offspring, elder
sibling(person)
end;
begin reference John (person);
John:=person;
date of birth(John):=today;
male(John):=t rue;
father(John):=Jack;
mother(John):=Jill;
youngest offspring(John):=null;
elder sibling(john):=youngest
offspring(Jack);
youngest offspring(Jack):=John
end
“널 값이 모든 타입의 멤버가 되도록 했고, 따라서 참조 변수를
사용할 때마다 널을 확인해야 한다. 이것이 아마도 10억 달러짜리
실수일 것이다.”
- 토니 호어
소프트웨어 결함 통계
Sapienz: Multi-objective Automated Testingfor Android Applications 에서
자바에서 null이 문제인 이유
● 모호한 null의 의미:
초기화되지 않음, 정의되지 않음, 값이 없음, 오류, null 값?
● 모든 참조의 기본 상태 또는 값
● 모든 참조는 null일 가능성이 있음 - 불안감, 제거 불가능한 불확실성, 잠재적 오류 가능성
null 안전성 언어 지원
● 2000년 대 중반부터 null 안전성이 중요한 화두가 됨
Eradicating the risk of void calls: x.fwith, at run time, the targetxnot denoting any
object,leading to an exception and usually a crash. - Bertrand Meyer1
● null 안전 연산자: null을 안전하고 쉽게 다루도록 도와주는 연산자
○ Null 병합 연산자(Null coalescing operator)
○ Null 조건 연산자(Safe navigation operator)
● null 안전한 타입 시스템: null 안전하다는 사실을 보장해주는 타입 시스템
1) Attached typesand their application to three open problemsof object-oriented programming, 2005, Bertrand Meyer
Null 병합 연산자(Null coalescing operator)
● a ?: b : 그루비, Obj-C, 코틀린, PHP
● a ?? b : 자바스크립트, C#, 스위프트, 타입스크립트, PHP
● a or b : 파이썬
● a // b : 펄
● 첫 인자가 null 이면 null을 반환하고 아니면 두번째 인자를 반환하는 이항 연산자
● 3항 연산자의 단축형 : a ?: b 는 a != null ? a : b 와 동일
● 통칭 “엘비스 연산자”
Null 조건 연산자(Safe navigation operator)
● 그루비(Groovy) : def name = person?.name
● C#: var name = articles?[0]?.Author?.Name;
● 코틀린: val name = article?.author?.name
● 스위프트: let authorName = articles?[0].author.fullname
● 첫 인자가 null 이면 null을 반환하고 아니면 두번째 인자의 작업을 실행하는 이항 연산자
● 명시적인 null 확인과 값 할당을 반복하지 않고 메서드 호출을 연이어 할 수 있도록 해줌
let zipcode = user?.address?.zipcode;
Type System 강화
Null 가능 타입
● null 가능을 선택적 타입 속성으로 취급
● 기본으로 모든 타입은 null 불가능
● null은 여전히 모든 타입에 대입 가능한 특수한 값
● 코틀린, C#(8.0 이상)
var a: String = "abc"
a = null // compilation error
var b: String? = "abc"
b = null // ok
print(b)
Type System 강화
Null 타입과 타입 결합(Union)
● null은 Null 타입의 값
● 특정 타입과 Null 타입을 결합해서 Null 가능하도록 선언 가능 :
val x:SomeType|Null = null
● 실론(Ceylon), 스칼라 3(예정)
Optional 사용
● 기본으로 모든 타입이 Null 불가능
● 값이 없을 수 있음은 Optional이나 Maybe 같은 펑터를 사용해서 표현
● 주로 함수형 프로그래밍 언어의 방식
● 스위프트(Swift)는 암시적으로 Optional 사용
val x: String = null // error: found `Null`, but required `String`
val x: String|Null = null // ok
Part 1: null 안전한 코딩 법
null 안전한 좋은 코딩 법
1. 기본으로 null을 쓰지 말자
2. null 문맥을 제한된 범위 안에 가두자
3. API에 null을 최대한 쓰지 말자
4. Null 객체를 활용하자
5. null을 명시적으로 표현하자
6. 계약에 의한 설계(design by
contract)를 적용하자
7. 구조체에는 펑터(Functor)를 활용하자
8. 객체의 기본값을 유용하게 만들자
1. 기본으로 null을 쓰지 말자
● “무조건적인 null 사용은 만악의 근원이다”1
● null 안전 규칙1
1, null 을 쓰지 말라
2, (전문가들만 따를 것) 아직 null을 쓰지 말라
● null 가능 != null 사용
● 객체 필드, 매개변수, 반환 값은 가능한 null 불가능으로 취급
● 공통 코딩 관례나 스타일 가이드에 포함
1) 최적화에 대한 격언을 차용한 농담입니다
정적분석/컴파일러 확장 기본 null 불가 표시법
● Intellij : @ParametersAreNonnullByDefault
● SpotBugs: @ParametersAreNonnullByDefault
● Eclipse:@NonNullByDefault
● Spring: @NonNullApi, @NonNullFields
● nullaway, checkerframework: 기본
2. null 문맥을 제한된 범위 안에 가두자
● CS 대원칙, “큰 문제는 제어 가능한 작은 문제로 나누어 정복하고 다시 통합한다”
● 상태, null 을 포함한 부작용은 지역적으로 제한할 경우 큰 문제가 안됨
● 클래스와 메서드를 작게 만들자
● 설계가 잘 된 코드에서는 null의 위험도 관리 가능
OOP to me means only messaging, local retention and protection and hiding
of state-process, and extreme late-binding of all things. - Alan Kay
1 2
3
캡슐화 (객체 = 작은 컴퓨터)
local retention and protection and hiding of state-process
낮은 응집, 높은 결합
높은 응집, 낮은 결합
좋은 캡슐화
● 높은 응집성: “한 가지 책임만 갖는다(SRP)”
○ 모든 필드가 객체와 생애주기가 같다1
○ 모든 메서드가 모든 필드를 대상으로 작업한다1, 2
● 낮은 결합
○ 디미터 법칙, “묻지 말고 시켜라(Tell, Don’t Ask)”
○ 인터페이스에 의존
1) 극단적이지만 유용한 지침이 되는 기준
2) 코드 컴플리트, “7.2 루틴 수준의 설계”
디미터 법칙(Law of Demeter)
● 객체의 과도한 결합을 낮출 수 있는 형식적 규칙
● OOPSLA 88에서 발표1
● “묻지 말고 시켜라”로 자연스럽게 연결
● 내부 구현을 외부로 유출되지 않게 막아줌
1) Lieberherr, K.; Holland, I.; Riel, A. (1988). "Object-Oriented Programming: An Objective Sense of Style"
모든 클래스 C와 C에 구현된 모든 메서드 M에 대해서, M이 메시지를 전송할 수 있는 모든 객체는 다음에 서술된 클래스의
인스턴스여야 한다. 이 때 M에 의해 생성된 객체나 M이 호출하는 메서드에 의해 생성된 객체, 전역 변수로 선언된 객체는
모두 M의 인자로 간주한다.
● M의 인자로 전달된 클래스(C 자체를 포함)
● C의 인스턴스 변수의 클래스 오브젝트 P184, 위키북스, 조영호,
"도트(‘.’)를오직 하나만사용하라"
디미터 법칙 예: DDD 애그리것(Aggregate)
<entity>
Aggregate Root
<entity>
<entity>
<value object>
<value object>
<app service>
<entity>
Aggregate Root
<entity> <entity>
<value object>
<value object>
aggregateFoo?.getEntityBar()?.getValueObjectBaz()?.getValue()
3. API에 null을 최대한 쓰지 말아라
● null로 지나치게 유연한 메서드를 만들지 말고 명시적인 메서드를 만들어라
● null을 반환하지 말라
○ 빈 컬랙션1
이나 “Null 객체2
”를 활용하라
○ 반환 값이 없을 수도 있음 명시적으로 Optional2,3
로 표현하라
○ 오류 상황에는 null을 반환하지 말고 예외를 던져라
● 선택적 매개변수는 null 대신 정적 다형성(메서드 추가 정의; overload)를 사용해서
표현하라 1) 이펙티브 자바 #54
2) 뒤에 설명
3) 이펙티브 자바 #55
Null 객체 (특수 사례 패턴; Special Case Pattern)
● 인터페이스는 동일하지만 아무 일도 하지 않는, 일종의 더미 객체
● 이 객체와 협력하는 객체는 더미라는 사실을 몰라야 함
● Bobby Woolf, “The Null Object Pattern”, 19961
● 타입 안전하면서 의미를 표현할 수 있는 동일 타입의 특수 상황용
객체를 null 대신 반환 (Special Case Pattern)
● 리스코프 치환 원칙 주의
● 디미터 법칙과 “묻지 말고 시켜라”를 잘 지켜야 유용
4. null 객체를 활용하자
널 오브젝트 패턴 (Null Object Pattern), 이종립님 번역,
https://johngrib.github.io/wiki/null-object-pattern/
● 한 객체에 협력 객체가 필요하다
● 일부 협력 객체 인스턴스가 아무 일도 하지 말아야 한다
● 사용하는 측이 실제 행동하는 협력 객체와 아무 일도 않는 Null 객체의 차이를
몰라도 문제가 없어야 한다
● 일관된 같은 방식으로 아무 일도 안하기를 바라는 외부 객체를 위해 아무일
안하는 행동을 재사용 할 수 있어야 한다
● 아무 일도 하지 않아야 할 행위는 모두 협력 클래스에 캡슐화 되어야 한다
null 객체를 사용할 상황
5. Null을 명시적으로 표현하자
● null은 참조 값이 없음 나타내는 암시적 표현 방법
● java.util.Optional 은 값이 없음을 명시적으로 표현하는 컨테이너 객체
● 받는 측에서 값이 없음을 인지하고 이에 대한 처리를 놓치지 않을 수 있음
● 값 기반 객체(Value Based Object)
자바 직렬화와 호환 문제로 당분간 직렬화 불가능, 새 제안 준비 중2
● 메서드 반환값으로만 사용하기를 권고
● 불필요한 기본 타입의 박싱/언박싱을 피하자1
OptionalDouble, OptionalInt, OptionalLong
1. 이펙티브 자바 #55
2. Towards Better Serialization
https://cr.openjdk.java.net/~briangoetz/amber/serialization.html
Optional<T>
값 값
?
“Optional - The Mother of All Bikesheds”, Stuart Marks1
1. 절대로 Optional 변수와 반환값에 null을 사용하지 말라
2. Optional에 값이 들어 있다는 걸 확신하지 않는한 Optional.get()을 쓰지 말라
3. Optional.isPresent(), Optional.get() 외 API를 가능한 사용하라
4. Optional에서 메서드를 연속해서 호출하고 값을 얻기 위해 Optional을 생성하는 건
권장할만하지 않다
5. Optional로 값을 처리하는 중에 그 안에 중간값을 처리하기 위해 또 다른 Optional이
사용되면 너무 복잡해진다
6. Optional을 필드, 메서드 매개변수, 집합 자료형에 쓰지 말라
7. 집합 자료형(List, Set, Map)을 감싸는 데 Optional을 쓰지 말고 빈 집합을
사용해라.
1) https://www.youtube.com/watch?v=Ej0sss6cq14
6. 계약에 의한 설계(Design by Contract)를 적용하자
● API 규약을 소비자와 제공자 사이에 지켜야 할 엄격한
계약으로 여기는 설계 방법
● 형식적 규약 외에 사전 조건과 사후 조건과 유지 조건(불변식)
을 포함
● 베르트랑 마이어(Bertrand Meyer) - 에펠( Eiffel)
프로그래밍 언어 제작
● 오브젝트 부록 A “계약에 의한 설계”, 조영호, 위키북스
품질을 추구하면 생산성은 따라온다 - K. Fujino
계약에 의한 설계(Design by Contract)
class DICTIONARY [ELEMENT]
feature
put (x: ELEMENT; key: STRING) is
-- Insert x so that it will be retrievable through key.
require
count <= capacity
not key.empty
do
......
ensure
has (x)
item (key) = x
count = old count + 1
end
invariant
0 <= count
count <= capacity
end
사전조건
사후조건
불변식
자바의 계약에 의한 설계
● 자바용 DbC 전문 라이브러리는 거의 없음
● Interface + Java Doc
● 사전 조건 ~= 보호절(guard clause)
○ 단정문
○ Objects의 메서드
○ IllegalArgumentException,
IllegalStateException,
NullPointerException
● 스프링 Assert 클래스
● 구아바 Preconditions 클래스
● valid4j + hamcrest
http://www.valid4j.org/
● requirements.java
https://bitbucket.org/cowwoc/requirements.java
● AssertJ Preconditions 클래스
자바 단정문(assertion)
● 부울식인 식1의 거짓이면 AssertionError 발생
● 식2는 AssertionError에 포함될 상세 정보를 만드는 생성식
● 공개 메서드에는 사용하지 말아야 함
● -enableassertions 또는 -ea 옵션으로 활성화
assert 식1 ;
assert 식1 : 식2;
private void setRefreshInterval(int interval)
{
assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval;
……
}
java.util.Objects
자바 8
● isNull(Object obj)
● nonNull(Object obj)
● requireNonNull(T obj)
● requireNonNull(T obj, String message)
● requireNonNull(T obj, Supplier<String> messageSupplier)
자바 9
● requireNonNullElse(T obj, T defaultObj)
● requireNonNullElseGet(T obj, Supplier<? extends T> supplier)
7. 구조체에는 펑터(Functor)를 활용하자
● 자바 != 순수 OOP 언어, 자바 클래스의 인스턴스가 모두 객체일 필요는 없다
● 자바 클래스를 데이터 구조체로 적절히 사용가능 (예: DTO)
● 구조체는 객체가 아니므로 디미터 법칙 적용 대상이 아님
val zipcode = myProfile?.address?.zipcode
“객체는 추상화로 데이터를 뒤에 숨기고 대신 이 데이터를 다루는 행위를 노출한다. 데이터
구조체는 데이터를 노출하고 의미 있는 함수를 갖지 않는다.” 로버트 C 마틴
null 조건 연산자의 자바
var myProfile = getMyProfile();
var address = myProfile != null ? myProfile.address : null;
var zipcode = address != null ? address.zipcode : null;
val zipcode = myProfile?.address?.zipcode
var zipcode = nullSafeMap(nullSafeMap(myProfile, x -> x.address), x -> x.zipcode);
…
public static <T,R> R nullSafeMap(T t, Function<T, R> f)
{
return t != null ? f.apply(t): null;
}
Person -> Address
Address -> String
A->B 매핑 추상화
interface Mapper<T> {
<U> Mapper<U> map(Function<T, U> f);
}
class NullSafeMapper<T> implements Mapper<T> {
private final T val;
private NullSafeMapper(T t) { this.val = t; }
@Override public <U> NullSafeMapper<U> map(Function<T, U> f) {
return this.val != null ? of(f.apply(this.val)): null;
}
public T get(T deaultValue) {
return this.val != null ? this.val : deaultValue;
}
public static <T> NullSafeMapper<T> of(T t) {
return new NullSafeMapper<>(t);
}
}T U V
map map
W
map
var zipcode = NullSafeMapper.of(myProfile)
.map(x -> x.address)
.map(x -> x.zipcode)
.get("no zipcode");
get
W
Optional = 펑터(Functor)
T U V
map map
W
map
var zipcode = Optional.of(myProfile).map(x -> x.address).map(x -> x.zipcode).orElse("no zipcode");
get
W
val zipcode = myProfile?.address?.zipcode ?: "no zipcode"
=
8. 객체의 기본값을 유용하게 만들자
● 객체를 생성했을 때 갖는 기본 값이 쓰기에 문제 없어야 한다
Go 언어의 “Make the zero value useful”1
● 실행 전에 모든 필드가 초기화되어야 한다 (지연 초기화 제외)
● 실행 시점에 null인 필드는 초기화되지 않았다는 의미가 아닌, 값이 없다는 의미여야 한다.
● 객체 필드의 생명주기는 모두 객체의 생명주기와 같아야 한다.
● 지연 초기화(lazy initialization) 필드의 경우 팩토리 메서드로 null 처리를 캡슐화 하라
1.https://go-proverbs.github.io/
null 안전한 좋은 코딩 법
● 기본으로 null을 쓰지 말자
● null 문맥을 제한된 범위 안에 가두자
● API에 null을 최대한 쓰지 말자
● Null 객체를 활용하자
● null을 명시적으로 표현하자
● 계약에 의한 설계(design by
contract)를 적용하자
● 구조체에는 펑터(Functor)를 활용하자
● 객체의 기본값을 유용하게 만들자
● 테스트 코드
Part 2: null에 안전하다고 점검해주는 도구
null 안전성을 도와주는 자바 도구
● 정적 분석 또는 어노테이션 프로세싱
○ JSR 305
■ 중단된 미완성 표준 (JSR 리뷰 2006/8/29~2006/9/11)
■ 각종 정적 분석 도구에서 비표준 어노테이션 제공
○ 전문 정적 분석 도구(파인드 벅스 & 스팟 벅스)
○ IDE 지원 (인텔리제이, 이클립스, 스프링, 안드로이드 스튜디오)
○ 어노테이션 프로세서(Lombok, Nullaway)
● 타입 시스템 확장
○ JSR 308: Annotations on Java Types
○ CheckerFramework, 이클립스
파인드 벅스/스팟 벅스 제공 어노테이션
● JSR-305: 파인드 벅스 창시자 빌 퓨(Bill Pugh)가 리드
● 비표준 JSR-305 제작: https://github.com/amaembo/jsr-305
● 스팟 벅스
○ JSR-305가 버려졌다고 판단, 관리하지 않기로 결정
○ JSR-305를 복제한 독자 어노테이션 제공
■ @ParametersAreNonnullByDefault
■ @Nullable, @NotNull, @CheckForNull, @PossiblyNull
■ @UnknownNullness
인텔리제이 제공 어노테이션
● JSR 305의 @ParametersAreNonnullByDefault 지원
○ 메서드, 클래스나 패키지에 지정 가능
○ 매개변수를 기본으로 Null이 아닌 것으로 설정
● JSR 305를 참고한 독자 어노테이션 제공(org.jetbrains.annotations)
○ @Nullable, @NotNull : 필드, 지역변수, 매개변수, 반환값에 지정 가능
○ @Contract : 메서드에 지정, 약한 DbC 지원
@Contract("_, null -> null")
@Contract("_, null -> null; _, !null -> !null")
@Contract("true -> fail")
@Contract("_ -> this")
@Contract("null -> fail; _ -> param1")
@Contract("!null, _ -> param1; null, !null -> param2; null, null -> fail")
이클립스 제공 제공 어노테이션
● Null 어노테이션(JSR-305 유사)
○ @NonNull, @Nullable:
매개변수, 반환값, 지역변수, 필드, 타입에 지정 가능
○ @NonNullByDefault :
■ 메서드, 타입, 패키지에 지정 가능
■ 지정된 범위의 메서드 매개변수, 반환값, 필드가 기본 null이 아님
● Null 타입 어노테이션(JSR-308)
○ 타입 어노테이션 버전 @NonNull, @Nullable,@NonNullByDefault
● 외부 어노테이션 추가 가능
롬복/Nullaway (어노테이션 프로세서)
롬복
● @NonNull 제공
● null 을 기본으로 생각, 기본 설정 어노테이션이 없음
● 지나치게 번잡할 수 있음
Nullaway
● 우버 오픈소스
● 기본이 null 아님, JSR-305 @Nullable 사용
● 내부적에서 CheckerFramework 활용
스프링 제공 어노테이션
● 기존 JSR-305 지원 외, 스프링 5에서 추가 널 안전성 지원
● @NonNull, @Nullable: 매개변수, 반환값, 필드에 지정 가능
● @NonNullApi, @NonNullFields :
○ 패키지에 지정 가능
○ 지정된 범위의 메서드 매개변수, 반환값, 필드가 기본 null이 아님
JSR-308 타입 어노테이션
● 선언부가 아닌 타입(타입 인자 포함) 사용 위치에 어노테이션 추가 가능
● 어노테이션 프로세싱을 통한 빈약한 자바 타입 시스템을 확장
● 초안 제출 2006/10/17, 최종안 승인 2014/2/18, 자바 8에 추가
● 워싱턴대 마이클 에른스트(Michael Ernst) 교수 주도
● CheckerFramework와 동시에 진행 (버전 0.1.1 2007/6/7 첫 출시)
소스 코드 기본 타입 확인
플러그인
타입 확인
바이트 코드
자바 컴파일러
Checker Framework
● null 안전성 확인
@Nullable, @NonNull, @PolyNull
● null 안전성 외에도 Map 키, 잠금, 순차 자료형(배열, List 등) 색인값, 정규식,
문자열 형식, 단위 등 다수의 고급 타입 확인 기능 제공
● 자작 타입 확인 기능 추가 가능
● 특정 환경이나 IDE 독립적, 인텔리제이 플러그인 제공
● 자바 11 지원
@NonNull과 @Nullable
public class Address
{
public final @NonNull String address1;
public final @Nullable String address2;
public final @NonNull String zipcode;
public final @NonNull String city;
public final @NonNull String country;
private Address(@NonNull String address1, @Nullable String address2, @NonNull String zipcode,
@NonNull String city, @NonNull String country)
{
……
}
public static Address of(@NonNull String address1, @NonNull String zipcode,
@NonNull String city, @NonNull String country)
{
return new Address(address1, null, zipcode, city, country);
}
● 과도한 어노테이션 사용 예방
● 기본 @NonNull
필드, 매개변수, 반환값 등
● 예외적 @Nullable
지역 변수, 타입 캐스트 등
기본 null 정책
public class Address
{
public final String address1;
public final @Nullable String address2;
public final String zipcode;
public final String city;
public final String country;
private Address(String address1, @Nullable String address2,
String zipcode, String city, String country)
{
…...
}
public static Address of(String address1, String zipcode, ……
{
return new Address(address1, null, zipcode, city, country);
}
● @DefaultQualifier
● 패키지(package-info.java)나 클래스 전체의 기본 정책 설정
패키지, 클래스 수준 기본 정책 설정
@DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.LOCAL_VARIABLE)
package dev.fupfin.null_safety.strict;
@DefaultQualifier(value = Nullable.class, locations = TypeUseLocation.FIELD)
class MyClass {
Object nullableField = null;
@NonNull Object nonNullField = new Object();
}
● 어노테이션 달린 자바 8, 9+ 표준 API
● 자바 11 지원 (어노테이션과 실행 모두)
● 임의의 API에 어노테이션 설정 가능
어노테이션 달린 자바 표준 API 제공
private static void password()
{
Console console = System.console();
char[] password = console.readPassword();
……
}
Error:(30, 27) java:
[dereference.of.nullable]
dereference of possibly-null
reference console
● 단순한 정적 타입 확인이 아닌 코드 흐름과 실행 결과를 반영
● 코드로 null 확인을 한 경우 @nonNull로 취급
● 메서드 내부로 제한
● Nullaway도 이 기능(data flow)를 사용
자동 타입 개선(Automatic type refinement)
private static void password()
{
Console console = System.console();
char[] password = nonNull(console) ? console.readPassword() : new char[0];
……
}
OK
● 상황에 따라서 @Nullable과 @NonNull의 두가지 버전이 될 수 있는 메서드
● @PolyNull로 표시된 것 중 하나가 @Nullable이면 전부 @Nullable로 교체,
@NonNull이면 모두 @NonNull로 교체
타입 다형성 지원 @PolyNull
@PolyNull T max(@PolyNull T x, @PolyNull T y);
@NonNull T max(@NonNull T x, @NonNull T y); //x, y, 반환값 모두 non-null이거나
@Nullable T max(@Nullable T x, @Nullable T y); // 모두 null 가능함
● List, Set, Map, 배열 등 집합 자료형의 요소도 null 확인
● 기본 @NonNull
● 타입 매개변수에 @Nullable 지정
집합 타입 요소의 null 안정성 지원
private static void printList()
{
List<String> names = Arrays.asList("kim", "choi", null, "park", "hwang");
for(String name: names)
out.println(name);
}
Error:(42, 43) java: [assignment.type.incompatible]
incompatible types in assignment.
found : @Initialized @NonNull List<@Initialized
@Nullable String>......
● @RequiresNonNull : null 가능한 특정 필드를 특정 메서드가 실행될 동안만 null
불가능 취급
● @EnsuresNonNull, @EnsuresNonNullIf :
메스드의 실행 후에 특정 식의 결과가 null이 아닌지 확인
메서드 행동 지원
객체 초기화 상태 확인 지원
Declarations Expression Expression’s nullness type, or checker error
class C {
@NonNull Object f;
@Nullable Object g;
...
}
@NonNull @Initialized C a; a @NonNull
a.f @NonNull
a.g @Nullable
@NonNull @UnderInitialization C b; b @NonNull
b.f @MonotonicNonNull
b.g @Nullable
@Nullable @Initialized C c; c @Nullable
c.f error: deref of nullable
c.g error: deref of nullable
@Nullable @UnderInitialization C d; d @Nullable
d.f error: deref of nullable
d.g error: deref of nullable
외부 어노테이션 호환
android.annotation.NonNull
android.support.annotation.NonNull
androidx.annotation.NonNull
androidx.annotation.RecentlyNonNull
com.sun.istack.internal.NotNull
edu.umd.cs.findbugs.annotations.NonNull
io.reactivex.annotations.NonNull
javax.annotation.Nonnull
javax.validation.constraints.NotNull
lombok.NonNull
org.eclipse.jdt.annotation.NonNull
org.eclipse.jgit.annotations.NonNull
org.jetbrains.annotations.NotNull
org.jmlspecs.annotation.NonNull
org.netbeans.api.annotations.common.NonNull
org.springframework.lang.NonNull
android.annotation.Nullable
android.support.annotation.Nullable
androidx.annotation.Nullable
androidx.annotation.RecentlyNullable
com.sun.istack.internal.Nullable
edu.umd.cs.findbugs.annotations.Nullable
edu.umd.cs.findbugs.annotations.CheckForNull
edu.umd.cs.findbugs.annotations.PossiblyNull
edu.umd.cs.findbugs.annotations.UnknownNullness
io.reactivex.annotations.Nullable
javax.annotation.Nullable
javax.annotation.CheckForNull
org.eclipse.jdt.annotation.Nullable
org.eclipse.jgit.annotations.Nullable
org.jetbrains.annotations.Nullable
org.jmlspecs.annotation.Nullable
org.netbeans.api.annotations.common.NullAllowed
org.netbeans.api.annotations.common.CheckForNull
org.netbeans.api.annotations.common.NullUnknown
org.springframework.lang.Nullable
타입 시스템 확장
정리
null은 왜 문제인가?
● 모든 참조 타입에 지정 가능한 값(상태?), 언어가 지원할 문제
좋은 코드는 비교적 null에 안전하다
● null을 예외적으로, 특히 API에 null을 최대한 쓰지 말아라
● 좋은 객체를 만들자: (상태와 같이) null의 범위를 지역(클래스, 메서드)에 제한하라.
● 사전 조건과 사후 조건을 확인하라: 계약에 의한 설계(design by contract)
● 초기화를 명확히 하라
null에 안전하다고 보장해주는 도구를 쓰자
● JSR 305 계열의 정적 분석과 JSR 308 계열의 타입 확장
● 가장 성숙한 자바 타입 확인 확장 기술: Checker Framework
코드에 책임을 지나요?
전문가로서 자신의 코드에 자부심을 느끼나요?
감사합니다.

More Related Content

What's hot

What's hot (20)

글쓰는 개발자 모임, 글또
글쓰는 개발자 모임, 글또글쓰는 개발자 모임, 글또
글쓰는 개발자 모임, 글또
 
Redmineのバージョンアップに追従していくための一工夫
Redmineのバージョンアップに追従していくための一工夫Redmineのバージョンアップに追従していくための一工夫
Redmineのバージョンアップに追従していくための一工夫
 
카카오뱅크 모바일앱 개발 이야기
카카오뱅크 모바일앱 개발 이야기카카오뱅크 모바일앱 개발 이야기
카카오뱅크 모바일앱 개발 이야기
 
DeNA_Techcon2017_DeNAでのチート・脆弱性診断への取り組み
DeNA_Techcon2017_DeNAでのチート・脆弱性診断への取り組みDeNA_Techcon2017_DeNAでのチート・脆弱性診断への取り組み
DeNA_Techcon2017_DeNAでのチート・脆弱性診断への取り組み
 
Visual Studio를 이용한 어셈블리어 학습 part 2
Visual Studio를 이용한 어셈블리어 학습 part 2Visual Studio를 이용한 어셈블리어 학습 part 2
Visual Studio를 이용한 어셈블리어 학습 part 2
 
How To Become Better Engineer
How To Become Better EngineerHow To Become Better Engineer
How To Become Better Engineer
 
Jupyter notebook 이해하기
Jupyter notebook 이해하기 Jupyter notebook 이해하기
Jupyter notebook 이해하기
 
[AKIBA.AWS] VPN接続とルーティングの基礎
[AKIBA.AWS] VPN接続とルーティングの基礎[AKIBA.AWS] VPN接続とルーティングの基礎
[AKIBA.AWS] VPN接続とルーティングの基礎
 
AWS Amplify - Auth/API Category & Vue 構築ハンズオン
AWS Amplify - Auth/API Category & Vue 構築ハンズオンAWS Amplify - Auth/API Category & Vue 構築ハンズオン
AWS Amplify - Auth/API Category & Vue 構築ハンズオン
 
ふつうのRailsアプリケーション開発
ふつうのRailsアプリケーション開発ふつうのRailsアプリケーション開発
ふつうのRailsアプリケーション開発
 
로그 기깔나게 잘 디자인하는 법
로그 기깔나게 잘 디자인하는 법로그 기깔나게 잘 디자인하는 법
로그 기깔나게 잘 디자인하는 법
 
Spring fest2020 spring-security
Spring fest2020 spring-securitySpring fest2020 spring-security
Spring fest2020 spring-security
 
코딩 테스트 및 알고리즘 문제해결 공부 방법 (고려대학교 KUCC, 2022년 4월)
코딩 테스트 및 알고리즘 문제해결 공부 방법 (고려대학교 KUCC, 2022년 4월)코딩 테스트 및 알고리즘 문제해결 공부 방법 (고려대학교 KUCC, 2022년 4월)
코딩 테스트 및 알고리즘 문제해결 공부 방법 (고려대학교 KUCC, 2022년 4월)
 
[NDC17] Kubernetes로 개발서버 간단히 찍어내기
[NDC17] Kubernetes로 개발서버 간단히 찍어내기[NDC17] Kubernetes로 개발서버 간단히 찍어내기
[NDC17] Kubernetes로 개발서버 간단히 찍어내기
 
AWS Black Belt Online Seminar 2017 Auto Scaling
AWS Black Belt Online Seminar 2017 Auto ScalingAWS Black Belt Online Seminar 2017 Auto Scaling
AWS Black Belt Online Seminar 2017 Auto Scaling
 
AWSで始めるサーバレスな RESTful API システム
AWSで始めるサーバレスな RESTful API システムAWSで始めるサーバレスな RESTful API システム
AWSで始めるサーバレスな RESTful API システム
 
AWS 기반 블록체인 (1부) - 블록체인 환경 구성하기 (박혜영 & 유다니엘, AWS 솔루션즈 아키텍트) :: AWS DevDay2018
AWS 기반 블록체인 (1부) - 블록체인 환경 구성하기 (박혜영 & 유다니엘, AWS 솔루션즈 아키텍트) :: AWS DevDay2018AWS 기반 블록체인 (1부) - 블록체인 환경 구성하기 (박혜영 & 유다니엘, AWS 솔루션즈 아키텍트) :: AWS DevDay2018
AWS 기반 블록체인 (1부) - 블록체인 환경 구성하기 (박혜영 & 유다니엘, AWS 솔루션즈 아키텍트) :: AWS DevDay2018
 
Sonar qubeでちょっと楽しい静的解析
Sonar qubeでちょっと楽しい静的解析Sonar qubeでちょっと楽しい静的解析
Sonar qubeでちょっと楽しい静的解析
 
目grep入門 +解説
目grep入門 +解説目grep入門 +解説
目grep入門 +解説
 
파이썬 생존 안내서 (자막)
파이썬 생존 안내서 (자막)파이썬 생존 안내서 (자막)
파이썬 생존 안내서 (자막)
 

Similar to Java null survival guide

3팀_객체지향 프로그래밍.pptx
3팀_객체지향 프로그래밍.pptx3팀_객체지향 프로그래밍.pptx
3팀_객체지향 프로그래밍.pptx
ssuser642b19
 
Effective c++ chapter5 6_ 131039 신동찬
Effective c++ chapter5 6_ 131039 신동찬Effective c++ chapter5 6_ 131039 신동찬
Effective c++ chapter5 6_ 131039 신동찬
Dong Chan Shin
 
외계어 스터디 3/5 function and object
외계어 스터디 3/5   function and object외계어 스터디 3/5   function and object
외계어 스터디 3/5 function and object
민태 김
 
클로져 소개 강의 (한국정보통신산업노동조합)
클로져 소개 강의 (한국정보통신산업노동조합)클로져 소개 강의 (한국정보통신산업노동조합)
클로져 소개 강의 (한국정보통신산업노동조합)
Sang-Kyu Park
 
More effective c++ 3주차
More effective c++ 3주차More effective c++ 3주차
More effective c++ 3주차
Injae Lee
 

Similar to Java null survival guide (20)

Effective c++ 1,2
Effective c++ 1,2Effective c++ 1,2
Effective c++ 1,2
 
3팀_객체지향 프로그래밍.pptx
3팀_객체지향 프로그래밍.pptx3팀_객체지향 프로그래밍.pptx
3팀_객체지향 프로그래밍.pptx
 
[Dev rookie]designpattern
[Dev rookie]designpattern[Dev rookie]designpattern
[Dev rookie]designpattern
 
effective c++ chapter 3~4 정리
effective c++ chapter 3~4 정리effective c++ chapter 3~4 정리
effective c++ chapter 3~4 정리
 
[1B1]스위프트프로그래밍언어
[1B1]스위프트프로그래밍언어[1B1]스위프트프로그래밍언어
[1B1]스위프트프로그래밍언어
 
Effective c++ chapter5 6_ 131039 신동찬
Effective c++ chapter5 6_ 131039 신동찬Effective c++ chapter5 6_ 131039 신동찬
Effective c++ chapter5 6_ 131039 신동찬
 
이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디
 
Java 이해하기 쉬운 코드
Java 이해하기 쉬운 코드 Java 이해하기 쉬운 코드
Java 이해하기 쉬운 코드
 
Java 이해하기 쉬운 코드 20210405
Java 이해하기 쉬운 코드 20210405Java 이해하기 쉬운 코드 20210405
Java 이해하기 쉬운 코드 20210405
 
Java 이해하기 쉬운 코드 20210405
Java 이해하기 쉬운 코드 20210405Java 이해하기 쉬운 코드 20210405
Java 이해하기 쉬운 코드 20210405
 
How to use the Ruby programing language
How to use the Ruby programing languageHow to use the Ruby programing language
How to use the Ruby programing language
 
C++ api design 품질
C++ api design 품질C++ api design 품질
C++ api design 품질
 
디자인패턴 1~13
디자인패턴 1~13디자인패턴 1~13
디자인패턴 1~13
 
Head first디자인패턴 1~13_희민_호준
Head first디자인패턴 1~13_희민_호준Head first디자인패턴 1~13_희민_호준
Head first디자인패턴 1~13_희민_호준
 
깨끗한 코드 (클린 코드, Clean Code)
깨끗한 코드 (클린 코드, Clean Code)깨끗한 코드 (클린 코드, Clean Code)
깨끗한 코드 (클린 코드, Clean Code)
 
외계어 스터디 3/5 function and object
외계어 스터디 3/5   function and object외계어 스터디 3/5   function and object
외계어 스터디 3/5 function and object
 
Communism OOP
Communism OOPCommunism OOP
Communism OOP
 
클로져 소개 강의 (한국정보통신산업노동조합)
클로져 소개 강의 (한국정보통신산업노동조합)클로져 소개 강의 (한국정보통신산업노동조합)
클로져 소개 강의 (한국정보통신산업노동조합)
 
More effective c++ 3주차
More effective c++ 3주차More effective c++ 3주차
More effective c++ 3주차
 
Swift5 vs objective c
Swift5 vs objective cSwift5 vs objective c
Swift5 vs objective c
 

More from Sungchul Park

More from Sungchul Park (19)

애자일 안한 이야기
애자일 안한 이야기애자일 안한 이야기
애자일 안한 이야기
 
Java.next
Java.nextJava.next
Java.next
 
자바 테스트 자동화
자바 테스트 자동화자바 테스트 자동화
자바 테스트 자동화
 
변경에 강한 애플리케이션, 유기적 애플리케이션
변경에 강한 애플리케이션, 유기적 애플리케이션변경에 강한 애플리케이션, 유기적 애플리케이션
변경에 강한 애플리케이션, 유기적 애플리케이션
 
자바 서버 애플리케이션 아키텍처 안티 패턴
자바 서버 애플리케이션 아키텍처 안티 패턴자바 서버 애플리케이션 아키텍처 안티 패턴
자바 서버 애플리케이션 아키텍처 안티 패턴
 
스프링보다 중요한 스프링 이야기
스프링보다 중요한 스프링 이야기스프링보다 중요한 스프링 이야기
스프링보다 중요한 스프링 이야기
 
Geeks at SK Planet
Geeks at SK PlanetGeeks at SK Planet
Geeks at SK Planet
 
Beyond Java: 자바 8을 중심으로 본 자바의 혁신
Beyond Java: 자바 8을 중심으로 본 자바의 혁신Beyond Java: 자바 8을 중심으로 본 자바의 혁신
Beyond Java: 자바 8을 중심으로 본 자바의 혁신
 
Java the good parts
Java the good partsJava the good parts
Java the good parts
 
스프링 코어 강의 3부 - 웹 애플리케이션 아키텍처
스프링 코어 강의 3부 - 웹 애플리케이션 아키텍처 스프링 코어 강의 3부 - 웹 애플리케이션 아키텍처
스프링 코어 강의 3부 - 웹 애플리케이션 아키텍처
 
스프링 코어 강의 2부 - Java 구성을 활용한 스프링 코어 사용
스프링 코어 강의 2부 - Java 구성을 활용한 스프링 코어 사용스프링 코어 강의 2부 - Java 구성을 활용한 스프링 코어 사용
스프링 코어 강의 2부 - Java 구성을 활용한 스프링 코어 사용
 
스프링 코어 강의 1부 - 봄 맞이 준비 운동
스프링 코어 강의 1부 - 봄 맞이 준비 운동스프링 코어 강의 1부 - 봄 맞이 준비 운동
스프링 코어 강의 1부 - 봄 맞이 준비 운동
 
자바8 나머지 공개
자바8 나머지 공개자바8 나머지 공개
자바8 나머지 공개
 
자바8 람다 나머지 공개
자바8 람다 나머지 공개자바8 람다 나머지 공개
자바8 람다 나머지 공개
 
팀장 잔소리
팀장 잔소리팀장 잔소리
팀장 잔소리
 
java 8 람다식 소개와 의미 고찰
java 8 람다식 소개와 의미 고찰java 8 람다식 소개와 의미 고찰
java 8 람다식 소개와 의미 고찰
 
Open Source가 바꾼 자바
Open Source가 바꾼 자바Open Source가 바꾼 자바
Open Source가 바꾼 자바
 
Work With Engineer
Work With EngineerWork With Engineer
Work With Engineer
 
DDD 산책
DDD 산책DDD 산책
DDD 산책
 

Java null survival guide

  • 1. null 서바이벌 가이드 스프링 사용자 모임: 박성철 자바한정
  • 2. 코드에 책임을 지나요? 전문가로서 자신의 코드에 자부심을 가지시나요?
  • 3. 차례 Part 0: null과 null안전성 Part 1: null을 안전한 코딩법 Part 2: null에 안전하다고 점검해주는 도구
  • 4. Part 0: null과 null안전성
  • 5. “나는 그것을 10억 달러짜리 실수라고 부릅니다. null 참조를 창안한 것이 1965년이었습니다.” - 토니 호어
  • 6. null 참조의 기원: 레코드 핸들링 ● 레코드 핸들링(Record Handling) ● 토니 호어 경, 64년 창안, 65년 발표 ● 레코드와 객체지향의 효시가 된 논문 ● 복잡한 구조의 자료를 표현하고 다루는 법을 설명 ● 시뮬라 67, 스몰톡 같은 OOP 언어와 알골 계열 언어의 복합 데이터 타입(레코드, 구조체 등)에 영향을 끼침
  • 7. null 참조의 기원: 레코드 핸들링의 널 참조 ● 참조(Reference) - 레코드 간의 관계를 표현 ● null - 관계가 없음을 나타내는 특수 값 ● null인 값을 사용하려고 하면 오류 ● 두 참조값이 null일 때 두 참조는 동일하다고 판단
  • 8. null 참조의 기원: 레코드 핸들링 예 record class person begin integer date of birth; Boolean male; reference father, mother, youngest offspring, elder sibling(person) end; begin reference John (person); John:=person; date of birth(John):=today; male(John):=t rue; father(John):=Jack; mother(John):=Jill; youngest offspring(John):=null; elder sibling(john):=youngest offspring(Jack); youngest offspring(Jack):=John end
  • 9. “널 값이 모든 타입의 멤버가 되도록 했고, 따라서 참조 변수를 사용할 때마다 널을 확인해야 한다. 이것이 아마도 10억 달러짜리 실수일 것이다.” - 토니 호어
  • 10. 소프트웨어 결함 통계 Sapienz: Multi-objective Automated Testingfor Android Applications 에서
  • 11. 자바에서 null이 문제인 이유 ● 모호한 null의 의미: 초기화되지 않음, 정의되지 않음, 값이 없음, 오류, null 값? ● 모든 참조의 기본 상태 또는 값 ● 모든 참조는 null일 가능성이 있음 - 불안감, 제거 불가능한 불확실성, 잠재적 오류 가능성
  • 12. null 안전성 언어 지원 ● 2000년 대 중반부터 null 안전성이 중요한 화두가 됨 Eradicating the risk of void calls: x.fwith, at run time, the targetxnot denoting any object,leading to an exception and usually a crash. - Bertrand Meyer1 ● null 안전 연산자: null을 안전하고 쉽게 다루도록 도와주는 연산자 ○ Null 병합 연산자(Null coalescing operator) ○ Null 조건 연산자(Safe navigation operator) ● null 안전한 타입 시스템: null 안전하다는 사실을 보장해주는 타입 시스템 1) Attached typesand their application to three open problemsof object-oriented programming, 2005, Bertrand Meyer
  • 13. Null 병합 연산자(Null coalescing operator) ● a ?: b : 그루비, Obj-C, 코틀린, PHP ● a ?? b : 자바스크립트, C#, 스위프트, 타입스크립트, PHP ● a or b : 파이썬 ● a // b : 펄 ● 첫 인자가 null 이면 null을 반환하고 아니면 두번째 인자를 반환하는 이항 연산자 ● 3항 연산자의 단축형 : a ?: b 는 a != null ? a : b 와 동일 ● 통칭 “엘비스 연산자”
  • 14. Null 조건 연산자(Safe navigation operator) ● 그루비(Groovy) : def name = person?.name ● C#: var name = articles?[0]?.Author?.Name; ● 코틀린: val name = article?.author?.name ● 스위프트: let authorName = articles?[0].author.fullname ● 첫 인자가 null 이면 null을 반환하고 아니면 두번째 인자의 작업을 실행하는 이항 연산자 ● 명시적인 null 확인과 값 할당을 반복하지 않고 메서드 호출을 연이어 할 수 있도록 해줌 let zipcode = user?.address?.zipcode;
  • 15. Type System 강화 Null 가능 타입 ● null 가능을 선택적 타입 속성으로 취급 ● 기본으로 모든 타입은 null 불가능 ● null은 여전히 모든 타입에 대입 가능한 특수한 값 ● 코틀린, C#(8.0 이상) var a: String = "abc" a = null // compilation error var b: String? = "abc" b = null // ok print(b)
  • 16. Type System 강화 Null 타입과 타입 결합(Union) ● null은 Null 타입의 값 ● 특정 타입과 Null 타입을 결합해서 Null 가능하도록 선언 가능 : val x:SomeType|Null = null ● 실론(Ceylon), 스칼라 3(예정) Optional 사용 ● 기본으로 모든 타입이 Null 불가능 ● 값이 없을 수 있음은 Optional이나 Maybe 같은 펑터를 사용해서 표현 ● 주로 함수형 프로그래밍 언어의 방식 ● 스위프트(Swift)는 암시적으로 Optional 사용 val x: String = null // error: found `Null`, but required `String` val x: String|Null = null // ok
  • 17. Part 1: null 안전한 코딩 법
  • 18.
  • 19. null 안전한 좋은 코딩 법 1. 기본으로 null을 쓰지 말자 2. null 문맥을 제한된 범위 안에 가두자 3. API에 null을 최대한 쓰지 말자 4. Null 객체를 활용하자 5. null을 명시적으로 표현하자 6. 계약에 의한 설계(design by contract)를 적용하자 7. 구조체에는 펑터(Functor)를 활용하자 8. 객체의 기본값을 유용하게 만들자
  • 20. 1. 기본으로 null을 쓰지 말자 ● “무조건적인 null 사용은 만악의 근원이다”1 ● null 안전 규칙1 1, null 을 쓰지 말라 2, (전문가들만 따를 것) 아직 null을 쓰지 말라 ● null 가능 != null 사용 ● 객체 필드, 매개변수, 반환 값은 가능한 null 불가능으로 취급 ● 공통 코딩 관례나 스타일 가이드에 포함 1) 최적화에 대한 격언을 차용한 농담입니다
  • 21. 정적분석/컴파일러 확장 기본 null 불가 표시법 ● Intellij : @ParametersAreNonnullByDefault ● SpotBugs: @ParametersAreNonnullByDefault ● Eclipse:@NonNullByDefault ● Spring: @NonNullApi, @NonNullFields ● nullaway, checkerframework: 기본
  • 22. 2. null 문맥을 제한된 범위 안에 가두자 ● CS 대원칙, “큰 문제는 제어 가능한 작은 문제로 나누어 정복하고 다시 통합한다” ● 상태, null 을 포함한 부작용은 지역적으로 제한할 경우 큰 문제가 안됨 ● 클래스와 메서드를 작게 만들자 ● 설계가 잘 된 코드에서는 null의 위험도 관리 가능 OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. - Alan Kay 1 2 3
  • 23. 캡슐화 (객체 = 작은 컴퓨터) local retention and protection and hiding of state-process 낮은 응집, 높은 결합 높은 응집, 낮은 결합
  • 24. 좋은 캡슐화 ● 높은 응집성: “한 가지 책임만 갖는다(SRP)” ○ 모든 필드가 객체와 생애주기가 같다1 ○ 모든 메서드가 모든 필드를 대상으로 작업한다1, 2 ● 낮은 결합 ○ 디미터 법칙, “묻지 말고 시켜라(Tell, Don’t Ask)” ○ 인터페이스에 의존 1) 극단적이지만 유용한 지침이 되는 기준 2) 코드 컴플리트, “7.2 루틴 수준의 설계”
  • 25. 디미터 법칙(Law of Demeter) ● 객체의 과도한 결합을 낮출 수 있는 형식적 규칙 ● OOPSLA 88에서 발표1 ● “묻지 말고 시켜라”로 자연스럽게 연결 ● 내부 구현을 외부로 유출되지 않게 막아줌 1) Lieberherr, K.; Holland, I.; Riel, A. (1988). "Object-Oriented Programming: An Objective Sense of Style" 모든 클래스 C와 C에 구현된 모든 메서드 M에 대해서, M이 메시지를 전송할 수 있는 모든 객체는 다음에 서술된 클래스의 인스턴스여야 한다. 이 때 M에 의해 생성된 객체나 M이 호출하는 메서드에 의해 생성된 객체, 전역 변수로 선언된 객체는 모두 M의 인자로 간주한다. ● M의 인자로 전달된 클래스(C 자체를 포함) ● C의 인스턴스 변수의 클래스 오브젝트 P184, 위키북스, 조영호, "도트(‘.’)를오직 하나만사용하라"
  • 26. 디미터 법칙 예: DDD 애그리것(Aggregate) <entity> Aggregate Root <entity> <entity> <value object> <value object> <app service> <entity> Aggregate Root <entity> <entity> <value object> <value object> aggregateFoo?.getEntityBar()?.getValueObjectBaz()?.getValue()
  • 27. 3. API에 null을 최대한 쓰지 말아라 ● null로 지나치게 유연한 메서드를 만들지 말고 명시적인 메서드를 만들어라 ● null을 반환하지 말라 ○ 빈 컬랙션1 이나 “Null 객체2 ”를 활용하라 ○ 반환 값이 없을 수도 있음 명시적으로 Optional2,3 로 표현하라 ○ 오류 상황에는 null을 반환하지 말고 예외를 던져라 ● 선택적 매개변수는 null 대신 정적 다형성(메서드 추가 정의; overload)를 사용해서 표현하라 1) 이펙티브 자바 #54 2) 뒤에 설명 3) 이펙티브 자바 #55
  • 28. Null 객체 (특수 사례 패턴; Special Case Pattern) ● 인터페이스는 동일하지만 아무 일도 하지 않는, 일종의 더미 객체 ● 이 객체와 협력하는 객체는 더미라는 사실을 몰라야 함 ● Bobby Woolf, “The Null Object Pattern”, 19961 ● 타입 안전하면서 의미를 표현할 수 있는 동일 타입의 특수 상황용 객체를 null 대신 반환 (Special Case Pattern) ● 리스코프 치환 원칙 주의 ● 디미터 법칙과 “묻지 말고 시켜라”를 잘 지켜야 유용 4. null 객체를 활용하자 널 오브젝트 패턴 (Null Object Pattern), 이종립님 번역, https://johngrib.github.io/wiki/null-object-pattern/
  • 29. ● 한 객체에 협력 객체가 필요하다 ● 일부 협력 객체 인스턴스가 아무 일도 하지 말아야 한다 ● 사용하는 측이 실제 행동하는 협력 객체와 아무 일도 않는 Null 객체의 차이를 몰라도 문제가 없어야 한다 ● 일관된 같은 방식으로 아무 일도 안하기를 바라는 외부 객체를 위해 아무일 안하는 행동을 재사용 할 수 있어야 한다 ● 아무 일도 하지 않아야 할 행위는 모두 협력 클래스에 캡슐화 되어야 한다 null 객체를 사용할 상황
  • 30. 5. Null을 명시적으로 표현하자 ● null은 참조 값이 없음 나타내는 암시적 표현 방법 ● java.util.Optional 은 값이 없음을 명시적으로 표현하는 컨테이너 객체 ● 받는 측에서 값이 없음을 인지하고 이에 대한 처리를 놓치지 않을 수 있음 ● 값 기반 객체(Value Based Object) 자바 직렬화와 호환 문제로 당분간 직렬화 불가능, 새 제안 준비 중2 ● 메서드 반환값으로만 사용하기를 권고 ● 불필요한 기본 타입의 박싱/언박싱을 피하자1 OptionalDouble, OptionalInt, OptionalLong 1. 이펙티브 자바 #55 2. Towards Better Serialization https://cr.openjdk.java.net/~briangoetz/amber/serialization.html Optional<T> 값 값 ?
  • 31. “Optional - The Mother of All Bikesheds”, Stuart Marks1 1. 절대로 Optional 변수와 반환값에 null을 사용하지 말라 2. Optional에 값이 들어 있다는 걸 확신하지 않는한 Optional.get()을 쓰지 말라 3. Optional.isPresent(), Optional.get() 외 API를 가능한 사용하라 4. Optional에서 메서드를 연속해서 호출하고 값을 얻기 위해 Optional을 생성하는 건 권장할만하지 않다 5. Optional로 값을 처리하는 중에 그 안에 중간값을 처리하기 위해 또 다른 Optional이 사용되면 너무 복잡해진다 6. Optional을 필드, 메서드 매개변수, 집합 자료형에 쓰지 말라 7. 집합 자료형(List, Set, Map)을 감싸는 데 Optional을 쓰지 말고 빈 집합을 사용해라. 1) https://www.youtube.com/watch?v=Ej0sss6cq14
  • 32. 6. 계약에 의한 설계(Design by Contract)를 적용하자 ● API 규약을 소비자와 제공자 사이에 지켜야 할 엄격한 계약으로 여기는 설계 방법 ● 형식적 규약 외에 사전 조건과 사후 조건과 유지 조건(불변식) 을 포함 ● 베르트랑 마이어(Bertrand Meyer) - 에펠( Eiffel) 프로그래밍 언어 제작 ● 오브젝트 부록 A “계약에 의한 설계”, 조영호, 위키북스 품질을 추구하면 생산성은 따라온다 - K. Fujino
  • 33. 계약에 의한 설계(Design by Contract) class DICTIONARY [ELEMENT] feature put (x: ELEMENT; key: STRING) is -- Insert x so that it will be retrievable through key. require count <= capacity not key.empty do ...... ensure has (x) item (key) = x count = old count + 1 end invariant 0 <= count count <= capacity end 사전조건 사후조건 불변식
  • 34. 자바의 계약에 의한 설계 ● 자바용 DbC 전문 라이브러리는 거의 없음 ● Interface + Java Doc ● 사전 조건 ~= 보호절(guard clause) ○ 단정문 ○ Objects의 메서드 ○ IllegalArgumentException, IllegalStateException, NullPointerException ● 스프링 Assert 클래스 ● 구아바 Preconditions 클래스 ● valid4j + hamcrest http://www.valid4j.org/ ● requirements.java https://bitbucket.org/cowwoc/requirements.java ● AssertJ Preconditions 클래스
  • 35. 자바 단정문(assertion) ● 부울식인 식1의 거짓이면 AssertionError 발생 ● 식2는 AssertionError에 포함될 상세 정보를 만드는 생성식 ● 공개 메서드에는 사용하지 말아야 함 ● -enableassertions 또는 -ea 옵션으로 활성화 assert 식1 ; assert 식1 : 식2; private void setRefreshInterval(int interval) { assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval; …… }
  • 36. java.util.Objects 자바 8 ● isNull(Object obj) ● nonNull(Object obj) ● requireNonNull(T obj) ● requireNonNull(T obj, String message) ● requireNonNull(T obj, Supplier<String> messageSupplier) 자바 9 ● requireNonNullElse(T obj, T defaultObj) ● requireNonNullElseGet(T obj, Supplier<? extends T> supplier)
  • 37. 7. 구조체에는 펑터(Functor)를 활용하자 ● 자바 != 순수 OOP 언어, 자바 클래스의 인스턴스가 모두 객체일 필요는 없다 ● 자바 클래스를 데이터 구조체로 적절히 사용가능 (예: DTO) ● 구조체는 객체가 아니므로 디미터 법칙 적용 대상이 아님 val zipcode = myProfile?.address?.zipcode “객체는 추상화로 데이터를 뒤에 숨기고 대신 이 데이터를 다루는 행위를 노출한다. 데이터 구조체는 데이터를 노출하고 의미 있는 함수를 갖지 않는다.” 로버트 C 마틴
  • 38.
  • 39. null 조건 연산자의 자바 var myProfile = getMyProfile(); var address = myProfile != null ? myProfile.address : null; var zipcode = address != null ? address.zipcode : null; val zipcode = myProfile?.address?.zipcode var zipcode = nullSafeMap(nullSafeMap(myProfile, x -> x.address), x -> x.zipcode); … public static <T,R> R nullSafeMap(T t, Function<T, R> f) { return t != null ? f.apply(t): null; } Person -> Address Address -> String
  • 40. A->B 매핑 추상화 interface Mapper<T> { <U> Mapper<U> map(Function<T, U> f); } class NullSafeMapper<T> implements Mapper<T> { private final T val; private NullSafeMapper(T t) { this.val = t; } @Override public <U> NullSafeMapper<U> map(Function<T, U> f) { return this.val != null ? of(f.apply(this.val)): null; } public T get(T deaultValue) { return this.val != null ? this.val : deaultValue; } public static <T> NullSafeMapper<T> of(T t) { return new NullSafeMapper<>(t); } }T U V map map W map var zipcode = NullSafeMapper.of(myProfile) .map(x -> x.address) .map(x -> x.zipcode) .get("no zipcode"); get W
  • 41. Optional = 펑터(Functor) T U V map map W map var zipcode = Optional.of(myProfile).map(x -> x.address).map(x -> x.zipcode).orElse("no zipcode"); get W val zipcode = myProfile?.address?.zipcode ?: "no zipcode" =
  • 42. 8. 객체의 기본값을 유용하게 만들자 ● 객체를 생성했을 때 갖는 기본 값이 쓰기에 문제 없어야 한다 Go 언어의 “Make the zero value useful”1 ● 실행 전에 모든 필드가 초기화되어야 한다 (지연 초기화 제외) ● 실행 시점에 null인 필드는 초기화되지 않았다는 의미가 아닌, 값이 없다는 의미여야 한다. ● 객체 필드의 생명주기는 모두 객체의 생명주기와 같아야 한다. ● 지연 초기화(lazy initialization) 필드의 경우 팩토리 메서드로 null 처리를 캡슐화 하라 1.https://go-proverbs.github.io/
  • 43. null 안전한 좋은 코딩 법 ● 기본으로 null을 쓰지 말자 ● null 문맥을 제한된 범위 안에 가두자 ● API에 null을 최대한 쓰지 말자 ● Null 객체를 활용하자 ● null을 명시적으로 표현하자 ● 계약에 의한 설계(design by contract)를 적용하자 ● 구조체에는 펑터(Functor)를 활용하자 ● 객체의 기본값을 유용하게 만들자 ● 테스트 코드
  • 44. Part 2: null에 안전하다고 점검해주는 도구
  • 45. null 안전성을 도와주는 자바 도구 ● 정적 분석 또는 어노테이션 프로세싱 ○ JSR 305 ■ 중단된 미완성 표준 (JSR 리뷰 2006/8/29~2006/9/11) ■ 각종 정적 분석 도구에서 비표준 어노테이션 제공 ○ 전문 정적 분석 도구(파인드 벅스 & 스팟 벅스) ○ IDE 지원 (인텔리제이, 이클립스, 스프링, 안드로이드 스튜디오) ○ 어노테이션 프로세서(Lombok, Nullaway) ● 타입 시스템 확장 ○ JSR 308: Annotations on Java Types ○ CheckerFramework, 이클립스
  • 46. 파인드 벅스/스팟 벅스 제공 어노테이션 ● JSR-305: 파인드 벅스 창시자 빌 퓨(Bill Pugh)가 리드 ● 비표준 JSR-305 제작: https://github.com/amaembo/jsr-305 ● 스팟 벅스 ○ JSR-305가 버려졌다고 판단, 관리하지 않기로 결정 ○ JSR-305를 복제한 독자 어노테이션 제공 ■ @ParametersAreNonnullByDefault ■ @Nullable, @NotNull, @CheckForNull, @PossiblyNull ■ @UnknownNullness
  • 47. 인텔리제이 제공 어노테이션 ● JSR 305의 @ParametersAreNonnullByDefault 지원 ○ 메서드, 클래스나 패키지에 지정 가능 ○ 매개변수를 기본으로 Null이 아닌 것으로 설정 ● JSR 305를 참고한 독자 어노테이션 제공(org.jetbrains.annotations) ○ @Nullable, @NotNull : 필드, 지역변수, 매개변수, 반환값에 지정 가능 ○ @Contract : 메서드에 지정, 약한 DbC 지원 @Contract("_, null -> null") @Contract("_, null -> null; _, !null -> !null") @Contract("true -> fail") @Contract("_ -> this") @Contract("null -> fail; _ -> param1") @Contract("!null, _ -> param1; null, !null -> param2; null, null -> fail")
  • 48. 이클립스 제공 제공 어노테이션 ● Null 어노테이션(JSR-305 유사) ○ @NonNull, @Nullable: 매개변수, 반환값, 지역변수, 필드, 타입에 지정 가능 ○ @NonNullByDefault : ■ 메서드, 타입, 패키지에 지정 가능 ■ 지정된 범위의 메서드 매개변수, 반환값, 필드가 기본 null이 아님 ● Null 타입 어노테이션(JSR-308) ○ 타입 어노테이션 버전 @NonNull, @Nullable,@NonNullByDefault ● 외부 어노테이션 추가 가능
  • 49. 롬복/Nullaway (어노테이션 프로세서) 롬복 ● @NonNull 제공 ● null 을 기본으로 생각, 기본 설정 어노테이션이 없음 ● 지나치게 번잡할 수 있음 Nullaway ● 우버 오픈소스 ● 기본이 null 아님, JSR-305 @Nullable 사용 ● 내부적에서 CheckerFramework 활용
  • 50. 스프링 제공 어노테이션 ● 기존 JSR-305 지원 외, 스프링 5에서 추가 널 안전성 지원 ● @NonNull, @Nullable: 매개변수, 반환값, 필드에 지정 가능 ● @NonNullApi, @NonNullFields : ○ 패키지에 지정 가능 ○ 지정된 범위의 메서드 매개변수, 반환값, 필드가 기본 null이 아님
  • 51. JSR-308 타입 어노테이션 ● 선언부가 아닌 타입(타입 인자 포함) 사용 위치에 어노테이션 추가 가능 ● 어노테이션 프로세싱을 통한 빈약한 자바 타입 시스템을 확장 ● 초안 제출 2006/10/17, 최종안 승인 2014/2/18, 자바 8에 추가 ● 워싱턴대 마이클 에른스트(Michael Ernst) 교수 주도 ● CheckerFramework와 동시에 진행 (버전 0.1.1 2007/6/7 첫 출시) 소스 코드 기본 타입 확인 플러그인 타입 확인 바이트 코드 자바 컴파일러
  • 52. Checker Framework ● null 안전성 확인 @Nullable, @NonNull, @PolyNull ● null 안전성 외에도 Map 키, 잠금, 순차 자료형(배열, List 등) 색인값, 정규식, 문자열 형식, 단위 등 다수의 고급 타입 확인 기능 제공 ● 자작 타입 확인 기능 추가 가능 ● 특정 환경이나 IDE 독립적, 인텔리제이 플러그인 제공 ● 자바 11 지원
  • 53. @NonNull과 @Nullable public class Address { public final @NonNull String address1; public final @Nullable String address2; public final @NonNull String zipcode; public final @NonNull String city; public final @NonNull String country; private Address(@NonNull String address1, @Nullable String address2, @NonNull String zipcode, @NonNull String city, @NonNull String country) { …… } public static Address of(@NonNull String address1, @NonNull String zipcode, @NonNull String city, @NonNull String country) { return new Address(address1, null, zipcode, city, country); }
  • 54. ● 과도한 어노테이션 사용 예방 ● 기본 @NonNull 필드, 매개변수, 반환값 등 ● 예외적 @Nullable 지역 변수, 타입 캐스트 등 기본 null 정책 public class Address { public final String address1; public final @Nullable String address2; public final String zipcode; public final String city; public final String country; private Address(String address1, @Nullable String address2, String zipcode, String city, String country) { …... } public static Address of(String address1, String zipcode, …… { return new Address(address1, null, zipcode, city, country); }
  • 55. ● @DefaultQualifier ● 패키지(package-info.java)나 클래스 전체의 기본 정책 설정 패키지, 클래스 수준 기본 정책 설정 @DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.LOCAL_VARIABLE) package dev.fupfin.null_safety.strict; @DefaultQualifier(value = Nullable.class, locations = TypeUseLocation.FIELD) class MyClass { Object nullableField = null; @NonNull Object nonNullField = new Object(); }
  • 56. ● 어노테이션 달린 자바 8, 9+ 표준 API ● 자바 11 지원 (어노테이션과 실행 모두) ● 임의의 API에 어노테이션 설정 가능 어노테이션 달린 자바 표준 API 제공 private static void password() { Console console = System.console(); char[] password = console.readPassword(); …… } Error:(30, 27) java: [dereference.of.nullable] dereference of possibly-null reference console
  • 57. ● 단순한 정적 타입 확인이 아닌 코드 흐름과 실행 결과를 반영 ● 코드로 null 확인을 한 경우 @nonNull로 취급 ● 메서드 내부로 제한 ● Nullaway도 이 기능(data flow)를 사용 자동 타입 개선(Automatic type refinement) private static void password() { Console console = System.console(); char[] password = nonNull(console) ? console.readPassword() : new char[0]; …… } OK
  • 58. ● 상황에 따라서 @Nullable과 @NonNull의 두가지 버전이 될 수 있는 메서드 ● @PolyNull로 표시된 것 중 하나가 @Nullable이면 전부 @Nullable로 교체, @NonNull이면 모두 @NonNull로 교체 타입 다형성 지원 @PolyNull @PolyNull T max(@PolyNull T x, @PolyNull T y); @NonNull T max(@NonNull T x, @NonNull T y); //x, y, 반환값 모두 non-null이거나 @Nullable T max(@Nullable T x, @Nullable T y); // 모두 null 가능함
  • 59. ● List, Set, Map, 배열 등 집합 자료형의 요소도 null 확인 ● 기본 @NonNull ● 타입 매개변수에 @Nullable 지정 집합 타입 요소의 null 안정성 지원 private static void printList() { List<String> names = Arrays.asList("kim", "choi", null, "park", "hwang"); for(String name: names) out.println(name); } Error:(42, 43) java: [assignment.type.incompatible] incompatible types in assignment. found : @Initialized @NonNull List<@Initialized @Nullable String>......
  • 60. ● @RequiresNonNull : null 가능한 특정 필드를 특정 메서드가 실행될 동안만 null 불가능 취급 ● @EnsuresNonNull, @EnsuresNonNullIf : 메스드의 실행 후에 특정 식의 결과가 null이 아닌지 확인 메서드 행동 지원
  • 61. 객체 초기화 상태 확인 지원 Declarations Expression Expression’s nullness type, or checker error class C { @NonNull Object f; @Nullable Object g; ... } @NonNull @Initialized C a; a @NonNull a.f @NonNull a.g @Nullable
  • 62. @NonNull @UnderInitialization C b; b @NonNull b.f @MonotonicNonNull b.g @Nullable @Nullable @Initialized C c; c @Nullable c.f error: deref of nullable c.g error: deref of nullable @Nullable @UnderInitialization C d; d @Nullable d.f error: deref of nullable d.g error: deref of nullable
  • 63. 외부 어노테이션 호환 android.annotation.NonNull android.support.annotation.NonNull androidx.annotation.NonNull androidx.annotation.RecentlyNonNull com.sun.istack.internal.NotNull edu.umd.cs.findbugs.annotations.NonNull io.reactivex.annotations.NonNull javax.annotation.Nonnull javax.validation.constraints.NotNull lombok.NonNull org.eclipse.jdt.annotation.NonNull org.eclipse.jgit.annotations.NonNull org.jetbrains.annotations.NotNull org.jmlspecs.annotation.NonNull org.netbeans.api.annotations.common.NonNull org.springframework.lang.NonNull android.annotation.Nullable android.support.annotation.Nullable androidx.annotation.Nullable androidx.annotation.RecentlyNullable com.sun.istack.internal.Nullable edu.umd.cs.findbugs.annotations.Nullable edu.umd.cs.findbugs.annotations.CheckForNull edu.umd.cs.findbugs.annotations.PossiblyNull edu.umd.cs.findbugs.annotations.UnknownNullness io.reactivex.annotations.Nullable javax.annotation.Nullable javax.annotation.CheckForNull org.eclipse.jdt.annotation.Nullable org.eclipse.jgit.annotations.Nullable org.jetbrains.annotations.Nullable org.jmlspecs.annotation.Nullable org.netbeans.api.annotations.common.NullAllowed org.netbeans.api.annotations.common.CheckForNull org.netbeans.api.annotations.common.NullUnknown org.springframework.lang.Nullable
  • 65. 정리 null은 왜 문제인가? ● 모든 참조 타입에 지정 가능한 값(상태?), 언어가 지원할 문제 좋은 코드는 비교적 null에 안전하다 ● null을 예외적으로, 특히 API에 null을 최대한 쓰지 말아라 ● 좋은 객체를 만들자: (상태와 같이) null의 범위를 지역(클래스, 메서드)에 제한하라. ● 사전 조건과 사후 조건을 확인하라: 계약에 의한 설계(design by contract) ● 초기화를 명확히 하라 null에 안전하다고 보장해주는 도구를 쓰자 ● JSR 305 계열의 정적 분석과 JSR 308 계열의 타입 확장 ● 가장 성숙한 자바 타입 확인 확장 기술: Checker Framework
  • 66. 코드에 책임을 지나요? 전문가로서 자신의 코드에 자부심을 느끼나요?