프로그래머의 통제권 밖에 있어 언제 어떻게 변경될지 모르는 외부 클래스를 상속할 때는 주의해야 한다. 이러한 클래스를 상속하게 하려면 상속할 때의 주의점을 문서화해야 한다. 상속을 고려한 설계와 문서화 상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 (자기 사용) 문서로 남겨야 한다. 클래스의 API로 공개된 메서드에서 클래스 자신의 또 다른 메서드를 호출할 수 있다. 이때 호출한 메서드가 재정의 가능한 메서드라면, 이 사실을 메서드의 API 설명에 명시해야 한다. 더 넓게 말하자면, 재정의 가능 메서드를 호출할 수 있는 모든 상황을 문서로 남겨야 한다. 이를 도와주는 용도로 @implSpec를 이용하면 javadoc이 내부 동작 방식을 설명하는 "Implementation Re..
상속은 코드를 재사용하는 강력한 수단이지만, 최선은 아니다. 잘못 사용하면 오류를 내기 쉽다. 1. 다른 패키지의 구체 클래스 상속의 단점 해당 장의 '상속'은 클래스가 다른 클래스를 확장하는 것을 의미한다. 다른 패키지의 구체 클래스를 상속하는 일은 위험하다. (인터페이스 확장 및 구현 제외) 메서드 호출과는 다르게 상속은 캡슐화를 깨트린다. 하위 클래스가 상위 클래스의 구현에 의존할 수 밖에 없기 때문이다. 상위 클래스의 구현은 변경될 수 있는데, 그러다보면 하위 클래스는 코드 수정 없이도 망가질 수 있다. // 상속을 잘못 사용한 예 class InstrumentedHashSet extends HashSet { // 추가된 원소의 수 private int addCount = 0; //생성자 publ..
변경 가능한 클래스로 만들 타당한 이유가 없다면, 가능한 한 변경 불가능한 불변 클래스로 만드는 것이 좋다. 불변 클래스란 객체가 생성된 시점부터 파괴되는 시점까지 그 내부 값을 수정(변경)할 수 없는 클래스를 말한다. 대표적인 예로는 String, Wrapper Class, BigInteger, BigDecimal 등이 있다. 불변 클래스는 설계, 구현, 사용이 쉬우며, 오류가 생길 여지가 적고 안전하다. 불변 클래스 생성 5가지 규칙 1. 객체 상태를 변경하는 메서드를 제공하지 않는다. (Setter) 2. 클래스를 확장할 수 없도록 한다. 하위 클래스에 의해 객체의 상태를 변하게 하는 사태를 막아준다. 3. 모든 필드를 final로 선언한다. 설계자의 의도를 명확히 드러내는 방법이다. 4. 모든 필..
단순히 인스턴스 필드를 모아놓는 클래스는 public이면 안된다. 데이터 필드에 직접 접근이 가능해져 캡슐화의 이점을 제공하지 못하기 때문이다. // public이면 안된다!! class Point { public double x; public double y; } 위와 같은 클래스는 API를 수정하지 않고는 내부 표현을 바꿀 수 없고, 불변식을 보장할 수 없으며, 외부에서 필드에 접근할 때 부수 작업을 수행할 수도 없다. public 클래스의 가변 필드는 절대 노출하면 안 된다! 캡슐화의 이점을 제공하지 못함 API 수정 없이 내부 표현을 변경하지 못한다. 불변식을 보장할 수 없다. 외부에서 필드에 접근할 때 부수 작업을 수행할 수 없다. 이런 클래스는 접근자와 변경자 메서드를 활용해 데이터를 캡슐화할..
내부 데이터와 내부 구현 정보가 외부로부터 잘 숨겨진 컴포넌트는 잘 설계된 컴포넌트라 할 수 있다. 즉, 정보 은닉과 캡슐화가 잘 되어있는 컴포넌트는 잘 설계된 컴포넌트라 볼 수 있다. 잘 설계된 컴포넌트는 다음과 같은 특징을 가지고 있다. 1. 캡슐화가 잘 되어있다. 2. 모든 내부 구현을 완벽히 숨겨, 구현과 노출되는 API가 깔끔하게 분리되어있다. 3. 오직 API로만 다른 컴포넌트와 메시지를 주고받으며, 서로의 내부 동작 방식에 신경 쓰지 않는다. 그만큼 정보 은닉, 캡슐화의 개념은 오늘날 소프트웨어 설계의 근간이되는 중요한 원리이다. 캡슐화의 장점 1. 서로 내부 구현을 신경쓰지 않아도 되기 때문에 컴포넌트를 병렬로 개발할 수 있어 시스템 개발 속도가 빨라진다. (생산성 향상) 2. 잘 분리되어..
Comparable을 구현하면 손쉽게 컬렉션을 정렬할 수 있다. Comparable 인터페이스를 활용하는 수많은 제네릭 알고리즘과 컬렉션의 힘을 좁쌀만 한 노력으로 누릴 수 있는 것이다. 따라서 알파벳, 숫자, 연대와 같이 순서가 명확한 클래스를 작성한다면 반드시 Comparable을 구현하자. compareTo 메서드의 일반 규약 compareTo 메서드의 일반 규약은 equals의 규약과 비슷하다. compareTo는 기준 객체와 주어진 객체의 순서를 비교한다. compareTo 반환값의 기준 기준 객체 주어진 객체 → 1 반환 첫 번째 규약 x.compareTo(y) 0..
클래스에서 clone을 재정의 하기 위해서는 해당 클래스에 Cloneable 인터페이스를 상속받아 구현한다. 그런데 clone 메서드는 Cloneable 인터페이스가 아닌 Object에 선언되어있다. Cloneable 인터페이스는 아무것도 선언되어있지 않은 빈 인터페이스이다. 메서드 하나 없는 Cloneable 인터페이스는 대체 무슨 일을 할까? Cloneable 인터페이스의 역할 Cloneable 인터페이스는 상속받은 클래스가 복제해도 되는 클래스임을 명시하는 용도의 믹스인 인터페이스(mixin interface)이다. mixin interface란? 클래스가 본인의 기능 외에 추가로 구현할 수 있는 자료형으로, 선택적인 기능을 제공한다는 사실을 명시하기 위해 쓰인다. Cloneable 인터페이스는 O..
Object의 기본 toString 메서드가 우리가 작성한 클래스에 적합한 문자열을 반환하는 경우는 거의 없다. Object.toString()은 [클래스이름]@[16진수로 표시한 해시코드]의 형태로 반환될 뿐이다. toString() 일반 규약 1) 간결하면서 사람이 읽기 쉬운 형태의 유익한 정보를 반환해야 한다. 2) 모든 하위 클래스에서 이 toString 메서드를 재정의해야 한다. 실전에서 toString은 객체 내의 중요 정보를 전부 담아 반환하는 것이 좋다. toString을 잘 구현하면 사용하기 편하고 디버깅이 쉬워진다. toString 구현 시 문서화 포맷 명시하는 경우 [장점] 1) 포맷을 명시하면 그 객체는 표준적이고, 명확하고, 사람이 읽을 수 있다. 2) 값 그대로 입출력에 사용하거..
1. equals를 재정의한 클래스 모두에서 hashCode도 재정의해야 한다. hashCode를 재정의하지 않으면 hashCode 일반 규약을 어기게 되어 HashMap이나 HashSet 같은 컬렉션의 원소로 사용할 때 문제가 발생한다. hashCode 재정의 조건 equals 비교에 사용되는 정보가 변경되지 않는다면, hashCode는 항상 같은 값을 반환해야 한다. equals가 두 객체가 같다고 판단하면, hashCode는 같은 값을 반환해야 한다. equals가 두 객체가 다르다고 판단해도, hashCode가 꼭 다를 필요는 없다. 다만, 다른 객체에서는 다른 값을 반환해야 성능이 좋아진다. hashCode 재정의를 잘못했을 때 크게 문제가 되는 조항은 2번이다. 논리적으로 같은 객체는 같은 해..