Dico

[Java] 내부 클래스

내부 클래스 Inner class

클래스 내에 선언된 클래스.

외부 클래스를 제외하고는 다른 클래스에서 잘 사용되지 않는 것이어야 한다.

종류

변수의 선언 위치에 따른 종류와 같다.

  • 인스턴스 클래스 instance class

     외부 클래스의 멤버변수 선언 위치에 선언.

     외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용

  • 스태틱 클래스 static class

     외부 클래스의 멤버변수 선언 위치에 선언.

     외부 클래스의 static 멤버, static 메서드에서 사용

  • 지역 클래스 local class

     외부 클래스의 메서드나 초기화 블럭 안에서 선언

     선언된 영역 내부에서만 사용 가능

  • 익명 클래스 anonymous class

     클래스의 선언과 객체의 생성을 동시에 하는 이름 없는 클래스(일회용)

선언

선언 위치에 따른 변수의 유효범위와 접근성을 동일하게 갖는다.

public class Outer{
    class InstanceInner {}
    static class StaticInner {}

    void method(){
        class LocalInner {}
    }
}

제어자와 접근성

엄연한 클래스므로, 제어자와 접근 제어자를 사용할 수 있다.

public class Outer{
    private class InstanceInner {
        int iv = 1;
	// static int cv = 1;              error
        final static int CONSTANT = 1;
    }

    protected static class StaticInner {
        int iv = 2;
	static int cv = 2;
    }

    void method(){
        class LocalInner {
	    int iv = 3;
	    // static int cv = 3;          error
	    final static int CONSTANT = 3;
	}
    }
}
  • 인스턴스 클래스와 스태틱 클래스 

     외부 클래스의 멤버변수와 같은 성질을 갖는다.

     = 인스턴스 멤버와 static 멤버간의 규칙도 똑같이 적용된다.

  • 스태틱 클래스

     내부 클래스 중 유일하게 static 멤버를 가질 수 있다.

     = 내부 클래스에 static 변수를 선언하려면 스태틱 클래스로 선언해야 한다.

  • 지역 클래스

     외부 클래스의 인스턴스 멤버와 static 멤버를 모두 사용할 수 있다.

     외부 클래스의 지역변수(지역 클래스가 포함된 메서드에 정의된 지역변수)는 final이 붙은 지역 변수(상수)만 접근 가능하다.

     JDK1.8부터 지역 클래스에 접근하는 지역 변수는 자동으로 final이 붙는다.

  • 상수(final과 static이 동시에 붙은 변수)는 모든 내부 클래스에서 정의 가능하다.
public class Outer{
    class InstanceInner { }
    static class StaticInner { }

    InstanceInner iv = new InstanceInner();
    static StaticInner cv = new StaticInner();

    static void staticMethod(){
        // InstanceInner obj1 = new InstanceInner();       error
	Outer out = new Outer();
        InstanceInner obj1 = out.new InstanceInner();
	StaticInner obj2 = new StaticInner();
    }

    void instanceMethod(){
        InstanceInner obj1 = new InstanceInner();
	StaticInner obj2 = new StaticInner();
	// LocalInner lv = new LocalInner();               error
    }

    void method(){
        class LocalInner { }
	LocalInner lv = new LocalInner();
    }
}
public class Outer{
    private int outIv = 0;
    static int outCv = 0;

    class InstanceInner {
        int iv1 = outIv;
	int iv2 = outCv;
    }

    static class StaticInner {
        // int iv = outIv;         error
	static int cv = outCv;
    }

    void method(){
        int lv = 0;
	final int LV = 0;

        class LocalInner {
	    int iv1 = outIv;
	    int iv2 = outCv;
   	    // int iv3 = lv;       JDK1.8 이전: error
	    int iv4 = LV;
	}
    }
}
  • 외부 클래스가 아닌 다른 클래스에서 내부 클래스에 접근할 경우

     내부 클래스로 선언해서는 안 되는 클래스를 내부 클래스로 선언했다는 의미.

public class Outer{
    class InstanceInner { }
    static class StaticInner { }
}

public class Test {
    public static void main(String[] args) {
        Outer out = new Outer();

	Outer.InstanceInner ii = out.new InstanceInner();
	Outer.StaticInner si = new Outer.StaticInner();
    }
}
  • 내부 클래스와 외부 클래스에 선언된 변수의 이름이 같을 때

     변수 앞에 'this' 또는 '외부 클래스.this'를 붙여서 서로 구별할 수 있다.

public class Outer{
    int iv = 0;

    class InstanceInner {
        int iv = 1;

        void method(){
	    int iv = 3;

	    System.out.println("iv: " + iv);                        // 3
       	    System.out.println("this.iv: " + this.iv);              // 1
	    System.out.println("Outer.this.iv: " + Outer.this.iv);  // 0
	}
    } 
}

익명 클래스 Anonymous class

클래스의 선언과 객체의 생성을 동시에 한다.

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

오로지 단 하나의 클래스를 상속 받거나 단 하나의 인터페이스만을 구현할 수 있다.

new ClassName() {
    void method() {}
}

new InterfaceName() {
    void method() {}
}
public class Anony {
    Object iv = new Object() { 
        void method(); 
    };

    static Object cv = new Object() { 
        void method(); 
    };

    void method(){
        Object lv = new Object() { 
            void method(); 
        };
    }
}

참고 서적: 자바의 정석 3판