모르지 않다는 것은 아는것과 다르다.

Java

내부 클래스

채마스 2022. 2. 26. 00:33

내부 클래스


내부 클래스란?

  • 클래스 내부에 선언한 클래스로 이 클래스를 감싸고 있는 외부 클래스와 밀접한 연관이 있는 경우가 많다.
  • 다른 외부 클래스에서 사용할 일이 거의 없는 경우에 내부 클래스로 선언해서 사용한다.
  • 중첩 클래스라고도 한다.
  • 내부 클래스의 종류는 아래와 같다.
    • 인스턴스 내부 클래스
    • 정적(static) 내부 클래스
    • 지역(local) 내부 클래스
    • 익명(anonymous) 내부 클래스





인스턴스 내부 클래스

  • 내부적으로 사용할 클래스를 선언한다. -> private 으로 선언하는 것을 권장한다.
  • 외부 클래스가 생성된 후 생성된다. -> 정적 내부 클래스와는 다르다.
  • private 이 아닌 내부 클래스는 다른 외부 클래스에서 생성할 수 있다.

코드 예시

class OutClass {

    private int num = 10;
    private static int sNum = 20;

    private class InClass{

        int inNum = 100;
        //static int sInNum = 200;  //에러 남

        void inTest(){
            System.out.println("OutClass num = " +num + "(외부 클래스의 인스턴스 변수)");
            System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수)");
            System.out.println("InClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수)");
        }

        //static void sTest(){  //에러 남

        //}

    }

    public void usingClass(){
        inClass.inTest(); //내부 클래스 변수를 사용하여 메서드 호출하기
    }
}

public class InnerTest {

    public static void main(String[] args) {
        OutClass outClass = new OutClass();
        outClass.inTest(); // 내부 클래스 메소드 실행
    }

}






정적 내부 클래스

  • 외부 클래스 생성과 무관하게 사용할 수 있다.
  • 정적 변수, 정적 메소드를 사용할 수 있다.

코드 예시

class OutClass {

    private int num = 10;
    private static int sNum = 20;

    static class InStaticClass{

        int inNum = 100;
        static int sInNum = 200;

        void inTest(){   //정적 클래스의 일반 메서드
            //num += 10;    // 외부 클래스의 인스턴스 변수는 사용할 수 없음.
            System.out.println("InStaticClass inNum = " + inNum + "(내부 클래스의 인스턴스 변수 사용)"); 
            System.out.println("InStaticClass sInNum = " + sInNum + "(내부 클래스의 스태틱 변수 사용)");
            System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수 사용)");
        }

        static void sTest(){  // 정적 클래스의 static 메서드
            //num += 10;   // 외부 클래스의 인스턴스 변수는 사용할 수 없음.
            //inNum += 10; // 내부 클래스의 인스턴스 변수는 사용할 수 없음.

            System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수 사용)");
            System.out.println("InStaticClass sInNum = " + sInNum + "(내부 클래스의 스태틱 변수 사용)");

        }
    }    
}

public class InnerTest {

    public static void main(String[] args) {

       ...

        //외부 클래스 생성하지 않고 바로 정적 내부 클래스 생성
        OutClass.InStaticClass sInClass = new OutClass.InStaticClass();  
        sInClass.inTest();
        OutClass.InStaticClass.sTest();
    }

}






지역 내부 클래스

  • 지역 변수와 같이 메소드 내부에서 정의하여 사용하는 클래스이다.
  • 메소드의 호출이 끝나면 메서드에 사용된 지역변수의 유효성은 사라진다.
  • 메소드 호출 이후에도 사용해야 하는 경우가 있기 때문에 지역 내부 클래스에서 사용하는 메소드의 지역변수나 매개변수는 자동으로 final 로 선언된다.

    코드 예시

class Outer{

    int outNum = 100;
    static int sNum = 200;

    Runnable getRunnable(int i){

        int num = 100;

        class MyRunnable implements Runnable{

            int localNum = 10;

            @Override
            public void run() {
                //num = 200;   //에러 남. 지역변수는 상수로 바뀜
                //i = 100;     //에러 남. 매개 변수 역시 지역변수처럼 상수로 바뀜
                System.out.println("i =" + i); 
                System.out.println("num = " +num);  
                System.out.println("localNum = " +localNum);

                System.out.println("outNum = " + outNum + "(외부 클래스 인스턴스 변수)");
                System.out.println("Outter.sNum = " + Outer.sNum + "(외부 클래스 정적 변수)");
                }
            }
         return new MyRunnable();
    }
}

public class LocalInnerTest {

    public static void main(String[] args) {

        Outer out = new Outer();
        Runnable runner = out.getRunnable(10);
        runner.run();
    }
}
  • 위의 코드와 같이 i, num 은 final 이기 때문에 값으 변경 할 수 없다.






익명 내부 클래스

  • 이름이 없는 클래스를 말한다.
  • 클래스의 이름을 생략하고 주로 하나의 인터페이스나 하나의 추상클래스를 구현하여 반환한다.
  • 인터페이스나 추상 클래스 자료형의 변수에 직접 대입하여 클래스를 생성하거나 지역 내부 클래스의 메서드 내부에서 생성하여 반환 할 수 있다. -> 변수 처럼 사용할 수 있다.
  • 위에서 구현한 지역내부 클래스의 경우 MyRunnable 클래스 이름은 실제로 호출할 일이 없다. 그렇기 때문에 아래처럼 익명 내부 클래스로 바꿀 수 있다.
class Outter2{
    int outNum = 100;
    static int sNum = 200;

    Runnable getRunnable(int i){

        int num = 100;

        return new Runnable() {

        @Override
        public void run() {
            @Override
            public void run() {
                //num = 200;   //에러 남. 지역변수는 상수로 바뀜
                //i = 100;     //에러 남. 매개 변수 역시 지역변수처럼 상수로 바뀜
                System.out.println("i =" + i); 
                System.out.println("num = " +num);  
                System.out.println("localNum = " +localNum);

                System.out.println("outNum = " + outNum + "(외부 클래스 인스턴스 변수)");
                System.out.println("Outter.sNum = " + Outer.sNum + "(외부 클래스 정적 변수)");
                }
            }
        };
    }

}

public class AnonymousInnerTest {

    public static void main(String[] args) {
        Outter2 out = new Outter2();

        Runnable runnerble = out.getRunnable(10);
        runnerble.run();        
    }
}
  • 위와 같이 MyRunnable 를 생략하고 바로 return 하는 식으로 바꿀 수 있다. 이러한 방식이 익명 내부클래스이다.
  • 또한 아래와 같은 방법으로도 익명내부 클래스를 구현할 수 있다.
class Outter2{

    ...

    Runnable runner = new Runnable() {

        @Override
        public void run() {
            System.out.println("Runnable 이 구현된 익명 클래스 변수");

        }
    };
}

public class AnonymousInnerTest {

    public static void main(String[] args) {
        Outter2 out = new Outter2();        
        out.runner.run();
    }
}

'Java' 카테고리의 다른 글

상수와 리터럴  (0) 2022.02.26
람다 표현식  (0) 2022.02.26
Thread  (0) 2022.02.26
String,StringBuilder,StringBuffer  (0) 2022.02.26
Stream  (0) 2022.02.26