본문은 Effective Java를 읽고 간단하게 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.
선결론
- 중첩 클래스에는 네 가지가 있으며, 각각의 쓰임이 다르다
- 1)메서드 밖에서도 사용하거나 메서드 안에서 정의하기에 너무 길다면 멤버 클래스로 만든다
- 멤버 클래스의 1-1)인스턴스 각각이 바깥 인스턴스를 참조한다면 비정적으로, 1-2)그렇지 않다면(참조하지 않는다면) 정적으로 만든다
- 2)중첩 클래스가 한 메서드 안에서만 쓰이면서 그 인스턴스를 생성하는 지점이 단 한 곳이고,
- 2-1) 해당 타입으로 쓰기에 적합한 클래스나 인터페이스가 이미 있다면 익명 클래스로 만든다
- 2-2) 그렇지 않다면(없다면) 지역 클래스로 만든다
중첩 클래스
- 중첩 클래스(nested class)란 다른 클래스 안에 정의된 클래스를 말한다
- 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 바깥 클래스 외부에서 쓰인다면 톱레벨 클래스로 만들어야 한다
- 중첩 클래스의 종류는 네 가지로, 정적 멤버 클래스, (비정적) 멤버 클래스, 익명 클래스, 지역 클래스가 있다
- 멤버 클래스, 익명 클래스, 지역 클래스는 모두 내부 클래스(inner class)에 해당한다
정적 멤버 클래스
- 다른 클래스 안에 선언되고 바깥 클래스의 private 멤버에 접근할 수 있다는 점에서 일반 클래스와 차이
- 다른 정적 멤버와 똑같은 접근 규칙이 적용됨
- 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스로 쓰인다(아래 예시에서 확인할 수 있다)
Calculator 클래스
public class Calculator {
// 중첩된 enum 타입은 암시적으로 static으로 선언되므로 명시적으로 static 키워드를 붙이지 않아도 됨
// https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.9
public enum Operation {
PLUS, MINUS;
}
}
Calculator의 클라이언트
class Client {
public static void main(String[] args) {
Calculator.Operation operation = Calculator.Operation.PLUS; // 원하는 연산 참조
}
}
비정적 멤버 클래스
- 비정적 멤버 클래스의 인스턴스는 바깥 클래스의 인스턴스와 암묵적으로 연결
- 비정적 멤버 클래스의 인스턴스 메서드에서 정규화된 this를 사용해 바깥 인스턴스를 호출하거나 바깥 인스턴스의 참조를 가져올 수 있다
- 정규화된 this란 클래스명.this 형태로 바깥 클래스의 이름을 명시하는 용법을 말한다
- 바깥 클래스가 외부에서 사용되지 않더라도 비정적 멤버 클래스가 바깥 클래스의 인스턴스를 참조할 수 있기 때문에 메모리 누수가 발생할 수 있다
- 정적 멤버 클래스의 인스턴스는 바깥 클래스와 독립적으로 생성할 수 있는 반면, 비정적 멤버 클래스의 인스턴스는 바깥 클래스의 인스턴스를 생성한 후 해당 참조를 이용해 생성해줘야 한다
- 따라서 메모리 공간을 추가적으로 사용하며, 생성시간도 더 걸린다
- 따라서 개념상 중첩 클래스의 인스턴스가 바깥 인스턴스와 독립적으로 존재할 수 있다면 정적 멤버 클래스로 만들어야 한다
- 비정적 멤버 클래스는 어댑터를 정의할 때 자주 쓰인다
- 어댑터란 어떤 한 구현체를 특정 인터페이스에 맞게 감싸주는 중간객체이다
- 즉, 어떤 클래스의 인스턴스를 감싸 마치 다른 클래스의 인스턴스처럼 보이게 하는 뷰로 사용하는 것
비정적 멤버 클래스 Member의 인스턴스 메서드에서 바깥 클래스인 Calculator의 참조를 가져오는 예시
public class Calculator {
private String name;
private Integer price;
// ...
private class Member {
private String name;
private Integer price;
private Member() {
this.name = Calculator.this.name;
this.price = Calculator.this.price;
}
}
}
비정적 멤버 클래스가 어댑터로 쓰이는 경우 - HashMap
익명 클래스
- 익명 클래스에는 이름이 없다
- 멤버 클래스와 달리 바깥 클래스의 멤버가 아니며, 쓰이는 시점에 선언과 동시에 인스턴스가 만들어진다
- 코드의 어디서든 만들 수 있다
- 오직 비정적인 문맥에서 사용될 때만 바깥 클래스의 인스턴스를 참조할 수 있으며, 정적 문맥에서라도 상수 변수 이외의 정적 멤버는 가질 수 없다
- 다음과 같은 제약사항을 지닌다(제약이 많다)
- 선언한 지점에서만 인스턴스를 만들 수 있다
- instanceof 검사나 클래스의 이름이 필요한 작업은 수행할 수 없다
- 여러 인터페이스를 구현할 수 없고, 인터페이스를 구현하는 동시에 다른 클래스를 상속할 수 없다
- 익명 클래스를 사용하는 클라이언트는 그 익명 클래스가 상위 타입에서 상속한 멤버 외에는 호출할 수 없다
- 가독성이 떨어진다 → 짧게 작성해야 한다
지역 클래스
- 네 가지 중첩 클래스 중 가장 드물게 사용된다
- 지역변수를 선언할 수 있는 곳이면 실질적으로 어디서든 선언할 수 있고, 유효 범위도 지역변수와 동일하다
- 멤버 클래스와 공통점이 있다
- 멤버 클래스처럼 이름이 있고 반복해서 사용할 수 있다
- 익명 클래스와 공통점이 있다
- 익명 클래스처럼 비정적 문맥에서 사용될 때만 바깥 인스턴스를 참조할 수 있다
- 정적 멤버를 가질 수없다
- 가독성이 떨어진다
'책 > Effective Java' 카테고리의 다른 글
[이펙티브 자바] 아이템 26: 로 타입은 사용하지 마라 (0) | 2022.05.12 |
---|---|
[이펙티브 자바] 아이템 25: 톱레벨 클래스는 한 파일에 하나만 담으라 (0) | 2022.05.11 |
[이펙티브 자바] 아이템 23: 태그 달린 클래스보다는 클래스 계층구조를 활용하라 (0) | 2022.05.11 |
[이펙티브 자바] 아이템 22: 인터페이스는 타입을 정의하는 용도로만 사용하라 (0) | 2022.05.11 |
[이펙티브 자바] 아이템 21: 인터페이스는 구현하는 쪽을 생각해 설계하라 (0) | 2022.05.09 |