java nested class(중첩클래스)

중첩클래스란(Nested Class)?

클래스 내부에 선언한 클래스를 말한다. 인터넷을 찾아보니, 중첩클래스(Nested Class)와 내부클래스(Inner Class) 이름을 혼용해서 사용 하는듯 하다. 본 글에서는 중첩클래스(Nested Class)로 표기한다.
중첩클래스는 크게 중첩클래스와 중첩인터페이스 크게 두개로 나뉜다. 이번글은 중접클래스만 다루도록 하겠다.
중첩클래스(Nested Class)를 사용하면 두 클래스의 멤버들을 서로 쉽게 접근할 수 있다는 장점과 외부에서 불필요한 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다.

중첩클래스(Nested Class)의 활용에 대한 개인적 고민

처음 중첩클래스(Nested Class)를 접했을 때, 대체 이것을 어디에 써먹을 수 있을까 고민을 했었다.
당시 고민을 하다가 애라 모르겠다 하고 그냥 책의 페이지를 넘긴 기억이 난다. 그 고민을 지금 다시 정리해 본다. (개인적인 생각 정리라 참고만 하길…)

자바라는 언어는 객체지향 언어이고 클래스 단위로 프로그래밍을 해야 한다. 내가 만약 어떤 프로그래밍 소스가 필요하다면 그것을 클래스로 만들고 그걸 구현해야 한다.

로직 -> 클래스 선언(클래스 파일생성) -> 클래스를 객체화

자바에서는 소스코드의 단위를 클래스 단위로 강제하고 있다. 이로 인해 클래스의 다양한 장점을 이용해서 코딩을 할 수있다. 다들 들어본 재사용성, 다형성, 캡슐화등이 그 대표적인 것들이다.
하지만 만약 재사용이 필요하지 않고 일회성 소스코드를 만들어 사용할 경우에 그 것들을 클래스 파일로 만들어서 사용해야 할까? 재사용이 필요하지 않는 클래스파일의 생성은 소스코드관리의 관점에서 좋지 못한 선택이다.

중첩클래스(Nested Class)는 이런 문제를 해소하기 위해 사용되는 것이라고 생각된다.
물론 익명클래스도(anonymouse class) 같은 고민을 해결해 준다. 중첩클래스(Nested Class)와 익명클래스는 둘 다 일회성 코드를 만드는 방법이지만 재사용성 노출범위에서, 그 정도에 차이가 있어보인다.
중첩클래스는 클래스를 파일 단위로 만들지는 않지만, 협소하게 재사용이 가능하게 클래스 선언을 한다. 따라서 그만큼 더 클래스의 장점을 사용 할 수 있다. 그에 반해 익명클래스는 클래스를 선언조차 하지 않는다.

중첩클래스와 익명클래스는 코딩시에 상황과 필요에 따라 선택되어 사용될 뿐이다.

참고로 디폴트클래스(defualt class)도 하나의 방법이 되겠지만, 디폴트클래스(defualt class)는 프로그래밍의 구조를 너무 느슨하게 만드는 단점을 가지기 때문에 개인적으로 지양한다. 따라서 굳이 언급하지 않았다.

중첩클래스의 종류

인스턴스 멤버클래스

1
2
3
class A{
class B{ ... }
}

특징 : 외부 클래스 A의 인스턴스 영역에 선언된 중첩클래스를 인스턴스 멤버클래스 라고 부른다. 인스턴스 멤버클래스(B)에서는 정적필드, 정적메소드를 선언할수 없으며, 외부 A클래스의 객체를 통해 내부 중첩클래스B에 접근하여 B의 구현객체를 생성할 수 있다. B로 구현된 객체는 외부객체의 인스턴스 필드와 외부객체의 클래스의 static영역에 접근 할 수 있다.

정적 멤버클래스

1
2
3
class A{
static class B{ ... }
}

특징 : 인스턴스 멤버클래스와 다르게 static(정적)으로 선언된 정적 멤버클래스는 모든 종류의 필드, 메소드를 선언 할 수 있으며, 바깥 A클래스를 통해 내부클래스 B에 접근하여 객체를 생성 할 수 있다.
당연히 내부클래스의 객체는 외부클래스로 구현된 객체의 인스턴스 영역에는 접근 할수 없으며 외부 클래스 A의 static(정적)영역에만 접근 할 수있다.

로컬클래스

1
2
3
4
5
class A{
void method1(){
class B{ ... }
}
}

특징 : method1가 실행할 때만 B중첩클래스 사용가능 하다. 외부 클래스의 모든 영역에 접근 가능하다. 심지어 method1의 메개변수와 로컬변수에도 접근이 가능한데, method1함수가 종료된 이후에도 로컬클래스의 객체는 힙 메모리에 존재해서 계속 사용 될 수 있다.

본래 매개변수나 로컬변수는 메소드실행이 종료되면 메모리에서 제거되어야 할 대상이다. 하지만 그 매개변수와 로컬변수를 로컬클래스로 생성된 객체가 참조한 경우가 문제가 될 수 있다.
자바는 이런 겅우를 위해 컴파일 시 로컬클래스에서 사용하는 매개변수나 로컬변수의 기억장소를 로컬클래스 내부에 복사해 두고 사용한다. (자바스크립트의 클로저를 연상시킨다.)
중요한 점은 참조만 가능하다는 것이다, 로컬클래스에서 자신을 감싸고 있는 함수의 매개변수와 로컬변수를 접근해서 수정을 할 수 없다. 아니 없어야 한다. (자바스크립트와는 다르다.)
따라서 컴파일러는 로컬클레스애서 자신을 감싸고 있는 외부함수의 로컬변수, 매개변수를 수정할 수 없게 강제한다.
java7 버전에서는 로컬클래스에서 참조하는 함수의 매개변수와 로컬변수는 반드시 명시적으로 final 선언 했어야 했다.
java8에서는 자동으로 final키워드를 내부적으로 붙여준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Outter {
//자바7 이전
public void methodJava7(final int arg) {
final int localVariable = 1;
class Inner {
public void method() {
int result = arg + localVariable;
System.out.println("java7 로컬클래스:" + result);
}
}// Inner
Inner java7Inner = new Inner();
java7Inner.method();
}//methodJava7
//자바8 이후
public void methodJava8(int arg) {
int localVariable = 2;
class Inner {
public void method() {
int result = arg + localVariable;
System.out.println("java8 로컬클래스:" + result);
}
}// Inner
Inner java8Inner = new Inner();
java8Inner.method();
}//methodJava8
public static void main(String [] args) {
Outter o1 = new Outter();
o1.methodJava7(7);
o1.methodJava8(8);
System.out.println("----");
}
}

결과

1
2
3
java7 로컬클래스:8
java8 로컬클래스:10
----

이 중첩클래스라는 것을 잘 쓸지 안쓸지는 모르겠으나, 문법적 구조는 눈에 익혀둘 필요가 있어보여 정리해본다.

익명클래스
중첩인터페이스