nested class,interface
중첩 클래스와 중첩 인터페이스란?
-
중첩 클래스(Nested class) 란 클래스 내부에 선언한 클래스
- 중첩클래스 사용 시 두 클래스의 멤버들을 서로 쉽게 접근할수 있다는 장점과 외부에는 불필요한 관계 클래스를 감춤으로써 코드의 복잡성이 줄어듬.
class ClassName{
class NestedClassName{ // 중첩클래스
}
}
class ClassName{
interface NestedInterfaceName{ //중첩 인터페이스
}
}
- 인터페이스도 클래스 내부에 선언가능함. 이런 인터페이스를 중첩 인터페이스라고 함.
- 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해서이며, 중첩 인터페이스는 주로 UI 프로그래밍에서 이벤트 처리 목적으로 많이 활용 됨.
중첩 클래스
- 중첩 클래스는 클래스 내부에 선언되는 위치에 따라 두가지로 분류 됨.
- 클래스의 멤버로서 선언되는 중첩 클래스를 멤버 클래스, 메소드 내부에서 선언되는 중첩클래스를
로컬 클래스
- 멤버 클래스는 클래스나 객체가 사용중이라면 언제든지 재사용이 가능
- 로컬 클래스는 메소드 실행 시에만 사용되고 메소드 실행 종료 시 없어짐
선언위치에 따른 분류 | 분류 | 선언위치 | 설명 |
---|---|---|---|
멤버 클래스 | 인스턴스 멤버 클래스 | class A{ class B {…} } | A객체를 생성해야만 사용할 수 있는 B중첩 클래스 |
멤버 클래스 | 정적 멤버 클래스 | class A { static class B {…} } | A 클래스로 바로 접근할 수 있는 B중첩 클래스 |
로컬 클래스 | class A{ void method() { class B{.. } } } | method()가 실행할 때만 사용할 수 있는 B중첩 클래스 |
- 멤버클래스도 하나의 클래스이기때문에 컴파일 시 바이트 코드 파일(.class) 이 별도로 생성 됨.
- A(바깥클래스) $ B(멤버클래스).class
- 로컬클래스의 경우 다음과 같이 생성됨.
- A(바깥클래스) $1 B(로컬클래스).class
인스턴스 멤버 클래스
- 인스턴스 멤버 클래스는 static 키워드 없이 선언된 클래스
- 인스턴스 멤버 클래스는 인스턴스 필드와 메소드만 선언 가능하고, 정적 필드와 메소드는 선언이 불가함
class A{
// 인스턴스 멤버 클래스
class B{
B(){} //생성자
int field1; //인스턴스 필드
void method1(){} //인스턴스 메소드
}
}
- A클래스 외부에서 인스턴스 멤버 클래스 B의 객체를 생성하려면 먼저 A객체 생성 후 B객체를 생성해야함.
A a = new A();
A.B b = a.new B();
b.field1 =3;
b.method();
정적 멤버 클래스
- 정적 멤버 클래스는 static 키워드로 선언된 클래스
- 정적 멤버 클래스는 모든 종류의 필드와 메소드를 선언 가능
class A{ static class C { c() {} //생성자 int field1; //인스턴스 필드 static int field //정적 필드 void method1(){} //인스턴스 메소드 static void method2(){ } // 정적 메소드 } }
- A 클래스 외부에서 정적 멤버 클래스 C 객체를 생성하기 위해서는 A객체를 생성할 필요가 없고, 다음과 같이 C객체를 생성
A.C c = new A.C();
c.field1 = 3; //인스턴스 필드 사용
c.method1(); //인스턴스 메소드 호출
A.C.field2 = 3; //정적 필드 사용
A.C.method1(); //정적 메소드 호출
로컬 클래스
- 중첩 클래스는 메소드 내에서도 선언 가능함. 이것을 로컬(local) 클래스라고 함
- 로컬 클래스는 접근제한자 및 static 을 붙일수 없음.
- 메소드 내부에서만 사용되므로 접근을 제한할 필요가 없음. 로컬 클래스 내부에는 인스턴스 필드와 메소드만 선언이 가능하고, 정적 필드와 메소드는 선언 불가
void method(){ //로컬 클래스 class D{ D(){} //생성자 int field1; //인스턴스 필드 void method1(){} } D d = new D(); d.field1 =3; d.method1(); }
- 로컬 클래스는 메소드가 실행될 때 메소드 내에서 객체를 생성하고 사용해야 함. (주로 비동기 처리를 위해 스레드 객체 만들때 사용함)
- 메소드 내부에서만 사용되므로 접근을 제한할 필요가 없음. 로컬 클래스 내부에는 인스턴스 필드와 메소드만 선언이 가능하고, 정적 필드와 메소드는 선언 불가
중첩 클래스의 접근 제한
바깥 필드와 메소드에서 사용 제한
- 멤버 클래스가 인스턴스 또는 정적으로 선언됨에 따라 바깥 클래스의 필드와 메소드에 사용 제한이 생김
- 인스턴스 멤버 클래스는 바깥 클래스의 인스턴스 필드의 초기값이나 인스턴스 메소드에서 객체를 생성할수 있으나, 정적 필드의 초기값이나 정적 메소드에서는 객체를 생성할 수 없다. 반면 정적 멤버 클래스는 모든 필드의 초기값이나 모든 메소드에서 객체를 생성할 수 있다.
멤버 클래스에서 사용 제한
- 멤버 클래스가 인스턴스 또는 정적으로 선언됨에 따라 멤버클래스 내부에서 바깥클래스의 필드와 메소드를 접근할때에도 제한이 따름
- 인스턴스 멤버 클래스 안에서는 바깥 클래스의 모든 필드와 모든 메소드에 접근할 수 있지만, 정적 멤버 클래스 안에서는 바깥 클래스의 정적 필드와 메소드에만 접근 가능하다.
로컬 클래스에서 사용 제한
- 로컬 클래스에서 사용가능한 것은 final 로 선언된 매개변수와 로컬변수임.
- final 선언을 하지 않아도 여전히 값을 수정할 수 없는 final 의 특성을 가지고있음. final 키워드 존재 여부의 차이점은 로컬 클래스의 복사 위치이다. final 키워드가 있다면 로컬 클래스의 메소드 내부에 복사 되지만, final 키워드가 없다면 로컬 클래스의 필드로 복사됨.
- 로컬 클래스에서 사용된 매개변수와 로컬 변수는 모두 final 특성을 가짐.
중첩클래스에서 바깥 클래스 참조 얻기
- 클래스 내부에서 this 는 객체 자신의 참조이나, 중첩 클래스에서 this 키워드는 바깥 클래스가 아닌 중첩 클래스의 객체 참조임.
- 따라서 중첩 클래스 내부에서 this.필드, this.메소드() 를 호출하면 중첩클래스의 메소드가 사용됨.
바깥클래스.this.필드;
바깥클래스.this.메소드();
- 중첩클래스 내부에서 바깥클래스의 객체 참조를 얻으려면 바깥클래스의 이름을 this 앞에 붙여주면 됨.
중첩 인터페이스
- 중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스를 말한다.
- 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해서임.
class A{ interface I { void method(); } }
- 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해서임.
익명 객체
- 익명(anonymous) 객체는 이름이 없는 객체를 뜻함.
- 익명 객체는 단독으로 생성 불가하고, 클래스를 상속하거나, 인터페이스를 구현해야만 생성할 수 있음.
- 익명객체는 필드의 초기값이나, 로컬 변수의 초기값, 매개변수의 매개값으로 주로 대입됨.
익명 자식 객체 생성
- 자식 클래스가 재사용되지 않고, 오로지 해당 필드와 변수의 초기값으로만 사용할 경우라면 익명 자식 객체를 생성해서 초기값으로 대입하는것이 좋은 방법
부모클래스 [필드|변수] = new 부모클래스(매개값, ... ){ //필드 //메소드 }; //실행문이므로 끝에는 세미콜론';'을 반드시 붙여야 함.
- 부모클래스(매개값, …){} 은 부모클래스를 상속해서 중괄호{}와 같이 자식 클래스를 선언하라는 뜻이고, new 연산자는 이렇게 선언된 자식 클래스를 객체로 생성한다. 부모클래스(매개값,…) 은 부모 생성자를 호출하는 코드로, 매개값은 부모 생성자의 매개변수에 맞게 입력하면 되고, 중괄호 {}내부에는 필드나 메소드를 선언하거나, 부모클래스의 메소드를 재정의(오버라이딩) 하는 내용이 , 일반 클래스와 차이점은 생성자를 선언할 수 없음.
class A{
Parent field = new Parent(){ // A클래스의 필드 선언
int childField;
void childMethod(){}
@Override // Parent메소드를 오버라이딩
void parentMethod(){}
};
}
- 메소드의 매개변수가 부모타입일 경우 메소드 호출 코드에서 익명 자식 객체를 생성해서 매개값으로 대입 가능
class A{ void method(Parent parent){} void method2(){ method1( new Parent(){ int childField; void childMethod(){} @Override void parentMethod(){} } ); } }
익명 자식 객체에 새롭게 정의된 필드와 메소드는 익명 자식 객체 내부에서만 사용되고, 외부에서는 필드와 메소드에 접근할 수 없다. 왜냐하면 익명 자식 객체는 부모타입 변수에 대입되므로 부모타입에 선언된 것만 사용할 수 있기 때문
익명 구현 객체 생성
인터페이스 [필드|변수] = new 인터페이스(){
// 인터페이스에 선언된 추상 메소드의 실체 메소드 선언
// 필드
// 메소드
};
인터페이스 (){} 는 인터페이스를 구현해서 중괄호 {}와 같이 클래스를 선언하라는 뜻이고, new 연산자는 선언된 클래스를 객체로 생성한다. 중괄호 {} 에는 인터페이스에 선언된 모든 추상 메소드들의 실체 메소드를 작성해야 함. 필드와 메소드를 선언할 수 있지만, 실체 메소드에서만 사용이 가능하고 외부에서는 사용하지 못한다.