내부 클래스(Inner Class)라는 것은 클래스 내에 또 다른 클래스를 정의한 것을 의미하며, 중첩 클래스라고도 말합니다.

이러한 내부 클래스는 다른 외부 클래스에서 사용할 일이 거의 없는 경우에 내부 클래스로 선언해서 사용하며, 클래스의 참조 범위를 제한하여 클래스의 이름 충돌 문제 해결과 정보 은닉화를 이룰 수 있습니다.

내부 클래스는 정의되는 위치와 속성에 따라 인스턴스 내부 클래스, 정적(Static) 내부 클래스, 지역(Local) 내부 클래스, 클래스의 이름 없이 동시에 클래스 정의와 생성하는 익명(anonymous) 내부 클래스로 구분됩니다.

인스턴스 내부 클래스

기본적인 인스턴스 내부 클래스 정의와 사용은 다음과 같습니다.

class OuterClass {
	private int num = 10;
	private static int sNum = 20;
	private InnerClass innerClass;
    
    public OuterClass(){
		innerClass = new InnerClass(); // 내부 클래스 생성
	}

    class InnerClass {
    	int inNum = 100;
		//static int sInNum = 200;  //에러 남
		
		void inTest(){
			System.out.println("OuterClass num = " +num + "(외부 클래스의 인스턴스 변수)");
			System.out.println("OuterClass sNum = " + sNum + "(외부 클래스의 스태틱 변수)");
			System.out.println("InnerClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수)");
		}
		
        public void usingClass(){
        	//내부 클래스 변수를 사용하여 메서드 호출하기
			innerClass.inTest(); 
		}

    }
}

public class InnerTest {

	public static void main(String[] args) {
		OuterClass outerClass = new OuterClass();
		System.out.println("외부 클래스 이용하여 내부 클래스 기능 호출");
		outerClass.usingClass();    // 내부 클래스 기능 호출
	    System.out.println();
	    
		OuterClass.InnerClass innerClass = outerClass.new InnerClass();   // 외부 클래스를 이용하여 내부 클래스 생성
		System.out.println("외부 클래스 변수를 이용하여 내부 클래스 생성");
		innerClass.inTest();
	}

}

결과

외부 클래스 이용하여 내부 클래스 기능 호출
OuterClass num = 10(외부 클래스의 인스턴스 변수)
OuterClass sNum = 20(외부 클래스의 스태틱 변수)
InnerClass inNum = 100(내부 클래스의 인스턴스 변수)

외부 클래스 변수를 이용하여 내부 클래스 생성
OuterClass num = 10(외부 클래스의 인스턴스 변수)
OuterClass sNum = 20(외부 클래스의 스태틱 변수)
InnerClass inNum = 100(내부 클래스의 인스턴스 변수)

내부적으로 사용할 클래스를 선언하며, private으로 선언하는 것을 권장합니다.

이 인스턴스 내부 클래스는 외부 클래스가 생성된 후 생성됩니다.

private이 아닌 내부 클래스는 위의 코드와 같이 다른 외부 클래스에서 생성할 수 있습니다.(OuterClass.InnerClass innerClass = outerClass.new InnerClass())

정적 내부 클래스

내부 클래스를 static으로 선언할 수 있습니다.
다음과 같은 구조를 갖습니다.

class OuterClass {
	static class InnerClass {
    	static String str;
        
        InnerClass(String s) { // 생성자
        	str = s;
        }
        
        void print() {
        	staticPrint(str);
        }
        
        static void staticPrint(String s) { // 정적 메서드
        	str = s;
            System.out.println(str);
        }
    }
}

public class StaticInnerClass {
	public static void main(String[] args) {
    	String s = "Static Inner Class...";
        OuterClass.InnerClass p = new OuterClass.InnerClass(s);
        p.print();
        
        OuterClass.InnerClass.staticPrint("call static method");
        p.print();
    }
}

결과

Static Inner Class...
call static method
call static method

위 코드와 같이 외부 클래스 생성과 무관하게 사용할 수 있음을 알 수 있습니다.

그리고 정적 변수와 정적 메서드를 사용합니다.

위의 코드에는 구현하지 않았지만 정적 내부 클래스에서 정적 메서드를 구현할 때, 주의할 점이 존재합니다. 바로 외부 클래스의 인스턴스 변수와 정적 내부 클래스의 인스턴스 변수를 사용할 수 없습니다. 그 이유는 간단하게 인스턴스 생성 전에 호출될 수 있기 때문에 인스턴스 변수를 사용할 수 없습니다.

지역 내부 클래스

지역 변수와 같이 메서드 내부에서 정의하여 사용하는 클래스를 지역 내부 클래스라고 합니다.

간단하게 예를 들면, 아래 코드와 같이 작성할 수 있습니다.

public class LocalInnerClass {
	public static void main(String[] args) {
    	class InnerClass {
        	String str;
            
            InnerClass(String s) {
            	str = s;
            }
            
            void print() {
            	System.out.println(str);
            }
        }
        
        String s = "Local Inner Class...";
        InnerClass p = new InnerClass(s);
        p.print();
    }
}

결과

Local Inner Class...

이 지역 내부 클래스는 메서드의 호출이 끝나면 메서드에 사용된 지역변수의 유효성은 사라집니다. 즉, 지역 내부 클래스도 일반 지역 변수와 같이 정의된 메서드의 범위를 벗어나면 참조를 할 수 없습니다.

메서드 호출 이후에도 사용해야 하는 경우가 있을 수 있으므로 지역 내부 클래스에서 사용하는 메서드의 지역 변수나 매개 변수는 final로 선언됩니다.

익명 내부 클래스

익명 내부 클래스는 객체를 생성하고자 하는 코드 부분에서 직접 객체 생성 루틴과 함께 정의된 클래스를 말하며, 클래스의 이름이 없기 때문에 이름이 없는 클래스, 무명 클래스라고 불립니다.

이 익명 내부 클래스는 한 번만 사용 가능하기 때문에 필요한 객체에 대하여 간단히 정의하여 사용하고자 할 때 이용됩니다.

클래스의 이름을 생략하고 주로 하나의 인터페이스나 하나의 추상 클래스를 구현하여 반환합니다.

바로 아래 코드와 같이 구현할 수 있습니다.

class AnonymousClass {
	public void print() {
    	System.out.println("This is AnonymousClass...");
    }
}
public class AnonymousExample {
	public static void methodA(AnonymousClass obj) {
    	obj.print();
    }
	public static void main(String[] args) {
    	methodA(new AnonymousClass() {
        	public void print() {
            	System.out.println("This is redefined by AnonymousExample....");	
            }
        });
    }
}

결과

This is redefined by AnonymousExample....

위의 코드는 익명 내부 클래스를 사용하여 필요한 클래스를 따로 정의하지 않고, methodA() 메서드의 매개변수로 직접 객체를 생성하여 사용한 것입니다.

특히, 익명 내부 클래스는 상위 클래스의 메서드를 재정의해서 사용하고자 할 때 클래스를 따로 정의하지 않고, 직접 객체 생성과 동시에 메서드를 재정의해서 사용합니다.

주로, 이벤트 처리를 위한 구문에서 많이 사용합니다.

이상으로 자바에서 사용하는 내부 클래스에 대해서 간단히 알아봤습니다.

profile
꾸준함으로 성장하는 개발자 지망생

1개의 댓글