반응형
1. 중첩클래스(Nested Class)란?
- 클래스(OuterClass) 내부에 다른 클래스(InnerClass)를 선언하는 것
- 컴파일 결과를 외부클래스$내부클래스.class 파일로 제공
2. 중첩클래스는 언제 사용?
- 두개의 클래스가 밀접한 관계에 있을 때 선언한다.캡슐화를 강화하는데 사용한다.
3. 중첩클래스 예시
- 이벤트프로그램 만들 때 - 일반중첩클래스 많이 사용
- 다중스레드프로그램 만들 때 - 일반중첩클래스 많이 사용
- 이벤트 발생 클래스와 이벤트 처리 클래스를 캡슐화할 때 사용함
- EX) 계산기 UI 만들기
- 컴포넌트를 조립해 프레임을 만듦 - 프레임을 만들어주는 클래스가 따로 존재하는데, 버튼을 누를 때마다 특정 명령이 실행되게함( 이벤트를 처리하는 클래스 )
- 이벤트를 발생하는 클래스 vs 이벤트를 처리하는 클래스
- 이 둘의 별개지만, 이벤트가 발생해야 → 이벤트가 처리됨
- 이럴 때는 중첩클래스를 사용하여 이벤트발생 클래스 내 이벤트 처리클래스를 넣을 수 있음
- 장점 : 이벤트발생 객체를 이벤트처리 클래스내에서 사용 가능하다.
4. 중첩클래스의 종류
1.🍊일반 중첩클래스 (인스턴스 중첩클래스)
- 내부클래스를 하나의 자료형으로 사용하는 것 (마치 필드와 메소드처럼)
- 일반 중첩 클래스에서는 static 제한자 사용불가능
- static필드 선언 NO
- static메소드 선언 NO
- 외부클래스의 멤버변수 선언위치에 선언된다.
- 외부클래스의 인스턴스 멤버처럼 다뤄진다.
- 주로 외부클래스의 인스턴스멤버들과 관련된 작업에 사용될 목적으로 선언된다.
🍊특징
- 외부클래스 → 내부클래스 접근하기
- 직접적인 참조 불가능
- 외부클래스에서 내부클래스로 객체를 생성하면 내부클래스의 접근 제한자가 무엇이든 필드 또는 메소드 모두 참조 가능
- 내부클래스에서 → 외부클래스 접근하기
- 외부클래스의 접근 제한자가 무엇이든 필드 또는 메소드 모두 참조 가능
🍊일반중첩클래스의 예시
package nested;
public class OuterOne {
//private필드
private int outerNum;
//생성자
public OuterOne() {}
public OuterOne(int outerNum) {
super();
this.outerNum = outerNum;
}
//Getter&Setter
public int getOuterNum() {return outerNum;}
public void setOuterNum(int outerNum) {this.outerNum = outerNum;}
//displayOuter메소드
public void displayOuter() {
System.out.println("outerNum = "+outerNum);
//🍊외부에서 내부의 직접적인 참조 불가능
//System.out.println("innerNum = "+innerNum); //error
//🍊외부에서 내부의 객체를 생성하면 접근제한자에 상관없이 내부클래스의 필드 또는 메소드 참조 가능
InnerOne innerOne=new InnerOne(200); //ok
System.out.println("innerNum = "+innerOne.innerNum); //ok
}
//🍊일반 중첩 클래스 >> 컴파일 결과를 [외부클래스$내부클래스.class] 파일로 제공
// => 일반 중첩 클래스에서는 static 제한자를 사용하여 필드 또는 메소드 선언 불가능
public class InnerOne {
//private필드
private int innerNum;
//생성자
public InnerOne() {}
public InnerOne(int innerNum) {
super();
this.innerNum = innerNum;
}
//Getter&Setter
public int getInnerNum() {return innerNum;}
public void setInnerNum(int innerNum) {this.innerNum = innerNum;}
//displayInner메소드
public void displayInner() {
System.out.println("innerNum = "+innerNum);
//내부클래스에서는 외부클래스의 필드 또는 메소드를 접근제한자에 상관없이 참조 가능
//System.out.println("outerNum = "+outerNum); //ok
//displayOuter(); //ok
}
}
}
🍊일반중첩클래스의 실행프로그램
package nested;
import nested.OuterOne.InnerOne;
public class OuterOneApp {
public static void main(String[] args) {
//1.
//일반적인 객체생성방법
OuterOne outerOne=new OuterOne(100);
outerOne.displayOuter();
//outerNum = 100
//innerNum = 200
//2.
//일반 중첩 클래스로 직접 객체 생성 불가능
//InnerOne innerOne=new InnerOne(); //error
//3.
//외부클래스의 객체를 이용하여 일반 중첩 클래스의 객체 생성 가능 - 효율적이지 못함
InnerOne innerOne=outerOne.new InnerOne(300); //ok
innerOne.displayInner();
//innerNum = 300
//outerNum = 100
//outerNum = 100
//innerNum = 200
}
}
2.🍋static 중첩클래스(정적 중첩 클래스)
- 외부 클래스 말고, 다른 클래스에서도 직접 객체를 만들어 사용 가능
- static 제한자를 사용하여 클래스 선언
- 정적 중첩 클래스에서는 static 제한자 사용가능
- 모든 종류의 필드와 메소드 선언 가능 (static ok, non-static ok)
- 외부클래스의 멤버변수 선언위치에 선언된다.
- 외부클래스의 static멤버처럼 다뤄진다.
- 주로 외부클래스의 static멤버, 특히 static메소드에서 사용될 목적으로 선언된다.
🍋특징
- 외부클래스 → 내부클래스(staic) 접근하기
- 내부클래스(static)의 인스턴스멤버 사용하기
- 내부클래스로 객체를 생성하여 객체를 통해 접근제한자에 상관없이 내부클래스의 필드 또는 메소드 참조 가능
- 내부클래스(static)의 static멤버 사용하기
- 내부클래스의 정적필드는 객체 생성 없이 클래스로 접근하여 참조 가능
- 내부클래스(static)의 인스턴스멤버 사용하기
- 내부클래스(static) → 외부클래스 접근하기
- 내부클래스(static)에서는 외부클래스의 필드 또는 메소드 참조 불가능!!!
- 단, 외부클래스의 정적필드(static)나 정적메소드(static)는 참조 가능
🍋 Runtime클래스의 nested클래스
- (배포된 클래스 중 정적중첩클래스)
- 우리가 직접 정적중첩클래스를 만들 일은 없지만, 배포된 클래스 중 Runtime클래스의 nested클래스가 정적중첩클래스이다.
- 즉, Runtime클래스의 nested클래스가 정적중첩클래스라서 Runtime.Version으로 객체 생성이 가능하다.
🍋중첩클래스의 예시
package nested;
public class OuterTwo {
//private필드
private int outerNum;
//생성자
public OuterTwo() { }
public OuterTwo(int outerNum) {
super();
this.outerNum = outerNum;
}
//Getter&Setter
public int getOuterNum() {return outerNum;}
public void setOuterNum(int outerNum) {this.outerNum = outerNum;}
//displayOuter메소드
public void displayOuter() {
System.out.println("outerNum = "+outerNum);
//🍋static내부클래스의 인스턴스필드 사용하기
//외부클래스에서는 내부클래스로 객체를 생성한다면 접근제한자에 상관없이 내부클래스의 필드 또는 메소드 참조 가능
InnerTwo innerTwo=new InnerTwo(); //ok
System.out.println("innerNum = "+innerTwo.innerNum);
//🍋static내부클래스의 static필드 사용하기
//내부클래스의 정적필드는 객체 생성 없이 클래스로 접근하여 참조 가능
System.out.println("staticNum = "+InnerTwo.staticNum); //ok
}
//🍋정적 중첩 클래스 - static 제한자를 사용하여 클래스 선언
// => 정적 중첩 클래스에서는 static 제한자를 사용하여 필드 또는 메소드 선언 가능
public static class InnerTwo {
//private필드
private int innerNum;
//private static필드
private static int staticNum=300;
//생성자
public InnerTwo() { }
public InnerTwo(int innerNum) {
super();
this.innerNum = innerNum;
}
//Getter&Setter
public int getInnerNum() {return innerNum;}
public void setInnerNum(int innerNum) {this.innerNum = innerNum;}
public static int getStaticNum() {return staticNum;}
public static void setStaticNum(int staticNum) {InnerTwo.staticNum = staticNum;}
//displayInner메소드
public void displayInner() {
System.out.println("innerNum = "+innerNum);
System.out.println("staticNum = "+staticNum);
//내부클래스에서는 외부클래스의 필드 또는 메소드 참조 불가능
// => 외부클래스의 정적필드나 정적메소드는 참조 가능
//System.out.println("outerNum = "+outerNum); //error
}
}
}
🍋중첩클래스의 실행프로그램
package nested;
//import nested.OuterTwo.InnerTwo(중첩클래스)
import nested.OuterTwo.InnerTwo;
public class OuterTwoApp {
public static void main(String[] args) {
//1.
//일반적인 객체 생성 방법
OuterTwo outerTwo=new OuterTwo(100);
outerTwo.displayOuter();
//outerNum = 100
//innerNum = 0
//staticNum = 300
//2.
//static 중첩 클래스는 직접 객체 생성 가능
InnerTwo innerTwo=new InnerTwo(200);
innerTwo.displayInner();
//innerNum = 200
//staticNum = 300
}
}
3.🍈local클래스(Local 중첩 클래스)
- 선언된 메소드에서만 객체 생성하여 사용 가능
- 메소드 내부에 선언된 클래스메소드가 종료되면 클래스는 자동 소멸(지역변수와 똑같음)
- final 또는 abstract 제한자만 사용하여 선언 가능
- static 제한자를 사용하여 필드 또는 메소드 선언 불가능
- 선언된 메소드 내부에서만 객체를 생성하여 사용하므로 접근제한자 선언 생략 가능
- 생성자 , Setter & Getter메소드도 굳이 만들필요없음
🍈특징
- 메소드 → 메소드내부클래스(local클래스) 접근하기
- local메소드에 의해 local객체 생성 가능
- 메소드가 종료되면 local클래스 자동 소멸
🍈local클래스의 사용
- 이번트에 대해 결과가 나오지 않더라도, 결과에 대한 또 다른 이벤트를 만들 수 있음
- 비동기식 처리를 위한 스레드 객체를 생성하기 위해 사용
- 그러나 우리가 local클래스를 직접 만들어 사용할 일은 없다!
- 익명의 내부클래스에서 객체만들 때 (Day13 참고)
🍈local클래스의 예시
package nested;
public class OuterThree {
//필드
private int outerNum;
//생성자
public OuterThree() { }
public OuterThree(int outerNum) {
super();
this.outerNum = outerNum;
}
//Getter & Setter
public int getOuterNum() {return outerNum;}
public void setOuterNum(int outerNum) {this.outerNum = outerNum;}
//displayOuter메소드
public void displayOuter() {
System.out.println("outerNum = "+ outerNum);
//외부클래스에서 로컬클래스를 사용하여 객체 생성 불가능
//InnerThree innerThree = new OuterThree(); //error
}
//local메소드
public void local() {
//local클래스(Local Class)
class InnerThree{
//local클래스의 필드
int innerNum;
//local클래스의 메소드
void displayInner(){
System.out.println("innerNum = "+ innerNum);
}
}
//local메소드 내부에서 local클래스참조변수로 객체생성
InnerThree innerThree = new InnerThree();
innerThree.innerNum = 200;
System.out.println("innerThree.innerNum = "+innerThree.innerNum);
innerThree.displayInner();
}
}
🍈local클래스의 실행프로그램
package nested;
public class OuterThreeApp {
public static void main(String[] args) {
OuterThree outerThree=new OuterThree(100);
outerThree.displayOuter();
//outerNum = 100
outerThree.local();
//innerThree.innerNum = 200
//innerNum = 200
}
}
4.🔎익명의 내부 클래스 (Annoymous Inner Class)( = local중첩클래스)
🔎 개요
- 인터페이스나 추상클래스는 참조변수 생성은 가능하지만, 객체 생성은 불가능하다.
- 또한 인터페이스는 클래스가 상속받기 위한 자료형이다.
Annoymous annoymous = new Annoymous; //객체생성 시 error
- 여기서 인터페이스의 단점!!!
- 인터페이스(추상메소드필수)나 추상클래스(추상메소드선택)를 상속받은 자식클래스는 인터페이스나 추상클래스에 선언된 모든 추상메소드를 반드시 오버라이드 선언해야하며, 오버라이드 된 메소드를 사용하기 위해서는 자식클래스로 객체를 생성하여 인터페이스 참조변수에 객체를 저장해야한다.
- 따라서 인터페이스를 사용하기 위해서는 자식클래스를 꼭 만들어줘야한다.
- 그렇다면 무조건 자식클래스를 만들어줘야할까????
- 아니요. 익명의 내부클래스 이용하자..!
🔎 익명의 내부클래스(Annoymous Inner Class)의 개념
- 인터페이스를 상속받은 이름이 없는 자식클래스
- 클래스의 선언과 객체 생성을 동시에 하는 이름이 없는 클래스(일회용)
🔎 익명의 내부클래스 사용법
- 인터페이스나 추상클래스를 상속받은 이름이 없는 자식클래스를 선언하고 ( = local중첩클래스), 자식클래스로 익명객체를 생성하여 인터페이스 참조변수에 저장
🔎 익명의 내부클래스 선언과 사용 시 주의점
- 익명의 내부클래스에서는 반드시 인터페이스의 모든 추상메소드를 오버라이드 선언해야함
- 인터페이스 대신 추상클래스를 사용하여 익명의 내부 클래스로 선언해 익명 객체 생성 가능
- 간단한 이벤트처리만들 때 익명객체를 사용하지만 많이 안씀
🔎 익명의 내부클래스 사용 예제
- Anonymous인터페이스
public interface Anonymous {
void display();
}
- AnnoymousApp실행프로그램
public class AnnoymousApp {
public static void main(String[] args) {
//인터페이스 참조변수에 상속받은 익명 자식클래스 선언과 동시에 자식객체생성 후 저장!
Annoymous annoymous = new Annoymous() {
@Override //오버라이드 선언 필수
public void display() {
System.out.println("익명의 내부클래스의 오버라이드 메소드 호출");
}
};
annoymous.display();
//익명의 내부클래스의 오버라이드 메소드 호출
}
}
반응형
'java > realization' 카테고리의 다른 글
[realization] 6. 상수필드(Constant)와 열거형(Enum) (1) | 2024.06.11 |
---|---|
[realization] 5. 기본메소드를 포함한 인터페이스 (0) | 2024.06.11 |
[realization] 4. 결합도를 낮춰 유지보수의 효율성 높이는 방법 (1) | 2024.06.10 |
[realization] 3. 🚢Boat🚗Car🔥RealApp (0) | 2024.06.10 |
[realization] 2. 🦊WolfHumanApp (0) | 2024.06.09 |