[java] 17. 내부클래스


내부클래스



내부 클래스는 클래스 내에 선언된 클래스입니다.

클래스에 다른 클래스를 선언하는 이유는 간단합니다.

두 클래스가 서로 긴밀한 관계에 있기 때문입니다.

한 클래스를 다른 클래스의 내부 클래스로 선언하면 두 클래스의 멤버들 간에

서로 쉽게 접근할 수 있다는 장점과 외부에는 불필요한 클래스를 감춤으로써 코드의 복잡성을

줄일 수 있다는 장점을 얻을 수 있습니다.


<내부 클래스의 장점>

 -내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.

 -코드의 복잡성을 줄일 수 있다.(캡슐화)




class A{ //외부 클래스
	class B{ //내부 클래스
		...
	}
	...
}



이 때 내부 클래스인 B는 외부 클래스인 A를 제외하고는 다른 클래스에서 잘 사용되지 않는 것이어야 합니다.



내부클래스의 종류와 특징



내부 클래스의 종류는 변수의 선언위치에 따른 종류와 같습니다.

내부 클래스는 마치 변수를 선언하는 것과 같은 위치에 선언할 수 있으며,

변수의 선언위치에 따라 인스턴스변수, 클래스변수(static 변수),

지역변수로 구분되는 것과 같이 내부 클래스도 선언위치에 따라 다음과 같이 구분되어 집니다.


인스턴스 클래스 : 외부 클래스의 멤버변수 선언위치에 선언하며, 

외부 클래스의 인스턴스멤버처럼 다루어집니다. 주로 외부 클래스의 인스턴스멤버들과

관련된 작업에 사용될 목적으로 선언됩니다.



스태틱 클래스 : 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 static멤버처럼 다루어집니다.



지역 클래스 : 외부 클래스의 메서드나 초기화블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있습니다.



익명 클래스 : 클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스



내부클래스의 선언



아래의 코드에는 외부 클래스(Outer)에 3개의 서로 다른 종류의 내부 클래스를 선언했습니다.


class Outer{
	class InstanceInner{} //인스턴스 클래스
	
	static class StaticInner{} //스태틱 클래스
	
	void myMethod(){
		class LocalInner{} //지역 클래스
	}
}



내부 클래스의 제어자와 접근성



내부클래스도 클래스이기에 abstract난 final과 같은 제어자를 사용할 수 있을 뿐만 아니라,

멤버변수들과 같이 접근제어자를 붙일 수 있습니다.

예제들을 보면서 이에 대해 설명해드리겠습니다.


image



내부 클래스 중에서 스태틱 클래스만 static 멤버를 가질 수 있습니다.

드문 경우지만 내부 클래스에 static 변수를 선언해야한다면 스태틱 클래스로 선언해야합니다.

다만 final 과 static이 동시에 붙은 변수는 상수이므로 모든 내부 클래스에서 정의가 가능합니다.


image



인스턴스멤버는 같은 클래스에 있는 인스턴스멤버와 static 멤버 모두 직접 호출이 가능하지만,

static 멤버는 인스턴스멤버를 직접 호출할 수 없는 것처럼,

인스턴스클래스는 외부 클래스의 인스턴스멤버를 객체생성 없이 바로 사용할 수 있지만,

스태틱 클래스는 외부 클래스의 인스턴스멤버를 객체생성 없이 사용할 수 있습니다.


image



위의 예제는 내부 클래스에서 외부 클래스의 변수들에 대한 접근성을 보여주는 예제입니다.

인스턴스 클래스는 외부 클래스의 인스턴스멤버이기 떄문에 인스턴스 변수 outerIv 와 static 변수 outerCv를 모두 사용할 수 있습니다.

심지어는 outerIv의 접근 제어자가 private 일지라도 사용가능합니다.

스태틱 클래스는 외부클래스의 static 멤버이기 때문에 외부 클래스의 인스턴스 멤버인 outerIv와 InstanceInner를 사용할 수 없습니다.

지역클래스는 외부 클래스의 인스턴스멤버와 static 멤버를 모두 사용할 수 있으며,

지역 클래스가 포함된 메서드에 정의된 지역변수도 사용할 수 있습니다.

단, final이 붙은 지역변수만 접근 가능한데 그 이유는 메서드가 수행을 마쳐서 지역변수가 소멸된 시점에도,

지역 클래스의 인스턴스가 소멸된 지역변수를 참조하려는 경우가 발생 할 수 있기 떄문입니다.

JDK 1.8 부터 지역 클래스에서 접근하는 지역 변수 앞에 final을 생략할 수 있게 바뀌었는데,

컴파일러가 자동으로 붙여줍니다.

즉, 편의상 fianl을 생략할 수 있게 한 것일 뿐 해당 변수의 값이 바뀌는 문장이 있으면 컴파일 에러가 발생합니다.

다음 예제를 보시죠!


image


image



출력결과입니다.


image



위의 예제는 외부 클래스가 아닌 다른 클래스에서 내부 클래스를 생성하고 내부 클래스의 멤버에 접근하는 예제입니다.

하지만 위의 예제와 같은 상황은 자주 발생하지 않습니다.

내부클래스는 말 그대로 외부 클래스 내부에서 사용되어야 하는 것인데,

위의 예제 같은 경우가 발생했다는 것은 내부클래스로 선언해서는 안 되는 클래스를 내부 클래스로 선언했다는 의미이기 때문입니다.

참고로 컴파일 시 생성되는 클래스 파일은 다음과 같습니다.


InnerEx4.class

Outer.class

Outer$InstanceInner.class

Outer$StaticInner.class

Outer$1LocalInner.class



컴파일 했을 때 생성되는 파일명은 ‘외부 클래스명$내부 클래스명.class’ 형식이고,

지역클래스는 서로 다른 메서드 내에서 이름이 같은 경우가 있을 수 있기에, 내부클래스명 앞에 숫자가 붙습니다.

마지막 예제는 this를 사용한 예제입니다.


image



익명클래스



익명클래스는 특이하게도 다른 내부 클래스들과는 달리 이름이 없습니다.

클래스의 선언과 객체의 생성을 동시에 하기 때문에

단 한번만 사용될 수 있고 오직 하나의 객체만을 생성할 수 있는 일회용 클래스입니다.


new 부모클래스이름(){
	//멤버선언
}



또는

new 구현인터페이스이름(){
	//멤버선언
}



이름이 없기 때문에 생성자도 가질 수 없으며, 부모클래스의 이름이나 구현하고자 하는 인터페이스의 이름을 사용해서 정의하기 때문에

하나의 클래스로 상속받는 동시에 인터페이스를 구현하거나 둘 이상의 인터페이스를 구현할 수 없습니다.

다음은 익명클래스를 사용한 예제입니다.


image