내부 클래스(inner class)
클래스 내에 선언된 클래스
-> 이 때 내부 클래스 B는 외부 클래스 A를 제외하고는 다른 클래스에서 잘 사용되지 않아야 함!
-> B는 A의 내부클래스(inner class), A는 B를 감싸고 있는 외부 클래스(outer class)
내부 클래스의 장점
- 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다. (B에서 A의 객체 생성 없어도 A의 멤버 접근 가능)
- 외부에 불필요한 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.(캡슐화)
public class InnerClass {
public static void main(String[] args) {
// BBB b = new BBB(); // 내부클래스는 다른 클래스에서 접근 불가능(AAA객체 생성뒤 가능)
// b.method(); // 참조변수 b를 통해서 BBB 객체의 method 호출
}
}
class AAA { // AAA는 BBB의 외부 클래스
int i = 100;
BBB b = new BBB();
class BBB { // BBB는 AAA의 내부 클래스(C에서는 사용 xx)
void method() {
AAA a = new AAA(); // 객체 생성하고 사용
System.out.println(a.i);
System.out.println(i); // 객체 생생없이 외부 클래스의 멤버 접근가능(내부클래스의 장점)
}
}
}
class CCC { // C클래스에서는 BBB를 사용 할 수 없음
// BBB a = new BBB();
}
내부 클래스의 종류와 특징
- 내부 클래스의 종류와 유효범위(scope, 사용할 수 있는 범위)는 변수와 동일
- 내부 클래스의 종류 == 변수의 선언위치에 따른 종류(iv, cv, lv)
내부 클래스 | 특징 |
인스턴스 (내부)클래스(instance class) (iv와 같음) | 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 인스턴스멤버처럼 다뤄진다. 주로 외부 클래스의 인스턴스멤버들과 관련된 작업에 사용될 목적으로 선언된다. |
스태틱 (내부)클래스(static class) (cv와 같음) |
외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 static멤버처럼 다뤄진다. 주로 외부 클래스의 static 멤버, 특히 static메서드에서 사용될 목적으로 선언된다. |
지역 (내부)클래스(local class) (lv와 같음) | 외부 클래스의 메서드나 초기화블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있다. |
익명 클래스(anonymous class) | 클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스(일회용) |
내부 클래스의 제어자와 접근성
-> 내부클래스가 외부클래스 멤버와 같이 간주된다!!
-> 원래 class 앞에는 접근제어자 public 아니면 (default)만 올 수 이지만 내부 class 앞에는 private, protected도 가능함(iv, cv, lv도 접근제어자 다 사용할 수 있기 때문)
예제1) Static 클래스만 static 멤버를 정의할 수 있고, 인스턴스 클래스, 지역 내부 클래스는 static 멤버를 정의할 수 없다.
public class OuterClass { // 외부클래스
class InstanceInner { // 인스턴스 내부 클래스
int iv = 100; // 인스턴스 변수 허용
static int cv = 100; // 에러! static 변수를 선언할 수 없다.
final static int CONST = 100; // final static은 상수이므로 허용
}
class StaticInner { // static 내부 클래스에서는 외부 클래스의 인스턴스 멤버에 접근할 수 없다
int iv = 200;
static int cv = 200; // static 클래스만 static 멤버를 정의할 수 있다.(static은 객체 생성 없이 사용 가능)
}
void myMethod() { // 메서드
class LocalInner { // 지역 내부 클래스
int iv = 300;
static int cv = 300; // 에러! static 변수를 선언할 수 없다.
final static int CONST = 300; // final static은 상수이므로 허용
}
}
public static void main(String[] args) {
System.out.println(InstanceInner.CONST); // 100
System.out.println(StaticInner.cv); // 200
// System.out.println(LocalInner.CONST); // 에러! 지역 내부 클래스는 메서드 내에서만
}
}
- staticInner 클래스에서 iv가 올 수 있는이유: staticInner 클래스도 클래스의 한 종류이기 때문에 멤버변수 정의할 때 처럼 iv, cv를 선언할 수 있다.
- 하지만 InstanceInner(인스턴스내부클래스)와 LocalInner(지역내부클래스) 에는 cv를 선언할 수 없다. (cv는 오직 staticInner(스태틱 클래스)에만!)
- 내부클래스를 정의했는데 static 멤버변수를 쓰고 싶다면 내부클래스를 static클래스로 정의해야 함!
- final과 static이 동시에 붙은 변수 == 상수 -> 모든 내부 클래스에서 정의 가능
- 지역내부 클래스(LocalInner)의 static 상수는 메서드 내에서만 사용 가능!
예제2) 인스턴스 멤버는 static 멤버를 항상 허용하지만 static 멤버는 인스턴스 멤버를 허용하지 않는다. static 멤버는 인스턴스 멤버에 접근 불가능하다.
public class InnerClass2 {
class InstanceInner {} // 인스턴스 내부클래스 선언
static class StaticInner { } // static 내부클래스 선언
InstanceInner iv = new InstanceInner(); // 인스턴스멤버 간에는 서로 직접 접근이 가능
static StaticInner cv = new StaticInner(); // static 멤버 간에는 서로 직접 접근이 가능
static void staticMethod() { // Static 내부메서드
// InstanceInner iv2 = new InstanceInner(); // Static 멤버는 인스턴스멤버에 직접 접근할 수 없다.
StaticInner cv2 = new StaticInner();
}
void instanceMethod() { // 인스턴스 내부메서드에서는 인스턴스멤버와 static멤버 모두 접근 가능
InstanceInner iv3 = new InstanceInner();
StaticInner cv3 = new StaticInner();
// LocalInner lv = new LocalInner(); // 메서드 내에 지역적으로 선언된 내부 클래스는 외부에서 접근할 수 없다.
}
void myMethod( ) {
class LocalInner { // 지역내부클래스
LocalInner lv = new LocalInner();
}
}
예제3) 내부 클래스에서 외부 클래스의 변수들에 대한 접근성을 보여주는 예제
-> private은 내부클래스에서는 사용할 수 있다. 내부클래스에서는 외부클래스의 private 멤버도 접근 가능하다!
public class InnerClass3 {
private int outerIv = 0;
static int outerCv = 0;
class InstatnceInner { // 인스턴스 클래스는 외부클래스의 인스턴스, static 멤버에 접근 가능
int iiv = outerIv; // 1. 외부클래스의 private 멤버도 접근 가능하다.
int iiv2 = outerCv;
}
static class StaticInner { // 스태틱 클래스는 외부클래스의 인스턴스멤버에 접근 불가능
int siv = outerCv;
static int scv = outerCv;
}
void myMethod() {
int lv = 0; // 값이 바뀌지 않는 변수는 상수로 간주
final int LV = 0; // JDK1.8부터 final 생략 가능
class LocalInner { // 2. 지역 내부 클래스를 감싸고 있는 메서드의 상수만 사용가능
int liv = outerIv; // 외부클래스 인스턴스 변수 접근 가능
int liv2 = outerCv; // 외부클래스 static 변수 접근 가능
//에러! 외부 클래스의 지역변수는 final이 붙은 변수(상수)만 접근 가능
int liv3 = lv; // 에러지만 (JDK1.8부터 에러아님)
int liv4 = LV; // OK!
}
}
}
예제4) 외부 클래스가 아닌 다른 클래스에서 내부 클래스를 생성하고 내부 클래스의 멤버에 접근하는 예제(내부 클래스로 선언해서는 안되는 클래스를 내부 클래스로 선언함)
class Outer2 {
class InstanceInner { // 내부 인스턴스 클래스
int iv = 100;
}
static class StaticInner { // 내부 스태틱 클래스
int iv = 200;
static int cv = 300;
}
void myMethod() {
class LocalInner { // 지역 내부 클래스
int iv = 400;
}
}
}
public class InnerClass4 {
public static void main(String[] args) {
// 외부 클래스의 인스턴스를 먼저 생성해야 인스턴스 클래스의 인스턴스를 생성 가능
Outer2 oc = new Outer2();
Outer2.InstanceInner li = oc.new InstanceInner();
System.out.println("li.iv: " + li.iv); // li. iv: 100
System.out.println("Outer2.StaticInner.cv: " + Outer2.StaticInner.cv); // Outer2.StaticInner.cv: 300
// 스태틱 내부 클래스의 인스턴스는 외부 클래스를 먼저 생성하지 않아도 된다.
Outer2.StaticInner si = new Outer2.StaticInner();
System.out.println("si.iv: " + si.iv); // si.iv: 200
}
}
컴파일시 생성되는 클래스 파일
InnerClass4.class | main메서드를 담고 있는 public class | Outer2.class | 내부클래스가 있는 외부클래스 |
Outer$InstanceInner.class Outer$StaticInner.class Outer$1LocalInner.class |
“외부클래스명$내부클래스명.class” |
예제5) 외부 클래스와 내부 클래스에 선언된 변수의 이름이 같을 때 변수 앞에 ‘this’ 또는 ‘외부 클래스명.this’를 붙여서 서로 구별할 수 있다.
public class InnerClass5 {
public static void main(String[] args) {
Outer3 outer = new Outer3(); // 참조변수outer로 Outer3객체 생성
Outer3.Inner in = outer. new Inner(); // Outer3의 Inner클래스의 참조변수 in으로 Inner 객체 생성
in.method1(); // 참조변수in으로 method1 호출
}
}
class Outer3 {
int value = 10; // Outer3.this.value 외부 클래스의 iv
class Inner {
int value = 20; // this.value 내부 클래스의 iv
void method1() {
int value = 30; // value lv(지역변수)
System.out.println("value: " + value); // value: 30
System.out.println("this.value: " + this.value); // this.value: 20
System.out.println("Outer3.this.value: " + Outer3.this.value); //Outer3.this.value: 10
}
} // Inner 클래스의 끝
} // Outer3 클래스의 끝
익명 클래스(anonymous class)
이름이 없는 일회용 클래스. 정의와 생성을 동시에
이름이 없어서 생성자도 가질 수 없고 단 하나의 클래스를 상속받거나 단 하나의 인터페이스 만을 구현할 수 있다.
예제1) 익명클래스의 사용 예
class Anonymous {
Object iv = new Object() { void method() {} }; // 익명클래스
static Object cv = new Object() { void method() {} }; // 익명클래스
void myMethod() {
Object lv = new Object() { void method() {} }; // 익명클래스
}
}
컴파일시 생성되는 클래스 파일
Anonymous.class
Anonymous$1.class
Anonymous$2.class
Anonymous$3.class
-> 익명클래스는 이름이 없기 때문에 “외부클래스명$숫자.class”의 형식
예제2) 일반 클래스 -> 익명 클래스
// 일반 클래스
public class AnonymousClass {
public static void main(String[] args) {
Button b = new Button("start");
b.addActionListener(new EventHandler());
}
}
class EventHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("ActionEvent occurred!!!");
}
}
// 익명 클래스
public class AnonymousClass {
public static void main(String[] args) {
Button b = new Button("start");
b.addActionListener(new ActionListener() { //클래스의 정의와 객체 생성을 동시에
public void actionPerformed(ActionEvent e) {
System.out.println("ActionEvent occurred!!!");
}
});
}
}
'멀티캠퍼스 풀스택 과정 > Java의 정석' 카테고리의 다른 글
자바의 정석5-2 예외 발생, 예외 처리, Finally블럭 (0) | 2022.01.06 |
---|---|
자바의 정석5-1 예외처리(exception handling) (0) | 2022.01.06 |
자바의 정석4-2 인터페이스(interface), 디폴트와 static 메서드 (0) | 2022.01.05 |
자바의 정석4-1 추상 클래스(abstract class)와 추상메서드 (0) | 2022.01.05 |
자바의 정석3-6 instanceof 연산자, 다형성의 장점(1,2) (0) | 2022.01.05 |