본문 바로가기
Programming/Java

[자바/Java] Inner 클래스

by 코딩하는 랄로 2023. 10. 14.
728x90

Inner Class 란?

Inner Class란, 클래스나 인터페이스 내부에서 선언되는 클래스를 이너 클래스라고 한다. 이너 클래스를 사용할 시 다음과 같은 이점을 가지고 있다.

 

  • 이너 클래스는 외부 클래스의 모든 멤버(변수, 메소드 등)에 접급할 수 있다는 관련성의 특별한 타입을 나타냄
  • 이너 클래스는 논리 그룹 클래스와 인터페이스 내부에 있기 때문에 더 읽기 쉽고, 유지 가능한 코드 개발에 사용
  • 적시 적소에 쓰면 불필요한 클래스의 낭비가 줄어든다. (1번 사용되는 클래스를 위해, 클래스 파일을 생성할 필요X)

 

이너 클래스는, 클래스 내부에서 어디에, 어떻게 선언되냐에 따라 여러 종류로 나뉜다.

 

  • Member Inner Class : 다른 클래스 내부에서 선언된 클래스
  • Static Ineer Class : 다른 클래스의 내부에서 static으로 선언된 클래스
  • Local Class
    • Local Inner Class : 메소드 내부에서 선언된 클래스
    • Anonymous Inner Class : 이름이 없는 local class

 

 

 

멤버 내부 클래스 (Memeber Inner Class)

멤버 내부 클래스는 외부 클래스의 객체가 사용된 이후에야 내부 클래스의 객체를 생성이 가능하다. 또한, 멤버 내부 클래스는 외부 클래스의 모든 멤버들(private 포함)을 사용할 수 있다.

 

// 클래스: 멤버 변수들 (+ 생성자들) + 멤버 메소드들 = 데이터 타입
public class Outer {
    // 멤버 변수:
    private int outerValue;
    
    // 생성자:
    public Outer() {}
    public Outer(int outerValue) {
       this.outerValue = outerValue;
    }
    
    // 메소드:
    public int getValue() {
       return outerValue;
    }
    public void setValue(int outerValue) {
       this.outerValue = outerValue;
    }

    // Member Inner class
    public class Inner {
       private int innerValue;
       public Inner(){}
       public Inner(int val) {this.innerValue = val;}

       public void printOuterValue(){
          System.out.println("outerValue = " + outerValue);
          // 멤버 내부 클래스는 외부 클래스의 멤버를 직접 접근 가능
       }

       public void printInnerValue(){
          System.out.println("innerValue = " + innerValue);
       }
    } 
}

 

위의 Outer 외부 클래스와 Inner 내부 클래스를 이용하여 객체를 생성하여 보겠다.

 

public class Main {
    public static void main(String[] args) {
       // 외부 클래스의 인스턴스 생성
       Outer out = new Outer(100);

       // 멤버 내부 클래스의 인스턴스 생성
       // 멤버 내부 클래스의 이름: [외부클래스 이름].[멤버 내부클래스 이름]
       // [외부클래스 이름].[내부클래스 이름] 참조변수 =
       //      [외부클래스 인스턴스].new 내부클래스 생성자();
       Outer.Inner in = out.new Inner(111);

       in.printOuterValue();
       in.printInnerValue();

       // 하나의 외부 클래스 인스턴스를 이용해서
       // 멤버 내부 클래스의 인스턴스는 여러개를 생성할 수 있다.
       Outer.Inner in2 = out.new Inner(123);
       in2.printOuterValue();
       in2.printInnerValue();

       Outer.Inner in3 = new Outer(30).new Inner(400);

    }
}

 

 

 

정적 내부 클래스 (Static Inner Class)

정적 내부 클래스는 중첩 클래스(Nested Class)라고도 하며, 다른 클래스의 내부에서 멤버로 정의된 클래스인데 static 키워드가 사용된 내부 클래스를 일컫는다.

 

static 키워드를 사용하여 멤버를 선언하면 클래스의 인스턴스가 생성되지 않아도 해당 멤버를 사용할 수 있다. 따라서, 중첩 클래스는 외부 클래스의 객체를 생성하지 않고 내부 클래스의 객체를 생성할 수 있다.

 

또한, 멤버 내부 클래스와는 달리 외부 클래스의 static으로 선언된 변수와 메소드만 사용가능하다.

 

public class Outer {

    // 멤버변수
    private int value;  // 인스턴스 변수
    private static int count = 100; // 클래스 변수 (static)
    
    // 생성자
    public Outer(int value) {
       this.value = value;
    }

    public static class Nested {
       public void displayInfo(){
          // System.out.println(value);
          // static 은 non-static 사용못함
          System.out.println(count);
       }

       public static void println(){}
    }
    
}

 

위의 Outer 외부 클래스와 중첩클래스를 이용하여 객체를 생성하여 보겠다.

 

public class Main {

    public static void main(String[] args) {
       // [외부클래스 이름].[내부클래스 이름] = new [외부클래스 이름].생성자()
       Outer.Nested nest1 = new Outer.Nested();
       nest1.displayInfo();

       Outer.Nested.println();
       
    }

    public class TestInner {

    }

}

 

 

 

지역 내부 클래스 (Local Inner Class)

지역 내부 클래스는, 메소드 내에서 선언된 클래스로 메소내에서만 동작하는 사용(인스턴스 생성, 참조변수 선언) 가능한 클래스이다.

 

이외에도, 접근 수식어(public, protected, private)은 사용할 수 없다. 외부 클래스의 멤버변수는 모두 사용가능하다. 또한, 메소드 내부에 선언된 지역 변수 또는 매개변수도 사용 가능하지만 해당 변수가 effectively final 변수여야 한다.

 

Effectively final 변수
final로서 선언된 변수 또는 한번 초기화가 된 이후로 값이 변경되지 않는 변수

 

public class Outer {
    // 멤버 변수
    private int num1 = 100;
    
    // 멤버 메소드
    public void localMethod() {

       int num2 = 200;   // localMethod() 의 지역변수

       // 메소드 localMethod() 내부에서 정의된 Local inner class
       class Local {
          private int num3 = 300;

          public void showNumbers() {
             System.out.println("num1 = " + num1);
             // effectively final 변수가 아니므로 사용X
             // System.out.println("num2 = " + num2);
             System.out.println("num3 = " + num3);
          }

       } 
       
       Local local = new Local();
       local.showNumbers();
       
    } 
}

 

 

지역 내부 클래스가 선언된 메소드 사용

 

public class Main {

    public static void main(String[] args) {

       Outer out = new Outer();
       out.localMethod();
       
    } 
}

 

 

 

익명 내부 클래스 (Anonymous Inner Class)

익명 내부 클래스는 이름이 없는 지역 내부 클래스이다. 이름이 없기 때문에 생성자로 객체를 생성할 수가 없다. 그렇기 때문에 클래스의 정의와 동시에 객체를 생성한다.

 

new 인터페이스() { 익명 클래스 내부 작성 };
new 부모클래스() { 익명 클래스 내부 작성 };

 

익명 내부 클래스는 일반 클래스, 추상 클래스, 인터페이스 모두 가능하다. 

 

interface MyReadable {
	void readInfo();
}

abstract class MyAbstract{

    int n = 10;
    int m = 2;

    abstract int methodB(int k);

} 

class MyClass {

    int a = 10;
    int b = 20;

    int methodA(int c) {
       return a + b + c;
    }

} 

public class Main {

    public static void main(String[] args) {
       
       MyReadable r = new MyReadable() {

          int a = 7;  // 익명클래스의 멤버변수(필드) 추가

          @Override
          public void readInfo() {
             System.out.println("readInfo() 호출");
             System.out.println(a);
          }
       };

       r.readInfo();
       System.out.println(r);

       int n = 12;   // effective final 은 사용 가능.

       new MyReadable() {
          int a = 200;
          @Override
          public void readInfo() {
             System.out.println(a + 100 + n);
          }
       }.readInfo();
       
       
       System.out.println(new MyClass().methodA(30));

       System.out.println(new MyClass(){
          int d = 400;

          @Override
          int methodA(int c) {
             return super.methodA(c) + d;
          }
       }.methodA(30));


       int result = new MyAbstract(){
          @Override
          int methodB(int k) {
             return n + m + k;
          }
       }.methodB(100);
       System.out.println("result = " + result);
       
    } 
}

 

익명 클래스를 통해 생성한 객체는 메소드를 통해서 반환하는 것도 가능하다.

 

public class Person {
	// 외부 클래스 멤버변수
	private String name;
	
	// 외부 클래스 생성자
	public Person(String name) {
		this.name = name;
	}

	public MyReadable createInstance(int age) {

		// MyReadable 을 implement 한 이름없는 익명 class 를 정의하고 그 instance 생성
		// 익명클래스 구문 사용!!
		return new MyReadable() {
			@Override
			public void readInfo() {
				System.out.println("이름: " + name);
				System.out.println("나이: " + age);
			}
		};
	}	
}

interface MyReadable {
	void readInfo();
}

 

사용 예시

public class Main {

    public static void main(String[] args) {
		
	    Person p = new Person("홍길동");
	    MyReadable r = p.createInstance(29);
	    r.readInfo();
	    //홍길동
	    //29
    }
}

 

728x90