신용권 님의 ''이것이 자바다'' 9장 공부 기록
책을 보면서 내용을 정리했다.
중첩 클래스(Nested Class)란 클래스 내부에 선언한 클래스를 말하는데, 중첩 클래스를 사용하면 두 클래스의 멤버들을 서로 쉽게 접근할 수 있고, 외부에는 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.
//중첩 클래스 예시
class ClassName{
class NestedClassName{
}
}
//중첩 인터페이스 예시
class ClassName{
interface NestedInterfaceName{
}
}
중첩 클래스는 클래스 내부에 선언되는 위치에 따라서 두 가지로 분류된다. 클래스의 멤버로 선언되는 중첩 클래스를 멤버 클래스라고 하고, 메소드 내부에서 선언되는 중첩 클래스를 로컬 클래스라고 한다. 멤버 클래스는 클래스나 객체가 사용 중이라면 언제든지 재사용이 가능하지만, 로컬 클래스는 메소드 실행 시에만 사용되고, 메소드가 실행 종료되면 없어진다.
멤버 클래스
인스턴스 멤버 클래스
class A {
class B{...}
}
A a = new A();
A.B b = a.new B(); //사용 가능
정적 멤버 클래스
class A{
static class C{...}
} //A 클래스로 바로 접근할 수 있는 C중첩 클래스
A.C c = new A.C(); //사용 가능
로컬 클래스
class A{
void method(){
class B{...}
}
} //method()가 실행될 때만 사용할 수 있는 B중첩
A a = new A();
a.method() // 로컬 클래스 객체 생성을 위한 메소드 호출
public class A{
//인스턴스 필드
B field1 = new B();
C field2 = new C();
//인스턴스 메소드
void method1(){
B var1 = new B();
C var2 = new C();
}
//정적 필드 초기화
//static B field3 = new B(); 인스턴스 멤버 클래스는 안됨
static C field3 = new C()
//정적 메소드
static void method2(){
//B var1 = new B(); 안됨
C var2 = new C();
}
//인스턴스 멤버 클래스
class B{}
//정적 멤버 클래스
static class C{}
}
public class A{
//인스턴스 멤버
int field1;
void method1(){};
//정적 멤버
static int field2;
static void method2();
//정적 메소드
static void method2(){
//B var1 = new B(); 안됨
C var2 = new C();
}
//인스턴스 멤버 클래스
class B{
field1=10;
method1();
field2=10;
method2(); // 모든 필드와 메소드에 접근할 수 있다.
}
//정적 멤버 클래스
static class C{
//field1=10;
//method1(); 인스턴스 필드와 메소드에는 접근할 수 없다.
field2=10;
method2();}
}
중첩 클래스에서 this키워드를 사용하면 중첩 클래스 자신의 객체를 참조하게 된다.
중첩 클래스 내부에서 바깥 클래스 참조를 얻으려면 다음의 방법을 사용한다.
바깥클래스.this.필드
바깥클래스.this.메소드();
인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 클래스를 만들기 위해서이다. 특히 UI 프로그래밍에서 이벤트를 처리할 목적으로 많이 활용된다.
publi class Button{
onClickListener listner; // 인터페이스 필드
void setOnClickListener(OnClickListener listener){
this.listener = listener;
} //setter 메소드, 매개 변수의 다형성 보여줌
void touch(){
listener.onClick();
} //구현 객체의 onClick() 메소드 호출
interface OnClickListener{
void onClick(); //중첩 인터페이스
}
}
//구현 클래스
public class CallListener implements Button.OnClickListener{
@Override
public void onClick(){
System.out.println("전화를 겁니다");
}
}
//버튼 이벤트 처리
public class ButtonEx{
public static void main(String[] args){
Button btn = new Button();
btn.setOnClickListener(new CallListener());
btn.touch();
}
}
익명 객체는 이름이 없는 객체를 말한다.
익명 객체는 단독으로 생성할 수 없고 클래스를 상속하거나 인터페이스를 구현해야만 생성할 수 있다.
익명 객체는 필드의 초기값이나 로컬 변수의 초기값, 매개 변수의 매개값으로 주로 대입된다.
UI 이벤트 처리 객체나 스레드 객체를 간편하게 생성할 목적으로 익명 객체가 많이 활용된다.
class Child extends Parent {}
class A {
Parent field = new Child(); // 필드에 자식 객체를 대입
void method(){
Parent localVar = new Child(); // 로컬 변수에 자식 객체를 대입
}
}
부모클래스[필드:변수] = new 부모클래스(매개값, ...){
//필드
//메소드
}; //세미콜론 붙여야함
중괄호 내부에는 필드나 메소드를 선언하거나 부모 클래스의 메소드를 재정의하는 내용이 들어가는데, 일반 클래스와는 달리 생성자를 선언할 수 없다.
필드를 선언할 때 사용하는 법
class A {
Parent field = new Parent() {
int childField;
void childMethod(){}
@Override //parent 메소드 재정의
void parentMethod(){}
};
}
class A {
void method(){
Parent localVar = new Parent(){
int childField;
void childMethod(){}
@Override
void parentMethod(){}
};
}
}
class A {
void method1(Parent parent){}
void method2(){
method1( //method1을 호출하면서, 매개값으로 익명 객체를 사용했다.
new Parent(){
int childField;
void childMethod(){}
@Override
void parentMethod(){}
};
)
}
}
class A {
Parent field = new Parent(){
int childField;
void childMethod(){}
@Override
void parentMethod(){
childField =3;
childMethod();
}
};
void method(){
//field.childField =3;
//field.childMethod(); //부모 타입 내용이므로 사용할 수 없다.
field.parentMethod();// 재정의했기 때문에 사용가능
}
}
class TV implements RemoteControl{}
class A{
RemoteControl field = new TV();// 인터페이스에 필요한 구현 객체를 생성하여 대입
void method(){
RemoteControl localVar = new TV(); // 로컬 변수에 구현 객체를 대입
}
}
인터페이스 [필드:변수] = new 인터페이스() {
//인터페이스에 선언된 추상 메소드의 실체 메소드 선언
//필드
//메소드
}; //중괄호에는 인터페이스에 선언된 모든 추상 메소드들의 실체 메소드를 작성해야 한다.
class A{
RemoteControl field = new RemoteControl(){
@Override
void turnOn{} //추상메소드에 대한 실체 메소드
}
}
void method(){
RemoteControl localVar = new RemoteControl(){
@Override
void turnOn(){} //추상 메소드에 대한 실체 메소드
};
}
class A {
void method1(RemoteControl rc){}
void method2(){
method1(
new RemoteControl(){
@Override
void turnOn(){}
}
); //메소드 안에 익명 구현 객체가 선언되면 해당 메소드의 )에 ;를 쓴다.
}
}
//UI 클래스
public class Button{
onClickListener listener; //인터페이스 타입 필드
void setOnClickListener(OnClickListener listener){
this.listener = listener; //매개 변수의 다형성
}
void touch(){ // 구현 객체의 onclick 메소드 호출
listener.onClick();
}
interface OnClickListener { //중첩 인터페이스
void onClick();
}
}
public class Window {
Button button1 = new Button();
Button button2 = new Button();
//필드 초기값으로 대입
Button.OnClickListener listener = new Button.OnClickListener() {
@Override
public void onClick(){
System.out.println("전화를 겁니다.");
}
};
Window(){
button1.setOnClickListener(lisetener); //매개값으로 위 필드 대입
button2.setOnClickListener(new Button.OnClickListener() {//매개값으로 익명 구현 객체 대입
@Override
public void onClick(){
System.out.println("메시지를 보냅니다.");
}
});
}
}
//실행 클래스
public class Main{
public static void main(String[]args){
Window w = new Window();
w.button1.touch();
w.button2.touch();
}
}
public interface Calculatable{
public int sum();
}
public class Anonymous{
private int field;
public void method(final int arg1, int arg2){
final int var1=0;
int var2 =0;
field =10; //바깥 클래스 내용은 제한 없이 사용
//arg1 =10;
//arg2 =10;
//var1=10;
//var2=10; final이므로 수정 불가
Calculatable calc = new Calculatable(){
@Override
publi cint sum(){
int result = field + arg1 +arg2 + var1 + var2;
return result;
}
};
System.out.println(calc.sum);
}
}
public class AnonymousEx{
public static void main(String[]args){
Anonymous anony = new Anonymous();
anony.method(0,0);
}
} //실행 결과 : 10