내부 클래스란?
내부 클래스는 클래스 내에 선언된 클래스이다.
내부 클래스를 쓰는 이유는 두 클래스가 긴밀한 관계기 때문이다.
내부 클래스의 장점
- 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
- 코드의 복잡성을 줄일 수 있다(캡슐화)
class A{
// ...
}
class B{
// ...
}
// 내부클래스화
class A{ // 외부클래스
// ...
class B{ // 내부 클래스
//...
}
// ...
}
내부 클래스인 B는 외부 클래스 A를 제외하고는 다른 클래스에서 잘 사용되지 않는 것이어야 한다.
내부 클래스의 종류와 특징
내부 클래스 | 특 징 |
인스턴스 클래스 (instance class) |
외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 인스턴스멤버처럼 다루어진다. 주로 외부 클래스의 인스턴스멤버들과 관련된 작업에 사용될 목적으로 선언된다. |
스태틱 클래스 (static class) |
외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 static멤버처럼 다루어진다. 주로 외부 클래스의 static멤버. 특히 static메서드에서 사용될 목적으로 선언된다. |
지역 클래스 (local class) |
외부 클래스의 메서드나 초기화블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있다. |
익명 클래스 (anonymous class) |
클래스으 선언과 객체의 생성을 동시에 하는 이름없는 클래스(일회용) |
내부 클래스의 선언
변수가 선언된 위치에 따라 인스턴스변수, 클래스변수, 지역변수로 나뉘듯,
내부 클래스도 선언위치에 따라 나뉜다.
선언위치에 따라 같은 선언위치의 변수와 동일한 유효범위와 접근성을 갖는다.
class Outer{
class InstanceInner{}
static class StaticInner{}
void myMethod(){
class LocalInner{}
}
}
내부 클래스의 제어자와 접근성
내부 클래스도 클래스이기 때문에 abstract나 final 같은 제어자를 사용할 수 있고,
멤버변수들처럼 private, protected와 같은 접근제어자도 사용이 가능하다.
관련 예제 모음
class InnerEx1 {
class InstanceInner {
int iv = 100;
// static int cv = 100; // 에러! static변수를 선언할 수 없다.
final static int CONST = 100; // final static은 상수이므로 허용한다.
}
static class StaticInner {
int iv = 200;
static int cv = 200; // 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);
System.out.println(StaticInner.cv);
}
}
class InnerEx2 {
class InstanceInner {}
static class StaticInner {}
// 인스턴스멤버 간에는 서로 직접 접근이 가능하다.
InstanceInner iv = new InstanceInner();
// static 멤버 간에는 서로 직접 접근이 가능하다.
static StaticInner cv = new StaticInner();
static void staticMethod() {
// static멤버는 인스턴스멤버에 직접 접근할 수 없다.
// InstanceInner obj1 = new InstanceInner();
StaticInner obj2 = new StaticInner();
// 굳이 접근하려면 아래와 같이 객체를 생성해야 한다.
// 인스턴스클래스는 외부 클래스를 먼저 생성해야만 생성할 수 있다.
InnerEx2 outer = new InnerEx2();
InstanceInner obj1 = outer.new InstanceInner();
}
void instanceMethod() {
// 인스턴스메서드에서는 인스턴스멤버와 static멤버 모두 접근 가능하다.
InstanceInner obj1 = new InstanceInner();
StaticInner obj2 = new StaticInner();
// 메서드 내에 지역적으로 선언된 내부 클래스는 외부에서 접근할 수 없다.
// LocalInner lv = new LocalInner();
}
void myMethod() {
class LocalInner {}
LocalInner lv = new LocalInner();
}
}
class InnerEx3 {
private int outerIv = 0;
static int outerCv = 0;
class InstanceInner {
int iiv = outerIv; // 외부 클래스의 private멤버도 접근가능하다.
int iiv2 = outerCv;
}
static class StaticInner {
// 스태틱 클래스는 외부 클래스의 인스턴스멤버에 접근할 수 없다.
// int siv = outerIv;
static int scv = outerCv;
}
void myMethod() {
int lv = 0;
final int LV = 0; // JDK1.8부터 final 생략 가능
class LocalInner {
int liv = outerIv;
int liv2 = outerCv;
// 외부 클래스의 지역변수는 final이 붙은 변수(상수)만 접근가능하다.
// int liv3 = lv; // 에러!!!(JDK1.8부터 에러 아님)
int liv4 = LV; // OK
}
}
}
class Outer {
class InstanceInner {
int iv=100;
}
static class StaticInner {
int iv=200;
static int cv=300;
}
void myMethod() {
class LocalInner {
int iv=400;
}
}
}
class InnerEx4 {
public static void main(String[] args) {
// 인스턴스클래스의 인스턴스를 생성하려면
// 외부 클래스의 인스턴스를 먼저 생성해야 한다.
Outer oc = new Outer();
Outer.InstanceInner ii = oc.new InstanceInner();
System.out.println("ii.iv : "+ ii.iv);
System.out.println("Outer.StaticInner.cv : " + Outer.StaticInner.cv);
// 스태틱 내부 클래스의 인스턴스는 외부 클래스를 먼저 생성하지 않아도 된다.
Outer.StaticInner si = new Outer.StaticInner();
System.out.println("si.iv : "+ si.iv);
}
}
위의 경우 접근은 가능하지만, 외부클래스가 아닌 다른클래스에서 접근하므로 내부클래스로 선언하면 안되는 클래스이다. 컴파일 시 생성되는 파일명은 '외부 클래스명$내부 클래스명.class'형식이다.
동일이름으로 여러개일경우, 내부클래스명 앞에 숫자가 붙는다.
class Outer {
int value=10; // Outer.this.value
class Inner {
int value=20; // this.value
void method1() {
int value=30;
System.out.println(" value :" + value);
System.out.println(" this.value :" + this.value);
System.out.println("Outer.this.value :" + Outer.this.value);
}
} // Inner클래스의 끝
} // Outer클래스의 끝
class InnerEx5 {
public static void main(String args[]) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.method1();
}
} // InnerEx5 끝
결과
value :30
this.value :20
Outer.this.value :10
위의 예제는 내부 클래스와 외부 클래스에 선언된 변수의 이름이 같을 때,
변수 앞에 'this' 또는 '외부 클래스명.this'를 붙여서 서로 구분이 가능하다.
익명 클래스(anoymous class)
익명 클래스는 단 한번만 사용 될때 쓴다.
이름이 없고 클래스의 선언과 객체의 생성을 동시에 한다.
익명 클래스는 조상클래스의 이름이나 구현하고자 하는 인터페이스의 이름을 사용해서 정의하기 때문에
오로지 단 하나의 클래스를 상속받거나 단 하나의 인터페이스만을 구현할 수 있다.
class InnerEx6 {
Object iv = new Object(){ void method(){} }; // 익명클래스
static Object cv = new Object(){ void method(){} }; // 익명클래스
void myMethod() {
Object lv = new Object(){ void method(){} }; // 익명클래스
}
}
익명 클래스는 이름이 없기 때문에 '외부 클래스명$숫자.class' 형식으로 클래스파일명이 결정된다.
참조
'Java의 정석' 책
'Language > Java' 카테고리의 다른 글
[Java] Object 클래스 (0) | 2022.03.07 |
---|---|
[Java] 예외처리(exception handling), 에러와 예외의 차이 (0) | 2022.03.06 |
[Java] 인터페이스(interface) (0) | 2022.03.04 |
[Java] 추상클래스(abstract class) -추상화,구체화 (0) | 2022.03.04 |
[Java] 다형성(polymorphism) - 업캐스팅,다운캐스팅 (0) | 2022.03.04 |
댓글