본문 바로가기
IT/Java

이펙티브 자바 8장 - 메서드

by 봉즙 2020. 1. 6.

매개변수가 유효한지 검사하라

 매개변수 검사를 제대로 하지 못하면 원자성을 어기는 결과를 낳을 수 있다. 그렇기에 던지는 예외를 자바독 태그를 이용하여 문서화 시켜야 한다.

 java.util.Object.requireNonNull 메서드는 유연하고 사용하기도 편하기에 수동으로 null 검사를 하지 않아도 된다.

 메서드는 최대한 범용적으로 설계하나 머서드가 건네받은 값으로 무언가 제대로 된 일을 할 수 있다면 매개변수 제약은 적게 만드는 것이 좋다.

 

적시에 방어적 복사본을 만들라

package effectivejava.chapter8.item50;
import java.util.*;

// 코드 50-1 기간을 표현하는 클래스 - 불변식을 지키지 못했다. (302-305쪽)
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;
    }

//    // 코드 50-3 수정한 생성자 - 매개변수의 방어적 복사본을 만든다. (304쪽)
//    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 + "보다 늦다.");
//    }
//
//    // 코드 50-5 수정한 접근자 - 필드의 방어적 복사본을 반환한다. (305쪽)
//    public Date start() {
//        return new Date(start.getTime());
//    }
//
//    public Date end() {
//        return new Date(end.getTime());
//    }

    // 나머지 코드 생략
}

 불변식이 깨지는 것을 방어하기 위해서 Date라는 가변은 새로운 코드를 작성할 때 사용해서는 안된다.
외부로 부터 방어하기 위해서는 생성자에서 받은 가변 매개 변수 각각을 방어적으로 복사해서 복사본을 이용하여 유효성 검사를 하게 되면 멀티 스레드 환경에서 원본의 유효성 검사 후 복사본을 만드는 순간 다른 스레드가 원본 객체를 수정할 위험이 있기에 이러한 순서로 진행해야 한다.
  clone은 final이 아닌 경우 하위 클래스가 필드의 참조르 ㄹprivate 정적 리스트에 담아 뒀다가 공격자에게 리스트에 접근하는 길을 열어줄 가능성이 있기에 매개변수가 제 3자에 의해 확장 될 수 있는 타입이라면 방어적 복사본을 만들 때 clone을 사용해서는 안된다.

 접근자 메서드가 내부의 가변 정보를 드러내는 경우 가변 필드의 방어적 복사본을 반환하는 것으로 방어 할 수 있다.

 인스턴스를 복사하는 데는 일반적을 생성자나 정적 팩터리를 사용하는 것이 좋다. (아이템 13 참조)

 길이가 1이상인 배열은 무조건 가변이며 클래스가 불변이든 가변이든 가변인 내부 객체를 클라이언트에 반환할 때는 원본을 노출하지 않는 것이 좋다. 혹은 배열의 불변 뷰를 반환하는 방법도 존재한다. (아이템 15 참조)

 Date의 경우 버전이 낮다면 Date 참조 대신 Date.getTime()이 반환하는 long정수를 사용하는 방법도 있다.

 만약 호출자가 컴포넌트 내부를 수정하지 않으리라 확신되거나 방어적 복사를 수행하는 비용이 너무 크다면 방어적 복사를 생략할 수 있으며, 호출자에서 해당 매개변수나 반환 값을 수정하지 말아야함을 문서화 시켜주는 것이 좋다.

 

메서드 시그니처를 신중히 설계하라

 메서드 이름은 항상  표준 명명 규칙을 따라야한다. 또한 편의 메서드를 너무 많이 만드는 것은 좋지 않으며, 자주 사용되는 경우가 아니라면 만들지 않는 것이 좋다.
 또한, 매개변수 목록은 짧게 유지하는 것(4개 이하)이 좋으며 같은 타입의 매개변수가 여러개 연달아 나오는 경우를 피하도록하는 것이 좋다. 그 방법으로 크게 3가지가 있다. 첫번째, 여러 메서드로 쪼개면  각각은 원래 매개변수 목록의 부분집합을 받게 되며, 메서드가 너무 많아지는 경우도 생기기도 하지만 직교성을 높이게 된다면 오히려 그 수 가 줄어들 수 있다. (예. java.util.List)

※ 직교성 : 공통점이 없는 기능들이 잘 분리되어 있다는 뜻으로 해석 가능하다.

 둘째,  매개변수 여러개를 묶어주는 도우미 클래스는 만든다. 일반적으로 도우미 클래스는 정적 멤버 클래스로 두는 것이 보통이다.
 세번째, 두가지 방법을 혼합한 것으로 객체 생성에 사용한 빌더 패턴을 메서드를 호출해 응용하는 것이다. 모든 매개변수를 하나로 추상화한 후, 객체를 정의하고 클라이언트에서 이 객체의 세터 메서드를 호출해 필요한 값을 설정하도록 한다. excute 메서드를 호출해 설정한 매개변수들의 유효성을 검사하고 객체를 넘겨 계산을 수항한다.

 매개변수의 타입으로는 클래스보다는 인터페이스가 더 낫다. 또한 boolean 보다는 원소 2개짜리의 열거 타입이 더 낫다.

 

다중정의는 신중히 사용하라

 오버라이딩(재정의)한 메서드는 동적으로 선택되며, 오버로딩(다중정의)한 메서드는 정적으로 선택되기에 오버로딩의 경우 컴파일 타임에서 어느 메서드를 호출할지 결정되기에 메서드를 재정의한다면 해당 객체의 런타임 타입이 메서드 호출의 기준이 된다.

오버라이딩 메서드의 경우 가장 하위에서 정의한 메서드가 실행되며, 오버로딩의 경우 런타입 타입은 중요하지 않으며 컴파일 타임에 매개변수의 컴파일 타임 타입에 의해 이뤄진다.

 안전하게 가려면 매개변수가 같은 오버로딩은 만들지 않는 것이 좋다. 생성자의 경우에는 이름을 다르게 지을 수 없기에 두번째 부터는 오버로딩이 된다. 정적팩터리라는 대안을 활용(아이템1)하는 것이 좋으며, 생성자는 오버라이딩 할 수 없으니 오버라이딩과 오버 로딩이 혼용될 걱정은 하지 않아도 된다.

 메서드를 오버로딩 하는 경우 서로 다른 함수형 인터페이스라 하더라도 같은 위치의 인수로 받아서는 안된다.

 

가변인수는 신중히 사용하라

 가변인수는 인수 개수가 정해지지 않은 경우 유용하다.

 인수 개수가 일정하지 않은 메서드 정의시 가변인수가 반드시 필요하며 메서드 정의시 필수 매개 변수는 가변인수 앞에두며, 가변인수 사용시 성능에 대해서도 고려해야한다.

 

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

 null을 반환하는 API는 사용하기 어려우며 오류 처리 코드가 늘어나게되며 성능면에서도 좋지 못하다.

'IT > Java' 카테고리의 다른 글

Field Injection | Contructor Injection  (0) 2020.07.07
정규식 추출  (0) 2020.03.17
이펙티브 자바 - 7장 람다와 스트림  (0) 2020.01.03
이펙티브 자바 6장 - enum 2  (0) 2020.01.03
이펙티브 자바 6장 - enum  (0) 2019.12.12

댓글