[JAVA] 객체 지향 프로그래밍 Ⅱ 정리 - (6) : 내부 클래스

DongGyu Jung·2022년 1월 28일
0

자바(JAVA)

목록 보기
18/60
post-thumbnail

🏃‍♂️ 들어가기 앞서..

본 게시물은 스터디 활동 중에 작성한 게시물로 자바의 정석-기초편 교재를 학습하여 정리하는 글입니다.
※ 스터디 Page : 〔투 비 마스터 : 자바〕

*해당 교재의 목차 순서와 구성을 참고하여 작성하며
각 내용마다 부족할 수 있는 내용이나 개인적으로 궁금한 점은
추가적인 검색을 통해 채워나갈 예정입니다.



🔗 내부 클래스 (inner class)

" 클래스 안의 클래스 "

말 그대로
클래스 안에
내부에 선언된 클래스인 것이다.

이런 구조로 선언하는 이유는
두 클래스가 서로 매우 긴밀한 관계에 있기 때문이다.

※ 구조

class A {
    ...
}
class B {
    ...
}

// ▼▼▼▼▼▼▼
class A { // 'B'의 외부 클래스
    ...
    // 클래스 A의 멤버같은 긴밀함
    class B { // 'A'의 내부 클래스
        ...
    }
    ...
}

👍 장점

  • 《내부 클래스》에서 외부 클래스 멤버 들을 쉽게 접근할 수 있다.
    : 객체를 별도로 만들 필요도 없이 외부 클래스의 멤버를 사용할 수 있다.

  • 코드 복잡성 감소 ( " 캡슐화 " )

class A {
    int i = 100 ;
    B b = new B() ;
}
class B {
    void method() {
        A a = new A() ;
        System.out.println(a.i) ;
    }
}
class C {
    B b = new B() ; // 문제 없음
}

public class Test {
    public static void main(String[] args) {
        B b = new B();
        b.method(); // -> 100출력
    }
}

/*
▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
*/

class A { 
    int i = 100 ;
    B b = new B() ;
    
    class B {
        void method() {
        // 객체 생성없이 멤버 변수 사용 가능
            System.out.println(i) ;
        }
    }
}
/*
class C {
    B b = new B() ; 
    // Error : B클래스는 A 내부에서만 사용 가능함
}
*/

❗ 종류 & 특징

"변수" 의 〔 선언위치에 따른 종류 〕와 같다.
( 유효범위(scope) & 접근성(accessibility) 동일 )

종류특징
인스턴스 내부클래스
(instance class)
<외부 클래스 멤버변수 선언위치>
" 인스턴스 변수 "처럼 다루어지고
외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용
스태틱 내부클래스
(static class)
<외부 클래스 멤버변수 선언위치>
" static 변수 "처럼 다루어지고
주로 static 메서드에서 사용될 목적으로 선언됨
지역 내부클래스
(local class)
<외부클래스 메서드 / 초기화블럭 내부>
선언된 영역 내부에서만 사용 가능
익명 내부클래스
(Anonymous class)
클래스 선언 & 객체 생성 을 동시에 하는
" 이름없는 클래스 (일회용) "


🔩 제어자 & 접근성

내부클래스
엄연한 " 클래스 "이기 때문에
<abstractfinal>과 같은 제어자를 사용할 수 있고

멤버변수들처럼
<private / protected / public / (default)> 같은 " 접근제어자도 사용할 수 있다.

" 인스턴스(instance) 내부 클래스 "와
" 스태틱(static) 내부 클래스 "는

" 외부클래스의 멤버변수 (인스턴스 변수 & 클래스 변수) "와 같은 위치에 선언된다.
( 성질 또한 멤버변수와 같은 성질을 가짐. )

《 짚고 넘어가자! 》
인스턴스 멤버static 멤버 사용은 가능하지만
static 멤버인스턴스 멤버 사용하는 것은 불가능하다.

💕 클래스간 접근성

class Test_2 {
    class InstanceInner {} // 인스턴스 내부 클래스
    static class StaticInner {} // static 내부 클래스
    
    // 인스턴스 멤버끼리 " 직접접근 " 가능
    InstanceInner iv = new InstanceInner() ; 
    // static 멤버끼리 " 직접접근 " 가능
    StaticInner cv = new StaticInner() ;
    
    
    /*
    같은 클래스 내에서의 각 내부 클래스 간 접근성
    */
    // static 멤버의 " 인스턴스 멤버 직접접근 " 불가능
    static void staticM() {
//      InstanceInner obj1 = new InstanceInner() ;
        StaticInner obj2 = new StaticInner() ;
        
        // 단, 외부 클래스(Test_2) 인스턴스 생성하면 가능
        // (실제로 이렇게까지 쓰지말자...ㅎㅎ)
        Test_2 outer =  new Test_2() ;
        InstanceInner obj1 = new InstanceInner() ;
    }
    
    
    // 인스턴스 멤버의 " static 멤버 직접접근 " 가능
    void instanceM() {
        InstanceInner obj1 = new InstanceInner() ;
        StaticInner obj2 = new StaticInner() ;
        // 당연히 지역 내부 클래스는 외부에서 접근 불가
//      LocalInner lv = new LocalInner();        
    }
    
    
    void myM() {
        class LocalInner {} // 지역 내부 클래스 생성
        LocalInner lv = new LocalInner();
    }
}

※ 【스태틱】 내부 클래스

class Test_2 {
    class InstanceInner { // 인스턴스 내부 클래스
        int iv = 100 ; // 인스턴스 변수 
//      static int cv = 100 ; // static 변수 선언 불가 _ static 클래스에서만 선언가능
        final static int Const = 100 ; // 상수(constant)는 선언 가능
    }
    
    static class StaticInner { // static 내부 클래스
        int iv = 200 ; 
        static int cv = 200 ; // static 메서드라 가능
    }
    
    void method() {
        class LocalInner { // 지역 내부 클래스
            int iv = 300 ;
//          static int cv = 300 ; // static 변수 선언 불가 _ static 클래스에서만 선언가능
	    final static int Const = 100 ; // 상수(constant)는 선언 가능
        }
    public static void main(String[] args) {
        System.out.println(InstanceInner.CONST) ;
        System.out.println(StaticInner.cv) ;
//      System.out.println(LocalInner.CONST) ; 은 불가능
}

<출력>
100
200

내부 클래스 中
당연히
< static클래스 >만 " static 멤버 " 를 가질 수 있고
static 내부 클래스에서는
" 외부클래스의 인스턴스 멤버에 접근할 수 없다. "

드물지만
static 변수를 선언해야한다면
스태틱 클래스로 선언해야 한다.

( ★ " final static 붙은 변수 " ▶ 상수(constant) _ 모든 내부 클래스에서 정의 가능 )

❗ 틈새 꿀팁 ❗

[ 상수 선언 ]

  • ~마다 다르지만 정해진 후바뀌면 안된다. = final
  • 처음부터 절대적으로 설정 = final static

🧲 "외부 클래스 멤버" 접근성

class Outer {
    private int outerIv = 0 ; // private변수
    static int outerCv = 0 ; // static 변수
    
    /* 인스턴스 내부 클래스 */
    class InstanceInner { 
        int iiv = outerIv ; // 외부클래스 <private멤버> " 접근 가능 "
        int iiv2 = outerCv ; // 외부클래스 static멤버 " 접근 가능 "
    }
    
    /* static 내부 클래스 */
    static class StaticInner { 
//      int siv = outerIv ; // 외부클래스 <인스턴스 멤버> " 접근 불가 "
        static int siv2 = outerCv ; // 외부클래스 static멤버 " 접근 가능 "
    }
    
    
    /* 메서드 내 [지역 내부 클래스] */
    void myM() {
        int lv = 0 ; //지역변수 -> 메서드 종료 소멸
        final int LV = 0 ; // (JDK1.8부터 final 생략 가능하대요~
        
        /*
        메서드는 종료되서 소멸되는데
        오히려 지역 클래스 객체가 더 오래 존재하는 경우 발생 가능
        
        그래서 lv(지역변수) 사용 못하게끔 하는 것
        */
        class LocalInner { // 지역 내부 클래스
            int liv = outerIv ;
            int liv2 = outerCv ;
            
            
            //외부 클래스의 지역변수는 "상수" 만 접근 가능
//          int liv3 = lv ; // 에러!
	    int liv4 = LV ; // 상수 접근 _ 문제 x

<< JDK1.8 부터 >>

/*
final이 생략이 가능해졌는데
       
그렇다면 일반 "변수"인 것인데
' 값이 안바뀌는 변수일 경우 ' 에러가 발생하지 않는다.
*/
class LocalInner { // 지역 내부 클래스
    int liv = outerIv ;
    int liv2 = outerCv ;
          
          
     //외부 클래스의 지역변수는 "상수" 만 접근 가능
    int liv3 = lv ; // lv 접근 가능!!
    int liv4 = LV ; // 상수 접근 _ 문제 x
       
/* 
만약 값이 바뀌게 되면 
바로
컴파일 에러가 발생!!!
*/

🎭 "다른 클래스에서의" 내부 클래스 접근

사실 이런경우는
매우 드물다

" 타 클래스에서 내부 클래스에 접근한다 "
이것은
" 내부 클래스로 선언 자체를 하면 안됐었던 것 "이기 때문에

그저 참고용이다.

/* 내부 클래스를 가진 (외부)클래스 */
class Outer {
    class InstanceInner { 
        int iv = 100 ;
    }
    
    static class StaticInner {
        int iv = 200 ; 
        static int cv = 300 ; 
    }
    
    void method() {
        class LocalInner { // 지역 내부 클래스
            int iv = 300 ;
        }
    }
}


class Test_3 {
    public static void main(String[] args) {
    // (외부)클래스 인스턴스 먼저 생성해야 한다
        Outer oc = new Outer() ; 
        Outer.InstanceInner iI = oc.new InstanceInner(); // .을 통해 인스턴스 생성 가능
        
    /* 
    static 내부 클래스의 인스턴스는 
    (외부)클래스 먼저 생성하지 않아도 된다.
    
    대신 Outer.~ 형식으로 접근
    */
        Outer.StaticInner sI = new Outer.StaticInner() ;
        
         System.out.println("iI.iv : " + iI.iv) ;
         System.out.println("Outer.StaticInner.cv : " + Outer.StaticInner.cv) ;
         System.out.println("sI.iv : " + sI.iv) ;
    }
}

<출력>
iI.iv : 100
Outer.StaticInner.cv : 300
sI.iv : 200

💥 변수 이름 충돌 (외부 - 내부)

우리가 지겹도록 공부했던
그것(?)이 생각날 것이다.

이름이 같을 때,
구분할 수 있는 방법은?

바로
" this " 이다.

아 그리고 하나 더,
" (외부 클래스명).this " 형식도 가능하다.

class Outer {
    int value = 100 ;
    
    class Inner {
        int value = 200 ; 
	
        void method() {
            int value = 300 ;
            
            // 현재 메서드 내 지역 변수 (lv)
            System.out.println("value : " + value) ;
            // 현 인스턴스(Inner class) 변수 (Inner iv)
            System.out.println("this.value : " + this.value) ;
            // 현 외부 클래스 변수 (Outer iv)
            System.out.println("Outer.this.value : " + Outer.this.value) ;
        }
    }
}

0개의 댓글