개발 공부/Java & Spring

6. 내부 클래스(inner class)

빵다희 2022. 11. 6. 23:26
내부 클래스
- 클래스 내에 선언된 클래스이다.
- 내부, 외부 클래스의 멤버들 간에 서로 쉽게 접근할 수 있다.
- 외부에 불필요한 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다(캡슐화)
/* 서로 독립적인 A, B 클래스 */
class A {
  ...
}

class B {
  ...
}​

/* B를 A의 내부클래스로, A를 B를 감싸고 있는 외부클래스로 변경 */
class A {    // 외부 클래스 
  ... 
  class B {  // 내부
    ...
  }
}

 

내부 클래스의 종류와 특징
- 내부클래스는 마치 변수를 선언하는 것과 같은 위치에 선언할 수 있으며, 선언 위치에 따라 종류가 나뉘어진다.
- 내부 클래스의 유효범위와 성질이 같은 위치에 선언된 변수와 유사하다. (ex. 인스턴스 클래스 - 인스턴스 변수)
내부 클래스 특징
인스턴스 클래스 (instance class) 외부 클래스의 멤버변수 선언 위치에 선언. 인스턴스멤버처럼 사용된다.
주로 외부클래스의 인스턴스멤버들과 관련된 작업에 사용됨.
스테틱 클래스 (static class) 외부 클래스의 멤버변수 선언 위치에 선언. static 멤버처럼 사용된다.
주로 외부클래스의 static멤버, 특히 static메서드에서 사용될 목적으로 선언된다. 
지역 클래스 (local class) 외부 클래스의 메서드나 초기화 블럭안에 선언. 선언된 영역 내부에서만 사용됨.
익명 클래스 (anonymous class) 클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스(일회용)

 

내부 클래스의 선언, 제어자와 접근성
- 변수가 선언된 위치에 따라 인스턴스 변수, 클래스변수(static변수), 지역변수로 나뉘듯이 내부 클래스도 이와 마찬가지로 선언된 위치에 따라 나뉜다. 
- 각 내부 클래스 선언위치에 따라 같은 선언위치의 변수와 동일한 유효범위와 접근성을 갖는다.
- 외부 클래스에 선언된 내부클래스는 외부 클래스의 멤버와 같이 간주되고, 인스턴스멤버와 static멤버 간의 규칙이 내부클래스에도 동일하게 적용된다. 
-  내부 클래스도 abstract, final 등 제어자를 사용할 수 있을 뿐만 아니라, 멤버변수들처럼 private, protected과 같은 접근제어자도 사용 가능하다.
/* 위치별로 선언한 변수들  */
class Outer {
 private int iv = 0;          // 인스턴스변수
 protected static int cv = 0; // static변수
 
 void myMethod() {    
   int lv = 0;                // 지역변수
 }
}

/* 위치별로 선언한 내부 클래스들 */
class Outer {
 private class InstanceInner {}        // 인스턴스 클래스
 protected static class StaticInner {} // static 클래스
 
 void myMethod() {
   class LocalInner {}                 // 지역 클래스 
 }
}

※ 내부 클래스 중에서 Static클래스만 Static멤버를 가질 수 있다. 다만 final과 static이 동시에 붙은 변수는 상수이므로 내부클래스의 종류에 상관없이 모든 내부클래스에서 정의가 가능하다.

 

* 내부 클래스에서 외부 클래스의 변수들에 대한 접근성을 보여주는 예제

class InnerEx {
 private int outerIv = 0;    // 외부 인스턴스 멤버 선언
 static  int outerCv = 0;    // 외부 static 멤버 선언
 
 class InstanceInner {       // 인스턴스클래스 선언
   int iiv = outerIv;        // 외부 클래스의 private멤버도 접근가능하다.
   int iiv2 = outerCv;
 }     
 
 static class StaticInner {  // Static클래스 선언
 //  int siv = outerIv;  -- static 클래스는 외부 클래스의 인스턴스멤버에 접근할 수 없다.
   static int scv = outerCv;
 } 
 
 // 인스턴스멤버 간에는 서로 직접 접근이 가능하다.
 InstanceInner iv = new InstanceInner();
 // static 멤버 간에는 서로 접근이 가능하다. 
 static StaticInner cv = new StaticInner();
 
 static void staticMethod() {
 // InstanceInner obj1 = new InstanceInner(); -- static멤버는 인스턴스멤버에 직접 접근할 수 없다.
   StaticInner obj2 = new StaticInner();
 
 // 객체를 만든후에는 인스턴스변수에 접근가능.
   InnerEx outer = new InnerEx();
   InstanceInner obj1 = outer.new InstanceInner();
 
 }
 
 void instanceMethod() {
 // 인스턴스메소드에서는 인스턴스멤버와 static멤버 모두 접근 가능하다.
   InstanceInner obj1 = new InstanceInner();
   StaticInner obj2 = new StaticInner();
   
 // Localinner lvc = new Localinner(); -- 메서드 내에서 지역적으로 선언된 내부 클래슨는 외부에서 접근 할 수 없다.
 }
 
 void myMethod() {
   int lv = 0;
   final int LV = 0; // JDK1.8부터 final 생략 가능
   
   class LocalInner {
     int liv = outerIv;
     int liv2 = outerCv;
     // 외부 클래스의 지역변수는 final이 붙은 변수(상수)만 접근가능하다.
     // int liv3 = lv;  -- 에러발생 (JDK1.8부터 에러가 아니다.)
     int liv4 = LV;    // 상수 접근 가능.
   }
   LocalInner lvc = new LocalInner();  // 선언 뒤 생성은 외부에서 접근 가능
   
 }
 
}

class Main {
  public static void main(String[] args) {
   // 인스턴스 클래스의 인스턴스를 생성하려면 외부클래스의 인스턴스를 먼저 생성해야 한다.
   InnerEx oc = new InnerEx();
   InnerEx.InstanceInner ii = oc.new InstanceInner();
   
   // 스태틱 내부 클래스의 인스턴스는 외부 클래스를 먼저 생성하지 않아도 된다. 
   InnerEx.StaticInner si = new InnerEx.StaticInner();
  }
}
  • 인스턴스 클래스는 외부클래스의 인스턴스멤버를 객체생성 없이 바로 사용가능하지만, 스테틱클래스는 외부 클래스의 인스턴스 멤버를 객체생성 없이 사용할 수 없다.
  • 인스턴스클래스는 스태틱클래스의 멤버들을 객체생성없이 사용할 수 있지만, 스태틱 클래스에서는 인스턴스클래스의 멤버들르 객체생성없이 사용할 수 없다. 
  • 인스턴스클래스는 외부 클래스의 인스턴스멤버이기 때문에 인스턴스변수를 모두 사용할 수 있다. 접근제어자가 private이어도 상관없다.
  • 스태틱클래스는 외부 클래스의 static멤버만 사용할 수 있다. 
  • 지역클래스는 외부 클래스의 인스턴스멤버와 static멤버를 모두 사용할 수 있으며, 지역클래스가 포함된 메서드에 정의된 지역변수도 사용할 수 있다. 
  • 단, final이 붙은 지역변수만 접근 가능하는데 이유는 메서드가 수행을 마쳐서 지역변수가 소멸된 시점에도, 지역클래스의 인스턴스가 소멸될 지역변수를 참조하려는 경우가 발생할 수 있기때문이다.(JDK1.8부터는 컴파일러가 자동으로 final을 붙이기때문에 생략해도 된다.)
  • 내부클래스의 객체를 만들때, 인스턴스의 경우는 외부 클래스의 객체를 먼저 생성한 후에 가능하지만 스태틱 내부 클래스는 먼저 생성하지않아도 된다. 
내부 클래스의 파일명
- 내부 클래스를 컴파일 했을 때 생성되는 파일명은 '외부 클래스명$내부 클래스명.class' 형식으로 되어 있다.
- 지역 내부 클래스는 다른 메서드에 같은 이름의 내부 클래스가 존재할 수 있기 때문에 내부클래스명 앞에 숫자가 붙는다.
class Outer {
 int value = 10;        // Outer.this.value
 
 class Inner {
   int value = 20;      // this.value
 }
 
 void myMethod() {
   class LocalInner {}
 }
 
 void myMethod2() {
   class LocalInner {}
 }
}

/* 컴파일 */
/*
Outer.class
Outer$Inner.class
Outer$1LocalInner.class 
Outer$2LocalInner.class
*/

※ 내부 클래스와 외부 클래스에 선언된 변수의 이름이 같을 때 변수 앞에 'this' 또는 '외부 클래스명.this'를 붙여서 서로 구별할 수 있다.

 

익명 클래스
- 이름이 없고 클래스의 선언과 객체의 생성을 동시에 한다.
- 단 한번만 사용될 수 있으며 오직 하나의 객체만을 생성할 수 있는 일회용 클래스이다. 
- 이름이 없기 때문에 생성자도 가질 수 없다.
- 단 하나의 클래스를 상속받거나 단 하나의 인터페이스만을 구현할 수 있다. 
- 익명 클래스는 '외부 클래스명$숫자.class'의 형식으로 클래스파일명이 결정된다. 
/* 익명클래스 생성*/
new 조상클래스이름() {
  // 멤버 선언
}

또는 

new 구현인터페이스이름() {
  // 멤버 선언
}

class AnonyEx {
 Object iv = new Object(){ void method(){} };       // 익명 클래스
 static Object cv = new Object(){void method(){} }; // 익명 클래스 
 
 void myMethod() {
   Object lv = new Object(){ void method(){} } ;    // 익명 클래스
 }
}

 

728x90
반응형

'개발 공부 > Java & Spring' 카테고리의 다른 글

8. StringBuffer 클래스와 StringBuilder 클래스  (0) 2022.12.06
7. 예외처리(exception handling)  (0) 2022.11.12
5. 제어자(modifier)  (0) 2022.10.24
4. package와 import  (0) 2022.10.23
3. 오버로딩(overloading)  (0) 2022.09.28