Chapter4 클래스와 인터페이스
1. 클래스와 멤버의 접근 권한을 최소화하라
- 접근 권한을 최소화 함으로써 소프트웨어의 재사용성을 높이고, 성능 최적화에 도움을 줌.
public
같은 경우, 외부 API가 되기 때문에 쉽게 고치기가 어려운 반면private
는 내부 구현이므로 언제든지 수정이 가능 함- 따라서 최소한의
public
API를 설계해야 함
2. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라
public class에서는 인스턴스 필드를 private로 바꾸고 메서드를 통해 접근을 해야 한다.
/* public class Point { public double x; public double y; //생성자 생략 } */ public class Point { private double x; private double y; public double getX() { return x; } public double getY() { return y; } //생성자 생략 }
3. 변경 가능성을 최소화하라
객체가 파괴되는 순간까지 바뀌지 않는 불변 클래스를 이용해야 한다.
- ex) String, 기본 타입의 박싱된 클래스, BigInteger, BigDecimal
- 스레드에 안전, 동기화 필요 없음.
- 객체 생성 비용이 계속 듦
만드는 방법
- 객체의 상태를 변경하는 메서드를 제공하지 않는다.
- 클래스를 확장할수 없도록 한다.
- 모든 필드를 private final로 선언한다.
- 자신 외의 내부의 가변 컴포넌트에 접근할 수 없도록 한다.
public final class Coordinate { private final double x; private final double y; public Coordinate(double x, double y) { this.x = x; this.y = y; } public double getX() { return x; } public double getY() { return y; } public Coordinate plus(Coordinate c) { return new Coordinate(x + c.getX, y + c.getY); // 새로운 객체(불변 객체)를 반환 } // ... }
만약 불변으로 만들 수 없을 때는 변경할 수 있는 부분을 최소한으로 줄여야 한다.
4. 상속보다는 컴포지션을 사용하라
상속은 메서드 호출과는 달리 캡슐화를 깨트릴 가능성이 있고 하위 클래스 동작에 예상하지 못한 문제를 발생시킬수 있다. → 컴포지션으로 해결해야 한다.
- 상속은 is-a 관계일때만 사용해야 한다.
컴포지션 : 기존 클래스가 새로운 클래스의 구성요소로 쓰이는 것
- 새로운 클래스를 만든 후에 기존 클래스를 private 필드인 인스턴스로 사용 → 래퍼 클래스
5. 추상 클래스보다는 인터페이스를 우선하라
인터페이스는 다중 구현이 되지만 추상 클래스는 하나만 상속해야 한다.
인터페이스는 기존 클래스에 쉽게 덧씌울 수 있기 때문에 추상 클래스보다 유용하다.
- 자바 8부터는 인터페이스에 디폴트 메서드가 추가 가능한데, equals와 hashCode 같은 메서드를 디폴트 메서드로 사용해서는 안된다.
6. 인터페이스는 구현하는 쪽을 생각해 설계하라
- 인터페이스의 디폴트 메세지는 컴파일이 성공하더라도 기존에 존재하는 구현체에 런타임 오류가 발생할 수 있으므로, 꼭 필요한 경우가 아니면 피해야 한다.
7. 인터페이스는 타입을 정의하는 용도로만 사용하라
인터페이스는 타입을 정의하는 용도로만 쓰여야한다. → 인터페이스는 인터페이스 자신을 구현한, 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다.
상수 공개용으로 상수 인터페이스를 만들어 쓰면 안된다.
차라리 상수 유틸리티 클래스를 만들어서 사용하는 편이 낫다.
public class PhysicalConstants { private PhysicalConstants() { } //인스턴스화 방지 public static final double AVOGADROS_NUMBER = 6.022_140_857e23; public static final double ELECTRON_MASS = 9.109_383_56e-31; } /* 사용시: PhysicalConstants.ELECTRON_MASS static import할 경우: ELECTRON_MASS */
- 숫자가 너무 커지면 세자리 마다
_
를 표시해서 묶는게 가독성이 좋고, 리터럴 값에 아무런 영향을 주지 않는다.
- 숫자가 너무 커지면 세자리 마다
8. 태그 달린 클래스보다는 클래스 계층구조를 활용하라
- 태그 달린 클래스는 장황하고, 오류를 내기 쉽고, 비효율적이다.
→ 클래스 계층구조(추상클래스 + 구체클래스)로 풀어야 한다.
9. 멤버 클래스는 되도록 static으로 만들라
9.1 중첩 클래스
정적 멤버 클래스
- 다른 클래스 안에서 static으로 선언
- 바깥 클래스의 private 멤버 접근 가능
(비정적) 멤버 클래스
- 비정적 멤버 클래스의 인스턴스가 바깥 클래스의 인스턴스와 암묵적으로 연결 됨
- 정규화된
this
를 통해 접근 가능 ex)클래스명.this
- 정규화된
- 어댑터를 정의할 때 자주 사용
바깥 인스턴스의 클래스.new Member(args)
를 호출해서 수동적으로 생성 가능- 메모리가 많이 차지되고 느림 → 꼭 필요한게 하니면 정적 멤버 클래스로 선언
- 비정적 멤버 클래스의 인스턴스가 바깥 클래스의 인스턴스와 암묵적으로 연결 됨
익명 클래스
지역 클래스
10. 톱레벨 클래스는 한 파일에 하나만 담으라
- 한 파일에 여러 클래스가 있으면 매번 컴파일시에 동작이 달라진다.
※ 참고
- 이펙티브 자바 - 조슈아 블로크
'BOOK > [이펙티브 자바]' 카테고리의 다른 글
[Effective Java] Chapter7 람다와 스트림 (0) | 2022.03.17 |
---|---|
[Effective Java] Chapter6 열거 타입과 애너테이션 (0) | 2022.03.15 |
[Effective Java] Chapter5 제네릭 (0) | 2022.02.15 |
[Effective Java] Chapter3 모든 객체의 공통 메서드 (0) | 2022.02.08 |
[Effective Java] Chapter2 객체 생성과 파괴 (0) | 2022.02.03 |