개발 공부/Java

[Java] - 내부 클래스

징_ 2023. 11. 14. 18:50

* 내부클래스(inner class)

:  클래스 내부에 선언한 클래스로 클래스 내부에서만 사용할 목적으로 만든다.(외부 클래스에서만 사용하기 위해)

↔ 외부클래스(out class)

 

 

<내부 클래스 종류>

=> 내부 클래스의 유형은 변수의 유형과 유사

 

 


 

 

- 인스턴스 내부 클래스(instance inner class)

  • 외부 클래스 생성 후 내부 클래스 생성
  • 내부 클래스에 정적 변수, 정적 메서드 사용 불가 -> 인스턴스가 동적으로 생성되기 때문에(인스턴스보다 먼저 정적 변수가 만들어질 수 없다.)
//외부 클래스
class OutClass{	
	//멤버 변수
	private int num = 10;				//외부클래스 - private 변수
    private static int sNum = 20; 		//외부클래스 -  정적 변수
    private InClass inClass;			//내부클래스 자료형 변수 (외부클래스 디폴트 생성자에서 
    									//쓰려고)
    
    //외부클래스 - 디폴트 생성자
    public OutClass() {
    	inClass = new InClass(); 		//외부클래스 생성된 후 내부클래스 쌩성
    }
    
    //내부 클래스 (인스턴스 내부 클래스 : 인스턴스가 위치하는 곳에 있는 클래스)
    class InClass{
    	//멤버 변수
    	int inNum = 100;				//내부클래스 - 인스턴스 변수
//      static int sInNum = 200;		//인스턴스 내부 클래스에 정적 변수 선언 불가
		
        //메서드 
        //1 - 테스트 일반 메서드
        void inTest()) {				//인스턴스 내부 클래스의 일반 메서드
        	System.out.println("외부클래스 인스턴스 변수 num: " + num);
			System.out.println("외부클래스 스태틱 변수 sNum: " + sNum);
        }
        
        //2 - 테스트 스태틱 메서드(불가능)
//      static void sTest() {}			//인스턴스 내부 클래스에 정적 메서드 선언 불가
		
        //3 - 클래스 사용 일반 메서드
		public void usingClass() {
        	inClass.inTest();
        }
    }
}

* 멤버변수와 인스턴스 변수는 같은말

 

  • 외부 클래스 참조변수 -> 내부클래스 메서드 사용 가능
// 외부 클래스로 내부클래스 메서드 접근
OutClass outClass = new OutClass();
outClass.usingClass();
  • 내부 클래스 private아니면 다른 클래스에서도 생성이 문법적으로는 가능함 (보통은 public보다 private으로 내부클래스가 있는 외부클래스에서만 사용함)
//외부클래스에서 내부클래스를 참조하여 내부클래스의 인스턴스를 생성해 쓸 수 있음
OutClass outClass = new OutClass());
OutClass.InClass inClass = outClass.new InClass();
  • 클래스 내부에서만 사용할 목적이면 private 선언 

 

 


 

 

 

- 정적 내부 클래스(static inner class)

: 외부 클래스의 생성과 무관하며 ( static은 new 생성 없이 클래스명으로 직접접근이 가능하다.)

정적 변수, 정적 메서드를 사용한다.

 

 

<정적 내부 클래스 메서드에서 변수 사용 여부>

 

class OutClass{
	
	//외부클래스 변수
	private int num = 10;			//인스턴스 변수
	private static int sNum = 20;	//정적 변수 = static 변수
    
    //정적 내부 클래스
	static class InStaticClass{
		//정적 내부 클래스 인스턴스변수 
		int inNum = 100;
		// 정적 내부 클래스 정적변수
		static int sInNum = 200;
		
		//정적 내부 클래스의 일반메서드
		void inTest() {
			//num += 10; 외부클래스의 인스턴스 변수는 사용 못함
			System.out.println("정적내부클래스 인스턴스 변수 inNum: "+ inNum);
			System.out.println("정적내부클래스 스태틱 변수 sInNum: "+ sInNum);
			System.out.println("외부클래스 스태틱 변수 sNum: "+ sNum);
		}
        
        //정적 내부 클래스의 static메서드
		static void sTest() {
			//num += 10; 외부클래스의 인스턴스 변수는 사용 못함
			//inNum += 10; 정적 내부 클래스의 인스턴스 변수는 정적 메서드에서 사용 못함
			System.out.println("외부클래스 스태틱 변수 sNum: "+ sNum);
			System.out.println("정적내부클래스 스태틱 변수 sInNum: "+ sInNum);
		}
	}
	
}

 

 

OutClass( InStaticclass ( void inTest(), static void sTest() )

=> 외부클래스 안에 정적 내부클래스, 정적 내부클래스 안에 일반 메서드와 정적 메서드가 있는 상태

 

★static은 static끼리 써야한다.

여기서 void inTest() 일반 메서드의 경우

- 정적 내부 클래스에서 선언된 모든 변수(정적 변수, 인스턴스 변수)를 사용할 수 있지만

- 정적 내부 클래스 밖에서(외부클래스) 선언된 인스턴스 변수는 사용할 수 없다. 

=> 왜냐하면 인스턴스 변수가 정적 내부클래스를 거쳐서 일반메서드로 들어와야하는데 정적 내부 클래스는 외부의 인스턴스 변수를 사용할 수 없기 때문이다. (외부 클래스의 static변수는 정적 내부클래스를 거쳐도 아무 문제가 없으므로 void inTest()에서  사용 가능하다.)

+) static 클래스에 인스턴스 변수를 선언하는 것은 가능하나 사용은 불가능!

 

 

✔️다른 클래스에서 정적 내부 클래스 사용하는 방법

 

1. 외부 클래스 생성 없이 바로 정적 내부클래스 생성가능

OutClass.InStaticClass sInClass = new OutClass.InStaticClass();

2. 정적 내부 클래스에 선언한 메서드, 변수가 private아닌 경우 바로 사용

OutClass.InStaticClass.sTest();

 

 

 


 

 

 

 - 지역 내부 클래스(local inner class)

: 메서드 내부에서 정의하여 사용하는 클래스 (지역 변수처럼)

 

* 지역 변수

- 함수 호출과 동시에 Stack영역에 생성되고 함수가 끝남과 동시에 Stack영역에서 소멸된다.

 

*지역 내부 클래스에서 사용하는 메서드의 지역변수

- 클래스이기 때문에 지역변수는  final로 선언되어 Stack영역이 아닌 상수풀로 옮겨진다. 따라서 함수가 끝나도 소멸되지 않는다. => 지역변수 값 변경 불가

 

=> 메서드 안에 클래스가 있으면 지역변수는 상수가 된다!

 

 

📄예제)  Runnable 인터페이스 구현

 

*MyRunnable은 Runnable인터페이스를 구현한 지역 내부클래스

+) Runnable인터페이스 : 스레드를 구현하기 위한 템플릿

//외부 클래스
class Outer {
	//멤버변수
	int outNum = 100;				//외부클래스 - 인스턴스 변수
    static int sNum = 200;			//외부클래스 - 정적 변수
    
    //Runnable 메서드 - run()메서드 재정의 필요
    Runnable getRunnable(int i) {	//지역변수 : num, i
    	//지역변수
        int num = 100;
        
        //내부 클래스(지역 내부 클래스 : 메서드 안 지역변수가 위치하는 곳에 있음)
        class MyRunnable implements Runnable {
        	//멤버변수
        	int localNum = 10;		//내부클래스 - 인스턴스 변수
            
            //메서드 - run() 구현
            @Override
            public void run() {
//          	num = 200;			//지역변수가 상수로 바뀌어 값 변경 불가
//              i = 100;			//지역변수가 상수로 바뀌어 값 변경 불가
            	System.out.println("i =" + i);		//참조는 가능
        		System.out.println("num =" + num);	//참조는 가능
            }
        }
        return new MyRunnable(); 	//메서드의 반환형이 Runnable이므로 
    }								//Runnable형의 객체 생성 후 반환해야함
}

 

*MyRunnable 지역 내부 클래스 사용

Outer out = new Outer();
Runnable runner = out.getRunnable(10); //Runnable 형 객체로 getRunnable()호출
runner.run();

=> getRunnable()메서드 호출이 끝났으므로 지역변수는 지워져 더이상 참조할 수 없어야하는데 run()메서드에서 지역변수 i, num이 사용된다. 이게 가능한 이유는 지역변수가 상수로 처리되기 때문이다.

 

 

 


 

 

 

 - 익명 내부 클래스(anonymous inner class)

: 이름이 없는 클래스로 주로 하나의 인터페이스하나의 추상 클래스를 구현하는데 사용(일회용)

(안드로이드 위젯의 이벤트 핸들러 구현에 많이 씀)

*이벤트 핸들러 : 사용자의 터치, 버튼 클릭, 텍스트 입력 등 사용자와의 상호작용 이벤트를 처리하는 코드 블록 또는 함수

 

 

<생성하는 방법>

  • 지역 내부 클래스가 있는 메서드(Runnable getRunnable()) 내부에서 생성(생성자를 반환하여 생성)
//외부 클래스
class Outter2 {
	
    //메서드 - Runnable형
	Runnable getRunnable(int i) {
    	//지역 변수
    	int num = 100;
        
        //내부 클래스(익명 내부 클래스 : 이름 없는 클래스)
        return new Runnable() {		//MyRunnable 클래스 이름 빼고 클래스 바로 생성
        	
            //메서드 - run()구현
        	@Override 
        	public void run() {
				System.out.println(i);
                System.out.println(num);
            }
       };	//익명 내부 클래스 끝에 세미콜론 붙여줘야함
    }
}

 

=> 사용 : getRunnable() 호출 후 내부 클래스 메서드인 run()참조

Outter2 out = new Outter2();
Runnable runnerable = out.getRunnable(10); //인스턴스 생성
reunnerable.run();

 

  • 인터페이스/추상클래스 자료형의 변수에 직접 대입하여 생성
//외부 클래스
class Outter2 {
	
    //메서드 - Runnable형
	Runnable getRunnable(int i) {
    	//지역 변수
    	int num = 100;
        
        //내부 클래스(익명 내부 클래스 : 이름 없는 클래스)
        Runnable runner = new Runnable() { //인스턴스 바로 생성
        	@Override
            public void run() {
            	System.out.println("Runnable이 구현된 익명 클래스 변수");
            }
        };	//익명 내부 클래스 끝에 세미콜론 붙여야 함
    }
}

 

=> 사용 : 외부클래스의 내부클래스를 참조하고 내부클래스의 run()을 참조

Outter2 out = new Outter2();
out.runner.run();

 

 


 - 내부 클래스 요약