본문 바로가기

Java/Java

(JAVA) 내부클래스의 종류 및 사용법

내부 클래스를 왜 사용하는가?

1. 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.

2. 서로 관련있는 클래스를 한 곳에 묶음으써, 한 클래스에 같은 책임을 한데 묶을 수 있다.

단, 작성한 내부 클래스는 재사용이 힘드므로 다른 클래스에서 참조하지 않는 것을 클래스로 둔다.


내부 클래스의 유형

1. 인스턴스 내부 클래스

외부 클래스를 먼저 만든 후 내부클래스 생성

2. 정적 내부 클래스

외부 클래스와 무관하게 생성

3. 지역 내부 클래스

메서드를 호출할때 생성

4. 익명 내부 클래스

메서드를 호출할 때 생성되거나, 인터페이스 타입 변수에 대입할 때 new 예약어를 사용하여 생성


인스턴스 내부 클래스

인스턴스 내부 클래스는 어떻게 생성되는가?

InnerClassExample 하위에 InnerClass라는 내부 클래스 생성

public class InnerClassExample {
    
    //내부클래스
    public class InnerClass {
        public InnerClass() {
            System.out.println("innerclass constructor");
        }


    }
}
class Main {

    public Main() {
       
    }
}

bin 폴더 하위에 생성

[상위 클래스]$[내부 클래스]. class로 생성된다. 

 

호출방법1

외부 클래스 outerclass = new 외부 클래스();

외부 클래스. 내부 클래스 inner = outerclass.new 내부 클래스();

이렇게 호출하는 이유는 내부 클래스는 static이 아니므로 클래스에서 클래스로 직접적인 접근을 할 수 없기 때문이다.

호출 방법 2

외부 클래스에서 내부 클래스에 대한 참조 변수를 선언

private InnerClass innerClass;

선언하였던 참조변수를 통해서 내부 변수를 호출

public void UsingInnerClass() {
innerClass.InMethod();
}

호출

outerclass.UsingInnerClass();

클래스와 호출할 메서드가 static으로 선언되어있다면 이렇게 선언하지 않아도 된다. 단지 직접적으로 클래스를 참조할 수 없기 때문에 우회하는 방법이라고 생각하자!


정적 내부 클래스

: 외부 클래스 내부에 static으로 내부 클래스를 선언

예시

public class InnerClassExample {


    //정적내부클래스
    
    static class StaticInnerClass{
		//일반 메서드
        void innerClass() {

        }
		//static 메서드
        static void innerStaticClass() {

        }
    }
}

 

호출 방법

//객가 생성되어야지만 호출할 수 있기 때문에체 객체 생성후 메서드를 호출한다.
InnerClassExample.StaticInnerClass staticInnerClass = new InnerClassExample.StaticInnerClass();
staticInnerClass.innerClass();


//메서드가 static일 떄
InnerClassExample.StaticInnerClass.innerStaticClass();

static 클래스 내부에 static메서드와 non-static 메서들 구분하여 사용하자.

non-static 메서드는 객체가 생성된 후 heap에 메모리가 생성된 이후에 호출이 가능하기 때문에(static과 생명주기가 다르기 때문)

new예약어를 선언 후 호출 가능하다.

static 메서드는 static과 같은 데이터 영역에 선언되어있고 생명주기 또한 같으므로 직접적으로 "."을 이용해서 호출이 가능하다.


지역 내부 클래스

지역 내부 클래스를 설명하기 이전에 Runnable interface에 대해서 살펴보자.

Runnable interface의 내부는 이렇다.

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

하나의 추상 메서드를 구현한다고 강제되어있다.

 

Outer 클래스 내부에 MyRunnerble이라는 내부 클래스를 생성해서 Runnable인터페이스를 구현하는 메서드를 만들어보자

class Outer {


    Runnable gerRunnable(int i) {

        int num = 100;
        class MyRunnerble implements Runnable {


            @Override
            public void run() {

            }
        }
        return new MyRunnerble();
    }
}

이때 getRunnable과 run의 생명주기가 다르다는 점에 주의하자

getRunnable이 소멸된 이후에도 run의 생명은 살아 있다.

 

따라서

class Outer {

    int outNum = 100;
    static int sNum = 200;

    Runnable gerRunnable(int i) {

        int num = 100;
        class MyRunnerble implements Runnable {

            num += 10;
            i = 20;


            @Override
            public void run() {
                System.out.println(i);
                System.out.println(num);
                System.out.println(outNum);
                System.out.println(Outer.sNum);
            }
        }
        return new MyRunnerble();
    }
}

num += 10;
i = 20;

는 초기화 할 수 없다. 왜 나면 위에 언급하였듯이 각각의 생명주기가 다르기 때문에getRunnable 객체가 생성된 이후에는

int i와 int num을 final로 만들어 버린다.

단지 해당 변수들은 참조만이 가능하다.

 

호출 방법

public class LocalInnerClass {
    public static void main(String[] args) {
        Outer outer = new Outer();
        Runnable runnable = outer.gerRunnable(50);

        runnable.run();
    }
}

익명 클래스

: 특정 클래스에만 쓰이는 코드를 익명 클래스로 사용할 수 있다.

Runnable getRunnable() {
        return new Runnable() {
            @Override
            public void run() {

            }
        };
    }

다른 방식

@FunctionalInterface
    public interface Company {

        public abstract void CompanyInfo();

}
public class AnonymousClass2 {

    Company company = new Company() {
        @Override
        public void CompanyInfo() {
            System.out.println("저는 익명 클래스 입니다.");
        }
    };
}

 

interface의 미구현 메서드가 하나일 때는 위와 같이 생성할 수 있다.