- 중첩 클래스란 클래스 내부에 선언한 클래스를 말한다.
- 장점 : 중첩 클래스를 사용하면 두 클래스의 멤버들을 서로 쉽게 접근할 수 있다.
외부에는 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.
- 중첩 인터페이스란 클래스 내부에 선언한 인터페이스를 말한다. (해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해)
1. 중첩 클래스
- 두 가지로 나뉜다. 1)멤버 클래스(클래스의 멤버로서 선언되는 중첩 클래스), 로컬 클래스(메소드 내부에 선언되는 중첩 클래스)
멤버 클래스는 다시 인스턴스 멤버 클래스와 정적 멤버 클래스로 나뉜다.
- 멤버 클래스도 하나의 클래스이기 때문에 컴파일 하면 바이트 코드 파일(.class)이 별도로 생성된다. 파일 이름은 다음과 같다
A $ B .class
2. 인스턴스 멤버 클래스
- 인스턴스 멤버 클래스는 static 키워드 없이 선언된 클래스를 말한다.
- 인스턴스 멤버 클래스는 인스턴스 필드와 메소드만 선언이 가능하고 정적 필드와 메소드는 선언할 수 없다.
- A 클래스 외부에서 인스턴스 멤버 클래스 B의 객체를 생성하려면 먼저 A 객체를 생성하고 B 객체를 생성해야한다.
A a = new A();
A.B b = a.new B();
b.field1 = 3;
b.method();
3. 정적 멤버 클래스
- 정적 멤버 클래스는 static 키워드로 선언된 클래스를 말한다.
- 정적 멤버 클래스는 모든 종류의 필드와 메소드를 선언할 수 있다.
class A{
/*정적 멤버 클래스*/
static class C{
C(){ } --생성자
int field1; --인스턴스 필드
static int field2; --정적 필드
void method1(){ } --인스턴스 메소드
static void method2(){ } --정적 메소드
}
}
- A 클래스 외부에서 정적 멤버 클래스 C의 객체를 생성하기 위해서는 A 객체를 생성할 필요가 없고 다음과 같이 C객체를 생성하면된다.
A.C c = new C();
c.field1 = 3; //인스턴스 필드 사용
c.method1(); //인스턴스 메소드 호출
A.C.field2 = 3; // 정적 필드 사용
A.C.method2(); //정적 메소드 호출
4. 로컬 클래스
- 메소드 내에 선언한 클래스를 로컬 클래스 라고 한다.
- 로컬 클래스는 접근 제한자(public, private) 및 static 을 붙일 수 없다.
- 로컬 클래스는 인스턴스 필드와 메소드만 선언이 가능하고, 정적 필드와 메소드는 선언할 수 없다.
void method(){
/*로컬 클래스*/
class D{
D(){ } --생성자
int field; --인스턴스 필드
//static int field2; --정적 필드(x)
void method1() { } --인스턴스 메소드
//static void method2(){ ] --정적 메소드(x)
}
D d = new D();
d.field1 =3;
d.method1();
}
5. 중첩 클래스의 접근 제한
1) 바깥 필드와 메소드에서 사용 제한
- 인스턴스 멤버 클래스(B)는 바깥 클래스의 인스턴스 필드(field1)의 초기값이나 인스턴스 메소드(method1())에서 객체를 생성할 수 있으나 정적 필드(field3)의 초기값이나 정적 메소드(method2())에서는 객체를 생성할 수 없다.
- 반면 정적 멤버 클래스(C)는 모든 필드의 초기값이나 모든 메소드에서 객체를 생성할 수 있다.
public class A{
//인스턴스 멤버 클래스
class B {}
//정적 멤버 클래스
class C {}
//인스턴스 필드
B field1 = new B();
C field2 = new C();
//인스턴스 메소드
void method1(){
B var1 = new B();
C var 2 = new C();
}
//정적 필드 초기화
//static B field3 = new B(); (x)
static C field4 = new C();
//정적 메소드
static void method2(){
//B var1 = new B(); (x)
C var2 = new C();
}
2)멤버 클래스에서 사용 제한
- 인스턴스 멤버 클래스(B)안에서는 바깥 클래스의 모든 필드와 모든 메소드에 접근 가능하지만,
- 정적 멤버 클래스(C)안에서는 정적 필드와 정적 메소드에서만 접근할 수 있고, 인스턴스 필드와 메소드에는 접근할 수 없다.
3)중첩 클래스에서 바깥 클래스 참조 얻기
- 중첩 클래스 내부에서 this 키워드를 사용하면 바깥 클래스의 객체 참조가 아니라, 중첩 클래스의 객체 참조가 된다.
- 바깥 클래스의 객체 참조를 얻으려면 아래와 같이 사용한다.
바깥클래스.this.필드
바깥클래스.this.메소드();
6. 중첩 인터페이스
- 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해서이다.
- UI 프로그래밍에서 이벤트를 처리할 목적으로 많이 활용된다.
public class Button{
interface OnClickListener{
void onClick();
}
OnClickListener listener; // 인터페이스 타입 필드
void setOnClickListener(OnClickListener listener){
this.listener = listener; //매개변수의 다형성
}
void touch(){
listener.onClick(); //구현 객체의 onClick() 메소드 호출
}
}
-> Button을 클릭했을 때 이벤트를 처리하는 객체를 받고 싶다고 가정해보자.
-> 그렇다고 아무 객체나 받으면 안되고, Button내부에 선언된 인터페이스를 구현한 객체만 받아야한다면 위와 같이 Button클래스를 선언하면 된다.
public class CallListener implements Buttton.OnClickListener{
@Override
public void onClick(){
System.out.println("전화를 겁니다.");
}
}
public class MessageListener implements Button.OnClickListener{
@Override
public void onClick(){
System.out.println("메시지를 보냅니다.");
}
}
다음은 어떤 구현 객체를 생성하서 Button객체의 setOnClickListener() 메소드로 세팅하느냐에 따라서 Button의 touch() 메소드의 실행 결과가 달라진다.
public class ButtonExample{
public static void main(String[] args){
Button btn = new Button();
btn.setOnClickListener(new CallListener());
btn.touch();
btn.setOnClickListener(new MessageListener());
btn.touch();
}
}
-> "전화를 겁니다"
-> "메시지를 보냅니다"
7. 익명 객체
- 익명 객체는 이름이 없는 객체를 말한다.
- 단독으로 생성할 수 없고 클래스를 상속하거나 인터페이스를 구현해야지만 생성할 수 있다.
- 익명 객체는 필드의 초기값이나 로컬 변수의 초기값, 매개변수의 매개값으로 주로 대입된다.
- UI 이벤트 처리 객체나 스레드 객체를 간편하게 생성할 목적으로 익명 객체가 많이 활용된다.
1. 익명 자식 객체 생성
- 부모 타입으로 필드나 변수를 선언하고, 자식 객체를 초기값으로 대입할 경우를 생각해보면, 먼저 부모 클래스를 상속하는 자식 클래스를 선언하고, new 연산자로 자식 인스턴스를 부모 타입의 변수나 필드에 대입하는게 일반적이다.
class Child extends Parent{ } --자식 클래스 선언
class A{
Parent field = new Child(); --필드에 자식 객체를 대입
void method() {
Parent localVar = new Child(); -- 로컬 변수에 자식 객체를 대입
}
}
-그러나, 만약 자식 객체가 재사용되지 않고, 그냥 초기값으로만 사용할 경우라면 익명 자식 객체를 생성하는게 더 좋은 방법이다.
-주의할 점은 하나의 실행문이므로 반드시 끝에는 세미콜론(;)을 붙여야 한다.
부모클래스 필드/변수 = new 부모클래스(매개값...){
//필드
//메소드
};
-부모클래스(매개값 ...){} 은 부모 클래스를 상속해서 중괄호 {}와 같이 자식 클래스를 선언하라는 뜻이고, new 연산자는 이렇게 선언된 자식 클래스를 객체로 생성한다.
-부모클래스(매개값...)은 부모 생성자의 매개 변수에 맞게 입력하면 된다.
-중괄호{} 내부에는 필드나 메소드를 선언하거나 부모 클래스의 메소드를 재정의(오버라이딩)하는 내용이 온다.
-일반 클래스와의 차이점은 생성자를 선언할 수 없다는 것이다.
class A{
Parent field = new Parent(){
int childField;
void childMethod(){ }
@Override
void parentMethod(){} //Parent의 메소드를 오버라이딩
};
}
(메소드 내에서 로컬 변수를 선언할 때 초기값으로 익명 자식 객체를 생성 후 대입한 예)
class A{
void method(){
Parent localVar = new Parent(){
int childField;
void childMethod(){ }
@Override
void parentMethod(){ }
};
}
}
'JAVA > 이것이 자바다' 카테고리의 다른 글
이것이 자바다 Ch 10 예외 처리 (0) | 2022.10.08 |
---|---|
이것이 자바다 Ch 8 인터페이스 (0) | 2022.09.08 |
이것이 자바다 Ch 7 상속 (0) | 2022.09.08 |
이것이 자바다 ch 6 클래스 (0) | 2022.09.05 |
이것이 자바다 Ch 5 참조 타입 (0) | 2022.09.05 |