개발자 도전기
[JAVA] 중첩 선언(중첩 클래스, 중첩 인터페이스) 본문
중첩클래스
클래스 내부에 선언하는 클래스를 중첩클래스라 한다. 중첩 클래스가 여러 클래스와 관계를 맺는 것이 아닌 특정 클래스만 관계를 맺을 경우에는 해당 클래스의 중첩 클래스로 선언하는 것이 유지보수의 도움이 된다.
중첩 클래스의 장점
- 유지보수가 용이하다
- 클래스의 멤버를 쉽게 사용할 수 있다
- 외부에는 중첩 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다
클래스는 선언하는 위치에 따라 두 가지로 분류된다
분류 | 선언 위치 |
멤버 클래스 | 클래스 내부 |
로컬 클래스 | 클래스의 메소드 내부 |
중첩 클래스도 하나의 클래스이기 때문에 컴파일하면 바이트코드 파일(.class)이 별도로 생성된다
// A: 바깥 클래스 , B : 멤버 or 로컬 클래스
A $ B .class // 멤버 클래스일 경우
A $1 B .class // 로컬 클래스인 경우
인스턴스 멤버 클래스
외부 클래스의 멤버로 선언된 내부 클래스를 말한다.
public class A {
private class B {}
}
일반 클래스처럼 public, default, private 접근 제한자를 가진다. private 접근 제한자의 경우 외부 클래스 내부에서만 내부 클래스를 사용할 수 있다.
인스턴스 멤버 클래스는 주로 외부 클래스의 내부에서 사용되므로 private 접근 제한을 갖는 것이 일반적이다. 또, 내부 클래스의 객체는 내부 클래스의 내부 어디에서나 생성할 수는 없고, 인스턴스 필드값, 생성자, 인스턴스 메소드에서 생성할 수 있다. A 객체가 있어야 B 객체도 생성할 수 있기 때문이다
만약 외부 객체가 default 또는 public 접근 제한을 가지고 있다면 다음과 같이 외부에서 생성할 수 있다
A a = new A(); // 외부 객체 생성
A.B b = a.new B(); // 외부 객체 생성 후 내부 객체 생성
인스턴스 멤버 클래스 역시 일반 클래스처럼 멤버로 필드, 생성자, 메소드, 정적 필드, 정적 메소드를 가진다.
(정적 필드와 정적 메소드는 Java 17부터 허용)
정적 멤버 클래스
정적 멤버 클래스는 static 키워드와 함께 선언된 내부 클래스를 말한다.
public class A {
public static class B {}
}
정적 멤버 클래스는 외부 클래스의 객체가 없어도 생성할 수 있기 때문에 주로 public, private 접근 제한을 가진다. 또, 같은 이유 때문에 외부 클래스의 내부 어디든 객체를 생성할 수 있다.
외부에서 다음과 같이 정적 멤버 클래스의 객체를 생성할 수 있다.
A.B b = new A.B();
역시 일반 클래스처럼 멤버로 필드, 생성자, 메소드, 정적 필드, 정적 메소드를 가진다.
(정적 필드와 정적 메소드는 Java 17부터 허용)
로컬 클래스
생성자 또는 메소드 내부에서 선언된 클래스를 로컬 클래스라고 한다.
class A{
public A(){
class B {} // 로컬 클래스
B b = new B(); // 생성자가 실행될 동안에만 객체 생성 가능
}
public void method(){
class B {} // 로컬 클래스
B b = new B(); // 메소드가 실행될 동안에만 객체 생성 가능
}
}
로컬 클래스는 생성자와 메소드가 실행될 동안에만 객체를 생성할 수 있다.
멤버로 필드, 생성자, 메소드, 정적 필드, 정적 메소드를 가진다.
(정적 필드와 정적 메소드는 Java 17부터 허용)
final 과 effectively final
로컬 클래스에서 해당 클래스가 포함된 메소드의 지역 변수를 사용할 수 있다. 단, 지역 변수가 final 이거나 effectively final이어야만 가능하다. 또한 지역변수들은 final의 속성을 가지고 있기 때문에 로컬 클래스에서 읽기만 가능하고 수정은 불가능하다.
effectively final
- final 키워드가 사용되지 않았다.
- 초기화 후, 재할당되지 않았다.
- 전위 또는 후위 연산자가 사용되지 않았다.
다음과 같이 i의 값을 재할당하면 변수 i는 더이상 effectively final이 아니게 되어 로컬 클래스에서 사용 불가능하다
public class C05EffectivelyFinal {
void method(int param) { // 파라미터도 final or effectively final
int i = 3; // final or effectively final
// i = 5; 로컬 클래스에서 변수 i 사용 불가
class LocalClass {
void method() {
System.out.println(param);
System.out.println(i);
}
}
}
}
외부 클래스 멤버 접근
중첩 클래스는 외부 클래스의 멤버에 접근할 수 있지만 선언 방법에 따라 제약이 따른다
구분 | 바깥 클래스의 사용 가능한 멤버 |
인스턴스 멤버 클래스 | 바깥 클래스의 모든 필드와 메소드 |
정적 멤버 클래스 | 바깥 클래스의 정적 필드와 정적 메소드 |
정적 멤버 클래스는 바깥 객체가 없어도 사용 가능해야 하므로 바깥 클래스의 인스턴스 필드와 인스턴스 메소드는 사용하지 못한다.
public class A {
int field1;
void method1 () {}
static int field2;
static void method2(){}
// 인스턴스 멤버 클래스
class B {
void method() {
// 인스턴스 필드와 메소드 사용 가능
field1 = 10;
method1();
// 정적 필드와 메소드 사용 가능
field2 = 10;
method2();
}
}
// 정적 멤버 클래스
static class C {
void method() {
// 인스턴스 필드와 메소드 사용 불가능
// field1 = 10;
// method1();
// 정적 필드와 메소드 사용 가능
field2 = 10;
method2();
}
}
}
만일 중첩 클래스와 외부 클래스가 같은 이름의 필드와 메소드를 가지고 있다면 바깥클래스이름.this를 사용하여 바깥 클래스의 객체를 얻을 수 있다.
public class A {
String field = "A-field";
void method() {
System.out.println("A.method");
}
class B {
String field = "B-field";
void method() {
System.out.println("B.method");
}
void print() {
// B 객체의 필드와 메소드 사용
System.out.println(this.field);
this.method();
// A 객체의 필드와 메소드 사용
System.out.println(A.this.field);
A.this.method();
}
}
}
중첩 인터페이스
중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스를 말한다. 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 객체를 만들기 위해서이다. 안드로이드와 같은 UI 프로그램에서 이벤트를 처리할 목적으로 많이 활용된다.
class A{
[public | private] [static] interface B { }
}
다음은 중첩 인터페이스의 활용 예제이다
class Button {
public static interface ClickListener {
void onClick();
}
private ClickListener clickListener;
public void setClickListener(ClickListener clickListener) {
this.clickListener = clickListener;
}
public void click() {
clickListener.onClick();
}
}
public class ButtonExample {
public static void main(String[] args) {
Button btnOk = new Button();
class OkListener implements Button.ClickListener {
@Override
public void onClick() {
System.out.println("ok버튼을 클릭했습니다");
}
}
btnOk.setClickListener(new OkListener());
btnOk.click();
Button btnCancel = new Button();
class CancelListener implements Button.ClickListener {
@Override
public void onClick() {
System.out.println("Cancel 버튼을 클릭했습니다");
}
}
btnCancel.setClickListener(new CancelListener());
btnCancel.click();
}
}
함께 공부하면 좋은 내용
'개발공부 > JAVA' 카테고리의 다른 글
[JAVA] 람다식(Lambda Expressions) (0) | 2024.03.13 |
---|---|
[JAVA] 익명 객체 (0) | 2024.03.12 |
[JAVA] 인터페이스(interface) (0) | 2024.03.11 |
[JAVA] instanceof (0) | 2024.03.08 |
[JAVA] 상속 - 클래스와 메소드의 final 키워드 (0) | 2024.03.08 |