728x90
5.1 정적 중첩 타입
- 중첩 타입을 정의할 수 있다는 것은 다음의 두 가지 주요 용도를 지원할 수 있다는 것을 의미한다.
- 중첩 클래스와 중첩 인터페이스가 논리적으로 관련된 그룹에 구조화되고 같은 범위에 속하는 타입이 될 수 있게 해준다.
- 중첩 클래스가 논리적으로 연관된 객체를 간단하고도 효율적으로 연결할 수 있다.
- 중첩 타입은 이 타입을 선언한 타입의 일부분이 되며 이 둘은 서로의 멤버에 접근할 수 있는 관계.
- 정적일 경우에는 단순한 구조의 타입을 허용하는 반면, 아닐 경우에는 중첩 객체와 이를 감싸는 외부 객체와의 특별한 관계를 정의해야 한다.
- 외부 클래스나 인터페이스 내에 static 멤버로 선언된 중첩 클래스나 중첩 인터페이스는 최상위 클래스나 최상위 인터페이스처럼 동작한다.
- 중첩 타입의 이름은 "외부이름.중첩이름"으로 표현.
- 그래서 중첩 타입은 외부 타입에 접근할 수 있는 경우에만 접근 가능.
- 정적 중첩 타입은 외부 타입 내에 선언된 멤버이기 때문에 적당한 객체 참조만 있다면 private 멤버를 포함한 모든 멤버에 접근 가능.
- 정적 중첩 클래스는 형식이 단순하다. 이 클래스를 선언하기 위해서는 클래스 선언 앞에 static 제한자를 선언하기만 하면 된다.
- 인터페이스에서 중첩 클래스를 선언할 경우, 무조건 static이어야 하며 제한자는 선언 안 해도 됨.
- 정적 중첩 클래스는 최상위 클래스처럼 동작함. 다른 클래스를 확장할 수도 있으며 다른 인터페이스를 구현할 수도 있음. 그리고 다른 클래스가 이 클래스를 확장할 수도 있음.
public class BankAccount{
private long number;
private long balance;
public static class Permissions{
public boolean cnaDeposit, canWithdraw, canClose;
}
}
- Permissions 클래스를 BankAccount 클래스 내부에 정의해서 BankAccount 클래스의 멤버로 만든다.
- 아예 외부에서 Permissions에 접근하기 위해서는 완전한 이름을 사용해야 한다.
- BankAccount.Permissions perm = ~~
- 인터페이스 내부에 선언된 정적 중첩 클래스는 무조건 public이지만 정적 중첩 클래스를 클래스 내부에 선언하는 경우에는 접근 제한자를 원하는데로 선언 가능.
- 중첩 인터페이스는 항상 static이며, 협약에 따라 인터페이스 선언 시 생략 가능.
5.2 내부 클래스
- 비정적 중첩 클래스를 내부 클래스라 부르기도 한다. 비정적 클래스 멤버들은 클래스의 인스턴스와 연관된다.
- 내부 클래스의 선언은 한 가지 제약 사항을 제외하고는 최상위 클래스를 선언하는 것과 동일하다. 이 제약 사항은 내부 클래스가 static 멤버를 가질 수 없다는 것이다. 하지만 상수나 상수를 생성하는 표현식으로 초기화해야 하는 final static 필드는 선언 가능.
- 중첩 클래스는 외부 클래스의 private 필드와 메소드를 포함한 모든 멤버를 조건 없이 접근할 수 있다. 이는 반대도 마찬가지.
- 외부 객체에 대한 참조를 위해서는 외부 클래스 이름과 this를 함께 사용하면 된다. 이를 한정된 this라 한다. X.this.method()처럼 말이다.
- 중첩 단계는 한 단계 정도가 적당하다. 그 이상이 되면 가독성 및 사용성을 떨어뜨린다.
- 내부 클래스도 정적 중첩 클래스나 최상위 클래스처럼 다른 클래스를 확장할 수 있다.
class Host{
int x;
class Helper extends Unknown{
void increment(){ x++; }
}
}
- Unknown에도 x 필드가 있다고 한다면 내부 클래스의 x는 Host 것이 아니라 Unknown 것이 된다. 코드가 어떤 일을 하는지 헷갈리지 않게 하기 위해서는 이처럼 이름만 사용해서는 안 된다. 그래서 this.x나 Host.this.x처럼 참조하는 대상을 명시해야 함.
5.3 지역 내부 클래스
- 내부 클래스는 메소드 몸체, 생성자, 초기화 블록 등의 코드 블록 내에 선언할 수도 있음.
- 이를 지역 내부 클래스라고 함.
- 지역 내부 클래스는 클래스의 멤버가 아니며 단지 지역 변수처럼 블록 내에 있는 코드의 일부분이다.
- 이러한 내부 클래스는 이 클래스가 정의된 블록 외부에서는 절대 접근할 수 없다.
- 지역 내부 클래스는 외부에서 접근할 수 없으니 접근 제한자를 가질 수 없고, static으로 선언도 불가.
- 지역 내부 클래스는 범위 내에 정의된 지역 변수, 메소드 매개변수, 인스턴스 변수, 정적 변수와 같은 변수에 접근 가능. 변수 접근 시 유일한 제약은 지역 변수와 메소드 매개변수가 final로 선언되었을 때만 접근 가능하다는 것.
- 내부 클래스는 외부 클래스의 인스턴스와 항상 관련되어 있다.
5.4 익명 내부 클래스
- 지역 내부 클래스가 과분하다면 클래스를 확장하거나 인터페이스를 구현할 수 있는 익명 내부 클래스를 사용해야 한다. 이 클래스는 new 연산자와 함께 인스턴스화되는 동시에 정의된다.
public static Iterator<Object> walkThrough(final Object[] objs){
return new Iterator<Object>(){
private int pos = 0;
public boolean hasNext(){
return ( pos < objs.length);
}
}
}
- 익명 클래스는 new 표현식에서 표현식의 일부로 정의된다. new가 명시한 타입은 익명 클래스의 슈퍼 타입이다.
- 익명 클래스는 extends와 implements문을 명시할 수 없으며 어노테이션을 포함하여 어떤 제한자도 가질 수 없다.
- 익명 내부 클래스는 이름을 가질 수 없기에 생성자를 명시적으로 선언할 수 없다. 그래서 익명 클래스가 생성자를 명시해야 할 정도로 복잡해진다면 지역 내부 클래스를 사용해야 한다.
5.5 중첩 타입의 상속
- 중첩 타입이 정적 클래스든, 정적 인터페이스든, 내부 클래스든 이 모두는 필드가 상속되는 방식과 동일한 방식으로 상속된다.
- 팩토리 메소드는 내부 클래스와 동일한 종류의 객체를 생성하기 위해 서브 클래스에서 오버라이드할 수 있는 메소드이다.
public abstract class Device {
abstract class Port{
}
}
class Printer extends Device{
class SerialPort extends Port{
}
Port serial = createSerialPort();
protected Port createSerialPort(){
return new SerialPort();
}
}
class HighSpeedPrinter extends Printer{
class EnhancedSerialPort extends SerialPort{
// ...
}
protected Port createSerialPort(){
return new EnhancedSerialPort();
}
}
- HighSpeedPrinter 객체가 생성되고 Printer 클래스의 초기자가 실행되면 createSerialPort 오버라이드 메소드가 호출돼서 EnhancedSerialPort 인스턴스가 반환된다.
- 이 예제는 서브 클래스 객체가 완전히 생성되기 전에 호출되는 서브 클래스의 메소드를 보여준다.
5.6 인터페이스 내의 중첩
- 클래스 내에 중첩 클래스와 중첩 인터페이스를 선언할 수 있는 것처럼 인터페이스 내에서도 이들을 선언할 수 있다.
public interface Changeable {
class Record{
public Object changer;
public String changeDesc;
}
Record getLastChange();
}
- getLastChange 메소드는 Changeable.Record 객체를 반환하며 이 객체는 변경을 가한 객체와 변경한 내역을 포함한다.
- 인터페이스 내에서 중첩 클래스의 또 다른 사용법은 인터페이스에 대한 기본 구현을 정의하는 것이다.
- 인터페이스에서 공유되고 수정할 수 있는 데이터가 필요한 경우에는 중첩 클래스가 적당한 대안이다. 공유할 데이터를 저장할 필드와 이를 접근하기 위한 메소드를 중첩 클래스에 선언하고 이 클래스의 인스턴스를 참조하는 변수를 선언해야 한다.
public interface SharedData {
class Data{
private int x = 0;
public int getX() {return x;}
public void setX(int x) {
this.x = x;
}
}
Data data = new Data();
}
- SharedData의 구현자와 사용자는 data 참조로 공통된 상태를 공유할 수 있다.
5.7 중첩 타입 구현
- 정적이거나 비지역 중첩 타입으로 정의된 Outer.inner를 생각해보자. Outer.Inner는 소스 코드의 클래스 이름이다.
- 그래서 중첩 타입을 구현할 때는 두 가지 경우를 고려해야 함.
- 애플리케이션의 클래스 파일을 묶을 때 파일 이름에 $가 있는지 살펴야 함.
- 만약 중첩 클래스 인스턴스를 생성하기 위해 리플렉션 매커니즘을 사용하고 있다면 변형된 클래스 이름도 알아야 함.
728x90
'도서 > 자바 프로그래밍 언어 - James Gosling' 카테고리의 다른 글
[자바 프로그래밍 언어] 7장 토큰, 값, 변수 (0) | 2021.12.27 |
---|---|
[자바 프로그래밍 언어] 6장 열거 타입 (0) | 2021.12.26 |
[자바 프로그래밍 언어] 4장 인터페이스 (0) | 2021.12.24 |
[자바 프로그래밍 언어] 3장 클래스 확장 (0) | 2021.12.23 |
[자바 프로그래밍 언어] 2장 클래스와 객체 (0) | 2021.12.22 |
댓글