8장 - 메서드
💡 매개변수가 유효한지 검사하라
“매개변수의 제약들을 문서화하고 메서드 코드 시작 부분에서 명시적으로 검사하자”
1. 매개변수 검사
- 매개변수 검사를 수행하지 않았을 때 문제점
- 메서드가 수행되는 중간에 모호한 예외를 던지며 실패할 수 있음.
- 메서드가 잘 수행되지만 잘못된 결과를 반환할 수 있음
- 메서드는 문제없이 수행됐지만, 어떤 객체를 이상한 상태로 만들어서 미래의 알 수 없는 시점에 이 메서드와는 관련 없는 오류를 낼 수 있음.
→ 매개변수 검사에 실패하면 실패 원자성(failure atomicity)을 어기는 결과를 낳을 수 있음.
- 매개변수 검사는 메서드 몸체가 실행되기 전에 진행해야 함.
- 매개변수의 제약을 문서화한다면 그 제약을 어겼을 때 발생하는 예외도 함께 기술해야 함.
2. 단언문(assert)
- 공개되지 않은 메서드라면 패키지 제작자가 메서드가 호출되는 상황을 통제할 수 있음.
- 따라서 오직 유효한 값만이 메서드에 넘겨지리라는 것을 보증할 수 있고, 그렇게 해야 함.
→ public이 아닌 메서드라면 단언문(assert
)을 사용해 매개변수 유효성을 검증할 수 있음.
- 단언문 설명
- 실패하면
AssertionError
를 던진다. - 런타임에 아무런 효과도, 아무런 성능 저하도 없다.
- 실패하면
💡 적시에 방어적 복사본을 만들라
“클래스가 클라이언트로부터 받는 혹은 클라이언트로 반환하는 구성요소가 가변이라면
그 요소는 반드시 방어적으로 복사해야 한다”
1. 방어적 복사 예시
// 기간을 표현하는 클래스 - 불변식을 지키지 못했다.
public final class Period {
private final Date start;
private final Date end;
/**
* @param start 시작 시각
* @param end 종료 시각. 시작 시각보다 뒤여야 한다.
* @throws IllegalArgumentException 시작 시각이 종료 시각보다 늦을 때 발생한다.
* @throws NullPointerException start나 end가 null이면 발생한다.
*/
public Period(Date start, Date end) {
if (start.compareTo(end) > 0)
throw new IllegalArgumentException(
start + "가 " + end + "보다 늦다.");
this.start = start;
this.end = end;
}
public Date start() {
return start;
}
public Date end() {
return end;
}
public String toString() {
return start + " - " + end;
}
// 나머지 코드 생략
}
- 위의 코드에서
Date
가 가변이라는 사실을 이용하면 쉽게 불변식을 깨뜨릴 수 있음
// Period 인스턴스의 불변식을 깨뜨리는 예시
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78); // p의 내부를 쉽게 수정.
- [참고]
Date
객체는 낡은 API이니 새로운 코드를 작성할 때는 더 이상 사용하면 안 됨LocalDateTime
이나ZonedDateTime
혹은Instant
를 사용하자
- 그렇다면
Period
를 불변으로 만드는 방법은 무엇일까?
→ 생성자에서 받은 가변 매개변수를 각각 방어적으로 복사(defensive copy)하자
// 수정한 생성자 - 매개변수의 방어적 복사본을 만든다.
public Period(Date start, Date end) {
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
// 매개변수 유효성 검사
if (this.start.compareTo(this.end) > 0)
throw new IllegalArgumentException(
this.start + "가 " + this.end + "보다 늦다.");
}
- 매개변수의 유효성을 검사하기 전에 방어적 복사본을 만들고, 이 복사본으로 유효성을 검사한 점에 주목하자.
- 반드시 이렇게 작성해야 함.
- Why? 멀티스레딩 환경이라면 원본 객체의 유효성을 검사한 후 복사본을 만드는 그 찰나의 취약한 순간에 다른 스레드가 원본 객체를 수정할 위험이 있기 때문.
- 이러한 공격을 검사시점/사용시점(time-of-check/time-of-use), TOCTOU 공격이라고 함.
- 아직
Period
클래스는 취약하다. 접근자가 내부의 가변 정보를 직접 드러내고 있음. 아래와 같은 공격이 가능함
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
p.end().setYear(78); // p의 내부를 쉽게 변경
- 이 공격을 막으려면 아래와 같이 단순히 접근자가 가변 필드의 방어적 복사본을 반환하면 됨.
// 수정한 접근자 - 필드의 방어적 복사본을 반환한다.
public Date start() {
return new Date(start.getTime());
}
public Date end() {
return new Date(end.getTime()) ;
}
→ 이제 Period
자신 말고는 가변 필드에 접근할 방법이 없다. (모든 필드가 객체 안에 완벽히 캡슐화)
- 교훈 : “되도록 불변 객체들을 조합해 객체를 구성해야 방어적 복사를 할 일이 줄어든다”
2. 방어적 복사의 수행
- 가변 객체라면 그 객체가 클래스에 넘겨진 뒤 임의로 변경되어도 그 클래스가 문제없이 동작할지를 따져보자
→ 확신할 수 없다면 방어적 복사본을 저장하자
- 방어적 복사를 수행하지 않아도 되는 경우
- (같은 패키지에 속하는 등의 이유로) 클라이언트가 컴포넌트 내부를 수정하지 않으리라 확신할 수 있을 때
- 방어적 복사 비용이 너무 클 때.
→ 이런 경우에 방어적 복사를 수행하는 대신 해당 구성요소를 수정했을 때의 책임이 클라이언트에 있음을 문서에 명시하자.
💡 메서드 시그니처를 신중히 설계하라
“API 설계 요령”
1. 메서드 이름을 신중히 짓자
- 항상 표준 명명 규칙을 따라야 함.
- 같은 패키지에 속한 다른 이름들과 일관되게 짓는 게 최우선 목표
- 개발자 커뮤니티에서 널리 받아들여지는 이름을 사용하는 것이 그 다음 목표
- 애매하면 자바 라이브러리 API 가이드를 참조하자.
2. 편의 메서드를 너무 많이 만들지 말자
- 메서드가 너무 많은 클래스(혹은 인터페이스)는 익히고, 사용하고, 문서화하고, 테스트하고, 유지보수하기 어려움.
→ 확신이 서지 않으면 만들지 말자
3. 매개변수 목록은 짧게 유지하자
- 4개 이하가 좋음
- 같은 타입의 매개변수가 연달아 나오는 것은 해롭다..
- 실수로 순서를 바꿔 입력해도 그대로 컴파일되고 실행 됨. 단지 의도와 다르게 동작할 뿐..
- 과하게 긴 매개변수 목록을 짧게 줄여주는 기술 3가지
- 여러 메서드로 쪼갠다.
- 매개변수 여러 개를 묶어주는 도우미 클래스를 만든다.
- 일반적으로 이런 도우미 클래스는 정적 멤버 클래스로 둠
- 빌더 패턴을 메서드 호출에 응용한다.
- 매개변수를 하나로 추상화한 객체를 정의하고, 클라이언트에서 이 객체의 세터(setter) 메서드를 호출해 필요한 값을 설정하게 하는 방식.
- 유효성 검증 및 설정이 완료된 객체를 메서드에 넘기면 됨.
- 매개변수가 많고 그 중 일부는 생략해도 괜찮을 때 도움이 되는 방식
4. 매개변수의 타입으로는 클래스보다는 인터페이스가 더 낫다
- 인터페이스 대신 클래스를 사용하면 클라이언트에게 특정 구현체만 사용하도록 제한하는 꼴.
- 혹시라도 입력 데이터가 다른 형태로 존재한다면 명시한 특정 구현체로 옮겨 담느라 비싼 복사 비용을 치러야 함.
5. 매개변수로 boolean보다는 원소 2개짜리 열거 타입이 낫다
- 메서드 이름상
boolean
을 받아야 의미가 더 명확할 경우는 예외임. Thermometer.newInstance(true)
보다는Thermometer.newInstance(TemperatureScale.CELSIUS)
가 하는 일을 훨씬 명확히 알려줌.
💡 다중정의는 신중히 사용하라
“프로그래밍 언어가 다중정의를 허용한다고 해서 다중정의를 꼭 활용하라는 뜻은 아니다”
1. 다중정의 메서드 선택 시점
// 컬렉션 분류기 - 오류! 이 프로그램은 무엇을 출력할까?
public class CollectionClassifier {
public static String classify(Set<?> s) {
return "집합";
}
public static String classify(List<?> lst) {
return "리스트";
}
public static String classify(Collection<?> c) {
return "그 외";
}
public static void main(String[] args) {
Collection<?>[] collections = {
new HashSet<String>(),
new ArrayList<BigInteger>(),
new HashMap<String, String>().values()
};
for (Collection<?> c : collections)
System.out.println(classify(c));
}
}
- 위 프로그램은 무엇을 출력할까?
- “집합”, “리스트”, “그 외”를 출력할 것으로 예상.
- 이상하게도 위 프로그램은 “그외”만 세번 연달아 출력한다.
- 이유 : 다중정의 메서드의 선택은 컴파일타임에 오직 매개변수의 컴파일타임 타입에 의해 이뤄지기 때문
- 즉,
main
메서드에서for
문 안의c
는 항상Collection<?>
타입이므로 항상 세 번째classify(Collection<?> c)
가 호출된 것. - 의도대로 동작시키려면 아래 코드처럼
classify
메서드를 하나로 합친 후instanceof
로 검사하면 됨
public static String classify(Collection<?> c) {
return c instanceof Set ? "집합" :
c instanceof List ? "리스트" : "그 외";
}
2. 다중정의 주의점
- 다중정의가 혼동을 일으키는 상황을 피해야 한다.
- API 사용자가 매개변수를 넘기면서 어떤 다중정의 메서드가 호출될지를 모른다면 프로그램이 오동작하기 쉽다.
- 안전하고 보수적으로 가려면 매개변수 수가 같은 다중정의는 만들지 말자
- 다중정의하는 대신 메서드 이름을 다르게 지어주는 길도 항상 열려 있음.
- 메서드를 다중정의할 때, 서로 다른 함수형 인터페이스라도 같은 위치의 인수로 받아서는 안 된다.
- 서로 다른 함수형 인터페이스라도 서로 근본적으로 다르지 않음.
💡 가변인수는 신중히 사용하라
“메서드 정의 시 필수 매개변수는 가변인수 앞에 두고, 가변인수를 사용할 때는 성능 문제까지 고려하자”
1. 간단한 가변인수 활용 예시
// 간단한 가변인수 활용 예
static int sum(int... args) {
int sum = 0;
for (int arg : args)
sum += arg;
return sum;
}
- 위 코드는 입력받은
int
인수들의 합을 계산해주는 가변인수 메서드임.(인수가 0개여도 됨)
2. 인수가 1개 이상일 때
- 잘못된 예시
// 인수가 1개 이상이어야 하는 가변인수 메서드 - 잘못 구현한 예!
static int min(int... args) {
if (args.length == 0)
throw new IllegalArgumentException("인수가 1개 이상 필요합니다.");
int min = args[0];
for (int i = 1; i < args.length; i++)
if (args[i] < min)
min = args[i];
return min;
}
- 위 코드의 문제점
- 인수를 0개만 넣어 호출하면 (컴파일타임이 아닌) 런타임에 실패 함.
- 코드도 지저분함.
args
유효성 검사를 명시적으로 해야함.
- 올바른 예시
// 인수가 1개 이상이어야 할 때 가변인수를 제대로 사용하는 방법
static int min(int firstArg, int... remainingArgs) {
int min = firstArg;
for (int arg : remainingArgs)
if (arg < min)
min = arg;
return min;
}
- 첫 번째로는 평범한 매개변수를 받고, 두 번째로 가변인수를 받으면 잘못된 예시의 문제점들이 말끔히 해결됨.
3. 성능 최적화
- 예시) 메서드 호출의 95%가 인수를 3개 이하로 사용하는 상황
→ 아래와 같이 인수가 0개인 것부터 4개인 것까지, 5개를 다중정의
public void foo() {}
public void foo(int a1) {}
public void foo(int a1,int a2) {}
public void foo(int a1, int a2, int a3) {}
public void foo(int a1, int a2, int a3, int... rest) {}
- 위와 같이 구현하면 마지막 다중정의 메서드가 인수 4개 이상인 5%의 호출을 담당함
→ 메서드 호출 중 단 5%만이 배열을 생성하게 됨
- 대다수의 성능 최적화와 마찬가지로 이 기법도 보통 때는 별 이득이 없지만, 꼭 필요한 특수한 상황에서 사막의 오아시스가 되어줄 것임.
💡 null이 아닌, 빈 컬렉션이나 배열을 반환하라
“
null
을 반환하는 API는 사용하기 어렵고 오류처리 코드도 늘어난다”
1. null을 반환
// 재고가 하나도 없을 때 null을 반환하는 예
public List<Cheese> getCheeses() {
return cheesesInStock.isEmpty() ? null
: new ArrayList<>(cheesesInStock);
}
- 이 코드처럼
null
을 반환한다면, 클라이언트는 이null
상황을 처리하는 코드를 아래처럼 추가로 작성해야 함
List<Cheese> cheeses = shop.getCheeses();
if(cheeses != null && cheeses.contains(Cheese.STILTON))
System.out.println("좋았어, 바로 그거야.");
- 클라이언트는
cheeses != null
처럼 방어코드를 항상 작성해줘야 함.
2. 빈 컬렉션을 반환
// 빈 컬렉션을 반환하는 올바른 예
public List<Cheese> getCheeses() {
return new ArrayList<>(cheesesInStock);
}
- 사용 패턴에 따라 빈 컬렉션 할당이 성능 저하의 주범이 되는 경우는 어떻게 하지?
Collections.emptyList
,Collections.emptySet
과 같은 빈 불변 컬렉션을 반환하면 됨
Collections.emptyList
사용 예시
// 최적화 - 빈 컬렉션을 매번 새로 할당하지 않도록 함.
public List<Cheese> getCheeses() {
return cheesesInStock.isEmpty() ? Collections.emptyList()
: new ArrayList<>(cheesesInStock);
}
- 배열을 쓸 때도 마찬가지임
→ 절대 null을 반환하지 말고 길이가 0인 배열을 반환하라
// 길이가 0일 수도 있는 배열을 반환하는 올바른 방법
public Cheese[] getCheeses() {
return cheesesInStock.toArray(new Cheeses[0]);
}
- 이 방식이 성능을 떨어뜨릴 것 같다면 길이 0짜리 배열을 미리 선언해두고 반환하면 됨.
💡 옵셔널 반환은 신중히 하라
“옵셔널 반환에는 성능 저하가 뒤따르니, 성능이 민감한 메서드라면
null
을 반환하거나 예외를 던지는 편이 나을 수 있다”
1. Optional
Optional<T>
는 null이 아닌 T 타입 참조를 하나 담거나, 혹은 아무것도 담지 않을 수 있음- ‘비었다’ : 아무것도 담지 않은 옵셔널
- ‘비지 않았다’ : 어떤 값을 담은 옵셔널
- 옵셔널은 원소를 최대 1개 가질 수 있는 불변 컬렉션임
- 옵셔널을 반환하는 메서드는 예외를 던지는 메서드보다 유연하고 사용하기 쉬우며, null을 반환하는 메서드보다 오류 가능성이 작다
2. 옵셔널 반환 예시
// 컬렉션에서 최댓값을 구해 Optional<E>로 반환한다.
public static <E extends Comparable<E>>
Optional<E> max(Collection<E> c) {
if (c.isEmpty())
return Optional.empty();
E result = null;
for (E e : c)
if (result == null || e.compareTo(result) > 0)
result = Objects.requireNonNull(e);
return Optional.of(result);
}
Optional.empty()
: 빈 옵셔널 생성Optional.of(value)
: 값이 든 옵셔널 생성value
파라미터에null
을 넘기면NullPointerException
을 던지니 주의하자
Optional.ofNullable(value)
:null
값도 허용하는 옵셔널 생성- 스트림의 종단 연산 중 상당수가 옵셔널을 반환 함.
→ 위의 메서드를 아래 코드처럼 리팩터링할 수 있음.
// 컬렉션에서 최댓값을 구해 Optional<E>로 반환한다. - 스트림 버전
public static <E extends Comparable<E>>
Optional<E> max(Collection<E> c) {
return c.stream().max(Comparator.naturalOrder());
}
3. 옵셔널 활용
- 기본값을 설정하는 방법
// 기본 값을 정해둘 수 있음
String lastWordInLexicon = max(words).orElse("단어 없음...");
- 원하는 예외를 던지는 방법
Toy myToy = max(toys).orElseThrow(TemperTantrumException::new)
- 항상 값이 채워져 있다고 가정
Element lastNobleFas = max(Elements.NOBLE_GASES).get();
isPresent()
: 옵셔널이 채워져 있으면true
, 비어 있으면false
를 반환
4. 옵셔널 사용 규칙
- 어떤 경우에 메서드 반환 타입
T
대신Optional<T>
로 선언해야 할까?
→ 기본 규칙 : 결과가 없을 수 있으며, 클라이언트가 이 상황을 특별하게 처리해야 한다면 Optional<T>
를 반환
- 컬렉션, 스트림, 배열 같은 컨테이너 타입은 옵셔널로 감싸면 안 됨.
- ex)
Optional<List<T>>
를 반환하지 말고List<T>
를 반환하자 - 빈 컨테이너를 그대로 반환하면 클라이언트에 옵셔널 처리코드를 넣지 않아도 됨.
- ex)
- 박싱된 기본 타입을 담은 옵셔널을 반환하는 일은 없도록 하자.
- 박싱된 기본 타입을 담는 옵셔널은 기본 타입보다 두 겹이나 값을 감싸서 무거울 수 밖에 없음.
- 대체채로
int
,long
,double
전용 옵셔널 클래스들이 있음OptionalInt
,OptionalLong
,OptionalDouble
- 옵셔널을 컬렉션의 키, 값, 원소나 배열의 원소로 사용하는 게 적절한 상황은 거의 없다.
- ex) 맵의 키로 옵셔널을 사용하는 경우, 맵 안에 키가 없다는 사실을 나타내는 방법이 두 가지가 됨. → 쓸데없이 복잡성만 높여서 혼란과 오류 가능성을 키울 뿐임.
- 옵셔널 반환의 성능 저하
Optional
도 엄연히 새로 할당하고 초기화해야 하는 객체이고, 그 안에서 값을 꺼내려면 메서드를 호출해야 함.- 성능이 중요한 상황에서는 옵셔널보다는
null
을 반환하거나 예외를 던지는 편이 나을 수 있음
💡 공개된 API 요소에는 항상 문서화 주석을 작성하라
“문서화 주석은 API를 문서화하는 가장 훌륭하고 효과적인 방법”
1. 자바독(Javadoc) 유틸리티
- 자바독은 소스코드 파일에서 문서화 주석(doc comment; 자바독 주석)이라는 특수한 형태로 기술된 설명을 추려 API로 문서로 변환해 줌.
- How to Write Doc Comments for the Javadoc Tool
2. 문서화 주석 지침
- API를 올바르게 문서화하려면 공개된 모든 클래스, 인터페이스, 메서드, 필드 선언에 문서화 주석을 달아야 함.
- 메서드용 문서화 주석에는 해당 메서드와 클라이언트 사이의 규약을 명료하게 기술해야 함.
- how(메서드가 어떻게 동작하는지)가 아니라 what(무엇을 하는지)를 기술해야 함
- 메서드를 호출하기 위한 전제조건(precondition)을 모두 나열해야 함.
- 일반적으로 전제조건은
@throw
태그로 비검사 예외를 선언하여 암시적으로 기술함 @param
태그를 이용해 그 조건에 영향받는 매개변수에 기술할 수도 있음
- 일반적으로 전제조건은
- 메서드가 성공적으로 수행된 후 만족해야 하는 사후조건(postcondition)도 모두 나열해야 함.
- 부작용도 문서화해야 함.
- 부작용 : 사후조건으로 명확히 나타나지는 않지만 시스템의 상태에 어떠한 변화를 가져오는 것
- ex) 백그라운드 스레드를 시작시키는 메서드
- 메서드의 계약(contract)을 완벽히 기술하려면 모든 매개변수에
@param
태그를, 반환 타입이void
가 아니라면@return
태그를, 발생할 가능성이 있는 (검사든 비검사든) 모든 예외에@thorw
태그를 달아야 한다. - 태그 사용 관례
- 관례상
@param
태그와@return
태그의 설명은 해당 매개변수가 뜻하는 값이나 반환값을 설명하는 명사구를 사용함. - 관례상
@param
,@return
,@throws
태그의 설명에는 마침표를 붙이지 않음
- 관례상
{@code}
태그- 태그로 감싼 내용을 코드용 폰트로 렌더링
- 태그로 감싼 내용에 포함된 HTML요소나 다른 자바독 태그를 무시함.
- 여러 줄로 된 코드 예시를 넣으려면
<pre>{@code ... 코드 ... }</pre>
처럼 사용
- 자기사용 패턴은 자바 8에 추가된
@implSpec
태그로 문서화한다. - API 설명에
<
,>
,&
등의 HTML 메타문자를 포함시키려면{@literal}
태그로 감싸자 - 한 클래스(혹은 인터페이스) 안에서 요약 설명이 똑같은 멤버(혹은 생성자)가 둘 이상이면 안 됨.
- 클래스, 인터페이스, 필드의 요약 설명은 대상을 설명하는 명사절이어야 함.
- 제네릭 타입이나 제네릭 메서드를 문서화 할 때는 모든 타입 매개변수에 주석을 달아야 함.
- 열거 타입을 문서화할 때는 상수들에도 주석을 달아야 함.
- 어노테이션 타입을 문서화할 때는 멤버들에도 모두 주석을 달아야 함.
- 패키지를 설명하는 문서화 주석은
package-info.java
파일에 작성한다. - 클래스 혹은 정적 메서드가 스레드 안전하든 그렇지 않든, 스레드 안전 수준을 반드시 API설명에 포함해야 한다.
{@inheritDoc}
태그를 사용해 상위 타입의 문서화 주석 일부를 상속할 수 있음.
3. 문서화 주석 사용 예시
// 문서화 주석 예
public class DocExamples<E> {
// 메서드 주석
/**
* Returns the element at the specified position in this list.
*
* <p>This method is <i>not</i> guaranteed to run in constant
* time. In some implementations it may run in time proportional
* to the element position.
*
* @param index index of element to return; must be
* non-negative and less than the size of this list
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= this.size()})
*/
E get(int index) {
return null;
}
// 한글 버전
// /**
// * 이 리스트에서 지정한 위치의 원소를 반환한다.
// *
// * <p>이 메서드는 상수 시간에 수행됨을 보장하지 <i>않는다</i>. 구현에 따라
// * 원소의 위치에 비례해 시간이 걸릴 수도 있다.
// *
// * @param index 반환할 원소의 인덱스; 0 이상이고 리스트 크기보다 작아야 한다.
// * @return 이 리스트에서 지정한 위치의 원소
// * @throws IndexOutOfBoundsException index가 범위를 벗어나면,
// * 즉, ({@code index < 0 || index >= this.size()})이면 발생한다.
// */
// E get(int index) {
// return null;
// }
// 자기사용 패턴 등 내부 구현 방식을 명확히 드러내기 위해 @implSpec 사용 (335쪽)
/**
* Returns true if this collection is empty.
*
* @implSpec This implementation returns {@code this.size() == 0}.
*
* @return true if this collection is empty
*/
public boolean isEmpty() {
return false;
}
// 한글 버전
// /**
// * 이 컬렉션이 비었다면 true를 반환한다.
// *
// * @implSpec 이 구현은 {@code this.size() == 0}의 결과를 반환한다.
// *
// * @return 이 컬렉션이 비었다면 true, 그렇지 않으면 false
// */
// public boolean isEmpty() {
// return false;
// }
// 문서화 주석에 HTML이나 자바독 메타문자를 포함시키기 위해 @literal 태그 사용 (336쪽)
/**
* A geometric series converges if {@literal |r| < 1}.
*/
public void fragment() {
}
// 한글 버전
// /**
// * {@literal |r| < 1}이면 기하 수열이 수렴한다.
// */
// public void fragment() {
// }
// 문서화 주석 첫 '문장'에 마침표가 있을 때 요약 설명 처리 (337쪽)
/**
* A suspect, such as Colonel Mustard or {@literal Mrs. Peacock}.
*/
public enum Suspect {
MISS_SCARLETT, PROFESSOR_PLUM, MRS_PEACOCK, MR_GREEN, COLONEL_MUSTARD, MRS_WHITE
}
// 한글 버전
// /**
// * 머스타드 대령이나 {@literal Mrs. 피콕} 같은 용의자.
// */
// public enum Suspect {
// MISS_SCARLETT, PROFESSOR_PLUM, MRS_PEACOCK, MR_GREEN, COLONEL_MUSTARD, MRS_WHITE
// }
// 자바독 문서에 색인 추가하기 - 자바 9부터 지원
/**
* This method complies with the {@index IEEE 754} standard.
*/
public void fragment2() {
}
// 한글 버전
// /**
// * 이 메서드는 {@index IEEE 754} 표준을 준수한다.
// */
// public void fragment2() {
// }
// 열거 상수 문서화
/**
* An instrument section of a symphony orchestra.
*/
public enum OrchestraSection {
/** Woodwinds, such as flute, clarinet, and oboe. */
WOODWIND,
/** Brass instruments, such as french horn and trumpet. */
BRASS,
/** Percussion instruments, such as timpani and cymbals. */
PERCUSSION,
/** Stringed instruments, such as violin and cello. */
STRING;
}
// 한글 버전
// /**
// * 심포니 오케스트라의 악기 세션.
// */
// public enum OrchestraSection {
// /** 플루트, 클라리넷, 오보 같은 목관악기. */
// WOODWIND,
//
// /** 프렌치 호른, 트럼펫 같은 금관악기. */
// BRASS,
//
// /** 탐파니, 심벌즈 같은 타악기. */
// PERCUSSION,
//
// /** 바이올린, 첼로 같은 현악기. */
// STRING;
// }
// 애너테이션 타입 문서화
/**
* Indicates that the annotated method is a test method that
* must throw the designated exception to pass.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
/**
* The exception that the annotated test method must throw
* in order to pass. (The test is permitted to throw any
* subtype of the type described by this class object.)
*/
Class<? extends Throwable> value();
}
// 한글 버전
// /**
// * 이 애너테이션이 달린 메서드는 명시한 예외를 던져야만 성공하는
// * 테스트 메서드임을 나타낸다.
// */
// @Retention(RetentionPolicy.RUNTIME)
// @Target(ElementType.METHOD)
// public @interface ExceptionTest {
// /**
// * 이 애너테이션을 단 테스트 메서드가 성공하려면 던져야 하는 예외.
// * (이 클래스의 하위 타입 예외는 모두 허용된다.)
// */
// Class<? extends Throwable> value();
// }
}
'📚 Reading > Tech' 카테고리의 다른 글
이펙티브 자바 - 10장. 예외 (0) | 2023.01.24 |
---|---|
이펙티브 자바 - 9장. 일반적인 프로그래밍 원칙 (0) | 2023.01.24 |
이펙티브 자바 - 7장. 람다와 스트림 (0) | 2023.01.24 |
이펙티브 자바 - 6장. 열거 타입과 애너테이션 (0) | 2023.01.24 |
이펙티브 자바 - 5장. 제네릭 (0) | 2023.01.24 |