9장 중첩클래스와 익명객체
중첩? 클래스안에 클래스 클래스안에 인터페이스 선언이 있을 수 있다.
익명개체 이름이 없는 객체
9.1 중첩 클래스
중첩클래스는 클래스 선언안에 도 다른 클래스 선언 을 말한다.
class A{class B{}} 등 사진참조
멤버클래스 클래스 블록에 선언함. 필드 생성자 메소드 처럼
인스턴스 멤버클래스 : a객체가 잇어야 b사용가능 정적 멤버클래스 a가없어도 b를 생성가능
로컬클래스 매소드나생성자 안에 있는데 이 메소드를 실행할때만 존재하는 클래스임.
멤버클래스를 컴파일하면 A $ B.class로 나옴. 바깥클래스 $ 멤버클래스
로컬클래스면 A $1 B .class
9.2 인스턴스 멤버 클래스
인스턴스 멤버클래스는 다음과 같이 A클래스의 멤버로 선언된 B클래스를 말한다.
접근제한자에 따른 인스턴스 멤버 클래스의 접근 범위는 다음과 같다.
public class B{} 다른패키지에서 B클래스를 사용할 수 있다.
class B{}(default) 같은 패키지에서만 B클래스를 사용할 수 있다.
private class B{} A클래스 내부에서만 B클래스를 사용할 수 있다.
B객체를 A크래스 외부에 생성하려면 default 또는 public 접근 제한을 가져야 하고
a객체를 생성한다음 b객체를 생성해야한다.
A a = new A();
A.B b = a.new B();
인스턴스멤버 클래스B또한 클래스이기때문에 내부에 필드 생성자 메소드 선언이 올 수 있다.
public class A {
//필드
//생성자
//인스턴스 멤버클래스
class B{
//인스턴스 필드
int field1 = 1;
//정적필드(java 17부터 허용)
static int field2 = 2;
//생성자
B() {
System.out.println("B-생성자 실행");
}
//인스턴스 메소드
void method1(){
System.out.println("B-method1 실행");
}
//정적메소드(java 17부터 허용)
static void method2() {
System.out.println("B-method2 실행");
}
}
//메소드
// 인스턴스 메소드
void useB() {
B b = new B();
System.out.println(b.field1);
b.method1();
//B클래스의 정적 필드 및 메소드 사용
System.out.println(B.field2);
B.method2();
}}public class Aexample {
public static void main(String[] args) {
// A객체 생성
A a = new A();
System.out.println(A.B.field2);
A.B.method2();
//A인스턴스 메소드 호출
a.useB();
}}9.3 정적 멤버클래스
정적멤버클래스는 다음과 같이 static 키워드와 함께 a클래스의 멤버로 선언된 b클래스를 의미한다.
[public] class A{[public|private] static class B{}}
public static class B{} 다른 패키지에서 B클래스를 사용할 수 있다.
static class B{} 같은 패키지에서만 B클래스를 사용할 수 있다.
private static class B{} A클래스 내부에서만 B클래스를 사용할 수 있다.
정적멤버 클래스b는 a클래스 내부에서 사용되기도 하지만 a클래스 외부에서 a화 함께 사용되는 경우가 많ㅇ아 주로 default또는 public접근제한을 가진다.
B객체는 A클래스 내부어디든 객체를 생성할 수 있다.
A객체와는 상관이없지만 A클래스내부에서 너무 밀접한 관련이 있디면 정적 B를 만들어야한다.
public class A {
//정적 멤버 클래스
static class B {}
B field1 = new B();
static B field2 = new B();
//생성자
A() {
B b = new B();
}
void method1() {
B b = new B();
}
//정적 메소드(Java 17부터 허용)
static void method2() {
B b = new B();
}}public class AExample {
public static void main(String[] args) {
// B객체 생성 및 인스턴스 필드 및 메소드 사용
A.B b = new A.B();
}}9.4 로컬 클래스
생성자 또는 메소드 내부에서 선언된 클래스를 로컬 클래스라고 한다.
[public] class A{
// 생성자
public A(){
class B{}
}
//메소드
public void method(){
class B {}
} 로컬 클래스는 생성자와 메소드가 실행될 동안에만 객체를 생성할 수 있다.
로컬변수(생성자 또는 메소드의 매개변수또는 내부에서 선언된 변수)를 로컬클래스에서 사용할 경우 로컬변수는 final특성을 갖게 되므로 값을 읽을수만있고 수정할수 없게됨
이것은 로컬 클래스 내부에서 값을 변경하지 못하도록 제한하기 때문이다.
public class A {
//메소드
public void method1(int arg) { //final int arg
//로컬변수
int var = 1; //final int var = 1;
//로컬 클래스
class B{
//메소드
void method2() {
//로컬변수 읽기
System.out.println("arg: " + arg);
System.out.println("var: " + var);
}}
//로컬변수 수정
// arg = 2; arg가 로컬클래스에서 사용되고 final특성을 가져서 못 바꿈
// var = 2; var이 로컬클래스에서 사용되고 final특성을 가져서 못 바꿈
//로컬 객체 생성
B b = new B();
//로컬 객체 메소드 호출
b.method2();
//로컬변수 수정
//arg = 3; 불가능
//var = 3; 불가능
}}public class Aexample {
public static void main(String[] args) {
// A객체 생성
A a = new A();
//A 메소드 호출
a.method1(3); //final이라 3으로 값고정
}}9.5 바깥 멤버 접근
중첩클래스는 바깥 클래스와 긴밀한 관계를 맺으면서 바깥 클래스의 멤버(필드, 메소드)에 접글할 수 있다.
하지만 중첩클래스가 어떻게 선언되엇느냐에 따라 접근 제한이 잇을수있다.
9.5.1 바깥 클래스의 멤버 접근제한
정적멤버 클래스 내부에서는 바깥 크래스의 필드와 메소드를 사용할때 제한이 따른다.
인스턴스 멤버클래스 바깥 클래스의 모든 필드와 메소드
정적멤버 클래스 바깥클래스의 정적필드와 정적메소드
public class A {
//A의 인스턴스 필드와 메소드
int field1;
void method1() {}
//A의 정적필드와 메소드
static int field2;
static void method2() {}
//인스턴스 멤버 클래스
class B {
void method() {
//A의 인스턴스 필드와 메소드 사용
field1 = 10;
method1();
//A의 정적필드와 메소드 사용
field2 = 10;
method2();
}
}
//정적멤버 클래스
static class C {
void method() {
//A의 인스턴스 필드와 메소드 사용
//field1 = 10; 불가능
// method1(); 불가능
//A의 정적필드와 메소드 사용
field2 = 10; // 가능
method2(); // 가능
}}}9.5.2 바깥클래스의 객체접근
중요!!
중첩클래스 내부에서 this는 해당 중첩 클래스의 객체를 말한다.
만약 중첩클래스 내부에서 바깥 클래스 객체를 얻으려면 바깥클래스이름에 this 를 붙여주면된다.
바깥클래스이름.this -> 바깥객체
중첩클래스에서 메소드를 사용하는데 바깥클래스의 메소드와 중첩클래스의 메소드가 이름이 같을때 이런 일이 생길 수 있다.
public class A {
//A의 인스턴스 필드
String field = "A-field";
//A의 인스턴스 메소드
void method() {
System.out.println("A-method");
}
//인스턴스 멤버 클래스
class B{
//B 인스턴스 필드
String field = "B-field";
//B 인스턴스 메소드
void method() {
System.out.println("B-method");
}
//B인스턴스 메소드
void print() {
//B객체의 필드와 메소드 사용
System.out.println(this.field);
this.method(); //그냥 method();도 가능
//A객체의 필드와 메소드 사용
System.out.println(A.this.field);
A.this.method();
}}
//A의 인스턴스 메소드
void useB() {
B b = new B();
b.print();
}}public class AExample {
public static void main(String[] args) {
// A객체생성
A a = new A();
//A 메소드 호출
a.useB();
}}9.6 중첩 인터페이스
중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스를 말한다.
인터페이스를 클래스 내부 선언하는 이유는 해당클래스와 긴밀한 관계를 맺는 구현 객체를 만들기 위해서다.
class A{
[public|private] [static] interface B{
//상수필드 추상메소드 디폴트메소드 정적메소드
}외부의 접근을 막지 않으려면 public을 붙이고 a클래스 내부에서만 사용하려면 private를 붙인다.
접근 제한자를 붙이지 않으면 같은 패키지 안에서만 접근이 가능하다.
그리고 a객체 없이 b인터페이스를 사용할 수 잇도록 하기 위해 static을 추가할 수 잇다.
중첩인터페이스는 안드로이드와 같은 ui 프로그램에서 이벤트를 처리할 으로 많이 활용된다.
예를들어 버튼을 클릭햇을때 이벤트를 처리할 중첩 인터페이스를 구현해서 만든다.
버튼을 클릭햇을때 ClickListener가 사용되는 것이므로 벗어나면 아무쓸모가없다.
그래서 중첩클래스든 인터페이스든 바깥클래스와 매우 긴밀하고 이외는 관계가 아예없을 경우에 사용한다.
외부에서 접근이 가능하도록 public 이면서 Button객체 없이 사용할수있는 static 중첩인터페이스로
ClickListener를 선언하였다. 그리고 추상메소드인 onClick()을 선언했다.
onClick메소드는 버튼이 클릭되엇을때 호츨될 메소드이다.
Button 클래스에 ClkickListner 타입의 필드와 setter를 추가해서 외부에서 setter를 통해
ClkickListner 구현 객체를 필드에 저장할수있도록 했다.
버튼이 클릭되엇을때 실행할 메소드로 click ()를 다음과 같이 추가한다. 실행내용은 clickListener 인터페이스
버튼을 이용해서 onClick()추상메소드를 호출한다.
public class Button {
//정적 멤버 인터페이스
public static interface ClickListener{
void onClick();
}
//필드
private ClickListener clickListener;
//메소드
public void setClickLisntener(ClickListener clickListener) {
this.clickListener = clickListener;
}
public void click() {
this.clickListener.onClick();
}}public class ButtonExample {
public static void main(String[] args) {
//Ok버튼 객체 생성
Button btnOk = new Button();
//Ok버튼 클릭 이벤트를 처리할 ClickListner 구현 클래스(로컬 클래스)
class OkListener implements Button.ClickListener //Button클래스 안의 인터페이스 참조
{
@Override
public void onClick() {
System.out.println("Ok 버튼을 클릭했습니다.");
}
}
//Ok 버튼 객체에 ClickLisnter구현객체 주입
//ClickListner clickListener이게 매개변수이니가
//ClickListner clickListener = new OkListener(); 구현한 구현객체를 넣어줄수잇다. 자동타입변환으로
btnOk.setClickLisntener(new OkListener());
//Ok버튼 클릭하기
btnOk.click();
//clickListener.onClick(); OkListener로 재정의된 onClick실행되는거임.
//--------------
//Cancel 버튼객체 생성
Button btnCancel = new Button();
//Cancel 버튼 클릭 이벤트를 처리할 ClickListener 구현 클래스(로컬클래스)
class CancelListener implements Button.ClickListener{
@Override
public void onClick() {
System.out.println("Cancel 버튼을 클릭했습니다.");
}
}
//Cancel 버튼 객체에 ClickListener 구현 객체 주입
btnCancel.setClickLisntener(new CancelListener());
//Cancel 버튼 클릭하기
btnCancel.click();
}}9.7익명객체
이걸 더 많이 씀 상속과도 관련잇고 구현과도 관련이잇다.
이름자체가 없는데 객체를 만든다.
빨리 객체만 만들어서 쓰고 싶을때 사용
익명객체는 이름이 없는 객체를 말한다. 명시적으로 클래스를 선언하지 않기때문에 쉽게 객체를 생성할수잇다.
익명 객체는 필드값, 로컬변수값, 매개변수값으로 주로 사용된다.
익명 객체는 클래스를 상속하거나 인터페이스를 구현해야만 생성할 수 있다. <<<< 중요
클래스를 상속해서 만들 경우 익명자식 객체라고 하고 인터페이스를 구현해서 만들경우 익명 구현객체라고 한다.
익명자식객체냐 구현객체냐는 코드를 보면 알 수있다.
보통 new하고 생성자인데 new하고 {}가 있음. 선언하겠다는 뜻=클래스의 선언부와 같으나 아님.
부모 생성자를 호출하며 {} 자식을 만든다음에 객체를 만든다.
9.7.1 익명 자식 객체
익명 자식객체는 부모 클래스를 상속받아 생성된다. 이렇게 생성된 객체는 부모 타입의 필드 로컬볍ㄴ수 매개변수값으로 대입할 수잇따.
new 부모생성자(매개값,...){
//필드
//메소드
}중괄호 블록안의 필드와 메소드는 익명자식객체가 가져야할 멤버로 중괄호 블록 안에서만 사용할 수 있다.
익명 자식객체는 부모 타입에 대입되므로 부모 타입에 선언된 멤버만 접근할 수 잇기때문이다.
중괄호 블록안에는 주로 부모 메소드를 재정의 하는 코드가 온다.
다음 예제는 Tire클래스의 익명자식 객체를 생성해서 필드 ,로컬변수, 매개변수의 값으로 사용하는 방법을 보여준다.
Tire 클래스는 roll()메소드를 가지고 있지만 익명자식 객체는 roll()을 재정의 해 실행내용을 변경한다.
public class Tire {
public void roll() {
System.out.println("일반 타이어가 굴러갑니다.");
}} public class Car {
//필드에 Tire 객체 대입
private Tire tire1 = new Tire();
//필드에 익명자식객체 대입
//생성자다음 중괄호 Tire가 부모가되고 상속한걸 만들겟다. Tire의 익명자식객체를대입하겟다
//Tire tire2 = new Tire() {익명자식객체 내용}
private Tire tire2 = new Tire() {
@Override
public void roll() {
System.out.println("익명 자식 Tire 객체 1이 굴러갑니다.");
}
};
//메소드(필드이름)
public void run1() {
tire1.roll(); //Tire 객체의 roll()실행
tire2.roll(); //Tire 객쳉의 익명자시객체의 roll()실행
}
//메소드(로컬변수 이름) //메소드안에 변수 선언한거임.
public void run2() {
Tire tire = new Tire() {
@Override
public void roll() {
System.out.println("익명 자식 Tire 객체 2이 굴러갑니다.");
}
};
tire.roll();
}
//메소드(매개변수 이용)
public void run3(Tire tire) //Tire와도되고 자식객체가 와도됨.
{
tire.roll();
}}public class CarExample {
public static void main(String[] args) {
// Car 객체 생성
Car car = new Car();
//익명자식 객체가 대입된 필드 사용
car.run1();
//익명자식 객체가 대입된 로컬변수사용
car.run2();
//익명자식 객체가 대입된 매개변수 사용
//public void run3(Tire tire)인데 여기서 자식객체도 올수있음을 우리는 알고있음.
//Tire tire = new 자식객체(); 자동타입변환때문에
//Tire tire자식객체가 올수있음. 근데 그냥 바로 익명자식객체를 넣어준거임.
car.run3(new Tire() {
@Override
public void roll()
{
System.out.println("익명 자식 Tire 객체 3이 굴러갑니다.");
}
});
//car.run3(); ();안에다가 new Tire(){}를 넣은거임 그래서 마지막에 );닫혀있는거임 잘확인하자.
}}
9.7.2 익명 구현객체
new 인터페이스() { } 인터페이스를 구현할 클래스를 만드는데 이름이 없고 그 내용은 {}안에 잇는거임.
익명이란 상속이나 구현을 해야만 만들어진다.
new 인터페이스() {
//필드
//메소드
}익명구현객체는 안드로이드와 같은 UI프로그램에서 이벤트를 처리하는 객체로 많이 사용된다.
중괄호 블록안의 필드와 메소드는 익명 구현객체가 가져야할 멤버로 중괄호블록안에서만 사용할수있다.
익명구현객체는 인터페이스 타입에 대입되므로 인터페이스 타입에 선언된 멤버만 접근할수있기때문잉다.
중괄호블록안에는 주로 인터페이스의 추상메소드를 재정의 하는 코드가 온다.
public interface RemoteControl {
//추상메소드
void turnOn();
void turnOff();
} public class Home {
//필드선언하면서 익명 구현객체 대입
private RemoteControl rc = new RemoteControl() {
@Override
public void turnOn() {
System.out.println("TV를 켭니다.");
}
@Override
public void turnOff() {
System.out.println("TV를 끕니다.");
}
};
//메소드(필드이용)
public void use1() {
rc.turnOn();
rc.turnOff();
}
//메소드(로컬변수 이용)
public void use2() {
//로컬변수에 익명구현객체 대입
RemoteControl rc = new RemoteControl() {
@Override
public void turnOn() {
System.out.println("에이컨를 켭니다.");
}
@Override
public void turnOff() {
System.out.println("에어컨을 끕니다.");
}
};
rc.turnOn();
rc.turnOff();
}
//메소드(매개변수 이용)
public void use3(RemoteControl rc) {
rc.turnOn();
rc.turnOff();
}} public class HomeExample {
public static void main(String[] args) {
// Home객체 생성
Home home = new Home();
//익명구현객체가 대입된 필드가 사용된 메소드 사용
home.use1();
//익명구현객체가 로컬변수로 대입된 메소드 사용
home.use2();
//익명구현객체가 대입된 매개변수 사용
//home.use3(new RemoteControl() {구현객체});
home.use3(new RemoteControl() {
@Override
public void turnOn() {
System.out.println("난방을 켭니다.");
}
@Override
public void turnOff() {
System.out.println("난방을 끕니다.");
}
});
}}
'기초단계 > JAVA' 카테고리의 다른 글
| 2022.12.16 JAVA 복습 java.base 모듈 (0) | 2022.12.16 |
|---|---|
| 2022.12.15-4 JAVA 복습 예외처리 (1) | 2022.12.15 |
| 2022.12.15-2 JAVA 복습 인터페이스 (0) | 2022.12.15 |
| 2022.12.15 JAVA 복습 상속 (0) | 2022.12.15 |
| 2022.12.14-6 JAVA 복습 클래스 (0) | 2022.12.14 |