Nested(중첩) 클래스는 내부에 존재하지만 외부 클래스와 독립적인 느낌을 준다. 반면, Inner(내부) 클래스는 내부에 존재하면서 외부 클래스와 연결된 느낌을 준다. 즉, 두 클래스 모두 내부에 있지만, 외부 클래스와의 관계성에 차이가 있다.
class Outer {
private static int test1 = 1;
private int test2 = 2;
static class Nested {
private void Test() {
sout(test1) //--> can do
sout(test2) //--> can't do
}
}
}
위의 코드는 간략하게 Outer(외부 클래스)에 Nested(정적 중첩 클래스)를 정의한 예시이다. 여기서 Nested 인스턴스가 사용하는 Test 메서드에서는 Outer 클래스의 클래스 변수는 사용 가능하지만, 인스턴스 변수는 사용할 수 없다.
이 점은 타당하다. Outer 클래스의 private 변수는 내부 클래스에서 자유롭게 접근할 수 있지만, 정적 Nested 클래스이기 때문에 인스턴스 변수에는 접근할 수 없고, 클래스 변수에는 접근이 가능하다. 이는 정적 클래스가 인스턴스에 의존하지 않기 때문이다. 마찬가지로, static 메서드는 클래스 차원에서 접근할 수 있지만, 인스턴스 메서드에는 접근할 수 없다.
내부 클래스는 중첩 클래스에서 static을 제거하여 사용하면 된다. 내부 클래스는 외부 클래스의 인스턴스에 소속된다고 볼 수 있다. 즉, Outer 클래스의 인스턴스를 통해 Inner 클래스를 활용할 수 있으며, 이를 다음과 같은 방식으로 생성한다. (이 생성 방식은 다소 생소할 수 있다.)
public class innerOuterMain {
public static void main(String[] args) {
innerOuter outer = new innerOuter();
innerOuter.Inner inner = outer.new Inner();
inner.print();
}
}
outer 인스턴스에 점을 찍고 new Inner()로 생성한다. outer에서 태어나는 Inner()라는 뜻으로 완전히 outer 인스턴스에 종속적이다.
내부 클래스를 포함한 모든 중첩 클래스는 클래스 간의 긴밀한 관계가 필요한 특별한 경우에만 사용한다. 일반적으로, 내부 클래스나 중첩 클래스를 사용하지 않고 외부에 별도의 클래스를 만들어 사용하는 것도 충분히 가능하다.
하지만 논리적 그룹화를 위해, 캡슐화 관점에서 코드의 가독성과 유지보수를 향상시키기 위해 내부 클래스나 중첩 클래스를 사용하는 것이 유리하다. 이러한 방식은 관련된 클래스들을 함께 묶어 관리할 수 있게 하여 코드의 구조를 더 명확하고 효율적으로 만들 수 있다.
class Outer {
private int value = 1;
class Inner {
private int value = 2;
private void Test() {
int value = 3;
sout(value) // ans = 3
sout(this.value) // ans = 2
sout(Outer.this.value) // ans = 1
}
}
}
외부 클래스와 내부 클래스가 구성되어 있을 때, 동일한 변수명이 존재하면 위의 코드와 같이 해결할 수 있지만, 헷갈리지 않도록 특수한 경우가 아니라면 변수명을 다르게 구성하는 것이 좋다. 이렇게 함으로써 코드의 가독성과 유지보수성을 높이고, 혼동을 방지할 수 있다.