본문 바로가기

STUDY REVIEW/이펙티브 자바 독서스터디

[EFFECTIVE JAVA] 이펙티브 자바 독서스터디 - 8장 메서드

8장 메서드
아이템 49. 매개변수가 유효한지 검사하라
아이템 50. 적시에 방어적 복사본을 만들라
아이템 51. 메서드 시그니처를 신중히 설계하라
아이템 52. 다중정의는 신중히 사용하라
아이템 53. 가변인수는 신중히 사용하라
아이템 54. null이 아닌, 빈 컬렉션이나 배열을 반환하라
아이템 55. 옵셔널 반환은 신중히 하라
아이템 56. 공개된 API 요소에는 항상 문서화 주석을 작성하라

 

 

메서드를 설계할때 주의할 점을 살펴본다는 생각으로 책읽기

매개변수와 반환값을 어떻게 처리할지?

메서드 시그니처는 어떻게 설계할지?

사용성, 견고성, 유연성에 집중하기

 

읽고 느낀 점

책의 반을 넘게 읽어간다.

나 자신이 좀 기특하다.

별도로 이펙티브 자바를 읽는거 외엔 아무것도 하지 않은 나는 쫌 ... ㅎ

 

그리고

그 동안 수 없는 코드를 작성하면서 내 자신이 똥싼 메소드들이 생각난다... 잘 지내니?

누군가 리펙토링을 해주겠지 ...

설마 그대로 남아 대대로 이어지는건 아니겠지 ...?

서ㄹ ... 마

제발 누가 좀 지워주세요...


아이템 49. 매개변수가 유효한지 검사하라

 

메서드 몸체가 실행되기 전에 매개변수를 확인하자.

매개변수의 제약을 문서화한다면 예외처리도 같이 기술화 해야한다. (@throws 자바독 태그를 사용!)

 

+ 책에서 알려주는 예외처리 예제

  • IllegalArgumentException
  • IndexOutOfBoundsException
  • NullpointerException
// 자바의 null 검사 기능 이용하기
this.strategy = Objects.requireNonNull(strategy, "전략");

+ java9에서는 범위 검사 기능도 더해졌다.

  • checkFromIndexSize();
  • checkFromToIndex();
  • checkIndex();

아이템 50. 적시에 방어적 복사본을 만들라

 

클라이언트가 우리의 클래스를 깨뜨리려 혈안이 되어 있다 가정하고 방어적으로 프로그래밍하자!

생성자에게 받은 가변 매개변수 각각을 방어적으로 복사해야 한다.

 

만약 복사 비용이 너무 크거나 클라이언트가 잘못 수정할 일이 없다고 선언한다면 방어적 복사를 하는 대신 해당 구성요소를 수정했을때 책임을 클라이언트에 있음을 명시하자!

 

// 매개변수의 방어적 복사본을 생성
public Period(Date start, Date end) {
    this.start = new Date(start.getTime());
    this.end = new Date(end.getTime());

    if(start.compareTo(end) > 0) {
    	throw new IllegalArgumentException(start + " after " + end);
    }
}

// 필드의 방어적 복사본을 반환
public Date start() { 
	return new Date(start.getTime());
}

public Date end() { 
	return new Date(end.getTime());
}

아이템 51. 메서드 시그니처를 신중히 설계하라

 

개별 아이템으로 두기 애매한 API 설계 요령!

  1. 메서드 이름을 신중하게 짓자.
    1. 긴 이름을 피하자.
  2. 메서드를 너무 많이 만들지 말자.
    1. 필요하다는 확신이 없으면 만들지 말자.
  3. 매개변수 목록을 짧게 유지하자
    1. 4개 이하가 좋다.
    2. 같은 타입의 매개변수가 여러개 나오면 기억하기 해롭다.
  4. 매개변수의 타입으로는 클래스보다 인터페이스가 좋다.

아이템 52. 다중정의는 신중히 사용하라

 

다중정의 (OverLoading, 오버로딩)

 

오버로딩이 혼동을 일으키는 상황을 피해야한다.

만약 매개변수 수가 같은 다중정의라면 필히 혼동이 생길 것이다.

정적 팩토리( item1 )를 사용하는 방안을 생각해보자.

 

상황에 따라 매개변수가 같은 오버로딩을 만들게 된다면 모두 동일한 동작을 하게 생성해야 한다.

그렇지 않다면, 다중정의된 메서드나 생성자를 효과적으로 사용하지 못할것이고, 의도대로 동작하지 않는 이유를 이해하지 못할 것이다.


아이템 53. 가변인수는 신중히 사용하라

 

인수 개수가 일정하지 않은 메서드를 정의해야 한다면, 가변인수가 반드시 필요하다.

메서드를 정의할 때 필수 매개변수는 가변인수 앞에 두고, 가변인수를 사용할 때는 성능 문제까지 고려하자.

// 인수가 1개 이상일때 가변인수를 제대로 사용하는 방법
static int min(int firstArg, int... remainingArgs) {
    int min = firstArg;
    for (int arg : remainingArgs) {
        if (arg < min) {
            min = arg;
        }
    }
    return min;
}

아이템 54. null이 아닌, 빈 컬렉션이나 배열을 반환하라

 

Null값이 아닌 빈 배열이나, 컬렉션을 반환하자.

null을 반환하는 API는 사용하기 어렵고 오류 처리 코드도 늘어난다.

그렇다고 성능이 좋은 것도 아니다.

 

컬렉션이 비어있으면 null을 반환하는 코드, 따라 하지 말 것!

private final List<Cheese> cheesesInStock = ...;

public List<Cheese> getCheeses() {
    return cheesesInStock.isEmpty() ? null : new ArrayList<>(cheesesInStock);
}

빈 컬렉션의 올바른 예시

public List<Cheese> getCheeses() {
    return new ArrayList<>(cheesesInStock);
}

최적화 : 빈 컬렉션을 매번 새로 할당하지 않도록 함

public List<Cheese> getCheeses() {
    return cheesesInStock.isEmpty() ? Collections.emptyList() : new ArrayList<>(cheesesInStock);
}

 

추가예시

// 길이가 0인 배열 반환
public Cheese[] getCheeses() {
    return cheesesInStock.toArray(new Cheese[0]);
}
// 최적화 - 매번 새로 할당하지 않게 하는 방법
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];

public Cheese[] getCheeses() {
    return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
    // 배열을 미리 할당하면 오히려 성능이 나빠진다.
    // return cheesesInStock.toArray(new Cheese[cheesesInStock.size()]);
}

아이템 55. 옵셔널 반환은 신중히 하라

 

옵셔널 활용 1) 기본 값을 설정할 수 있다.

String lastWordInLexicon = max(words).orElse("단어 없음...");
 

옵셔널 활용 2) 원하는 예외를 던질 수 있다.

예외 팩토리를 건네면 예외가 실제 발생하지 않는 한 예외 생성 비용이 들지 않는다.

Toy myToy = max(toys).orElseThrow(TemperTantrumException::new);
 

옵셔널 활용 3) 항상 값이 있음을 확신할 때 값을 꺼내 사용하는 선택지이다.

값이 없다면 NoSuchElementException이 발생한다.

Element lastNobleGas = max(Elements.NOBLE_GASES).get();
 

옵셔널 활용 4) 기본값 설정 비용이 커서 부담이라면 orElseGet을 사용한다면 값이 처음 필요할 때 Supplier<T>를 생성하므로 초기 설정 비용을 낮출 수 있다.

Connection conn = getConnection(dataSource).orElseGet(() -> getLocalConn());

 

하지만,

반환값으로 옵셔널을 사용한다고 해서 무조건 득이 되는 건 아니다.

컬렉션, 스트림, 배열, 옵셔널 같은 컨테이너 타입은 옵셔널로 감싸면 안 된다.

결과가 없을 수 있으며, 클라이언트가 이 상황을 특별하게 처리하길 원하면 Optional<T>로 반환하자.

단, 기본 타입을 담은 옵셔널을 반환하는 일이 없도록 하자.

옵셔널을 컬렉션의 키, 값, 원소나 배열의 원소로 사용하는 적절한 상황이 거의 없기 때문이다.


아이템 56. 공개된 API 요소에는 항상 문서화 주석을 작성하라

 

작성 가이드

@param 모든 매개변수 관례상 명사구를 사용하며 마침표 없이 작성한다.
@return void가 아닌 반환 관례상 명사구를 사용하며 마침표 없이 작성한다.
메서드 설명과 같을 때는 생략할 수 있다.
@throws 발생할 가능성 있는 모든 예외 관례상 마침표 없이 작성한다.
@code 코드용 폰트로 렌더링 HTML 요소나 다른 자바독 태그를 무시한다.
@implSpec 구현스펙 안내 해당 메서드와 하위 클래스 사이의 계약 설명
@literal HTML 요소 무시 @code와 다르게 코드용 폰트로 렌더링하지 않는다.
@index 색인화 자바 9에서 추가되었으며 지정한 용어를 색인화할 수 있다.
@summary 요약 설명 해당 설명에 대한 요약(자바 10부터)

태그 용도 가이드

일반 가이드

  • 가독성 좋게 작성해야 한다.
    • {@literal |r| < 1} VS |r| {@literal < } 1
    • @literal이 필요한 곳은 < 한 곳이지만 가독성을 위해 문장 전체를 감싼다.
  • 패키지 설명 문서 주석은 package-info.java에 작성한다.
  • 모듈 설명은 module-info.java 파일에 작성한다.
  • 제네릭 타입은 모든 타입 매개변수에 주석을 달아준다.
  • 열거형 타입은 상수에도 주석을 달아준다.
  • 애너테이션 타입은 멤버들에도 주석을 달아준다.
  • 클래스 혹은 정적 메서드는 스레드 안전 수준을 반드시 포함한다.
  • 직렬화 할 수 있는 클래스라면 직렬화 형태도 기술한다.
  • 메서드 주석은 상속할 수 있다. 주석이 없는 경우 상위 클래스를 참고한다.
    • 클래스 주석보다는 인터페이스 주석이 더 우선순위가 높다.

https://madplay.github.io/post/effectivejava-chapter8-methods#%EC%95%84%EC%9D%B4%ED%85%9C-50-%EC%A0%81%EC%8B%9C%EC%97%90-%EB%B0%A9%EC%96%B4%EC%A0%81-%EB%B3%B5%EC%82%AC%EB%B3%B8%EC%9D%84-%EB%A7%8C%EB%93%A4%EB%9D%BC

 

[이펙티브 자바 3판] 8장. 메서드

[Effective Java 3th Edition] Chapter8: Methods

madplay.github.io