Burninghering's Blog
article thumbnail

내부 클래스란? (inner class) = 중첩 클래스 

클래스 내부에 선언한 클래스로 이 클래스를 감싸고 있는 외부 클래스와 밀접한 연관이 있는 경우가 많고,

다른 외부 클래스에서 사용할 일이 거의 없는 경우에 내부 클래스로 선언해서 사용함

 

내부 클래스의 종류 :

1) 인스턴스 내부 클래스, 정적(static) 내부 클래스, 지역(local) 내부 클래스 (멤버 변수 (=인스턴스 변수)와 선언 위치, 생성주기 등 똑같음)

2) 익명(anonymous) 내부 클래스

 

- 인스턴스 내부 클래스

  • 내부적으로 사용할 클래스를 선언 (private으로 선언하는 것을 권장, 안하면 외부에서도 사용가능하니까)
  • 외부 클래스가 생성된 후 생성됨 ( 정적 내부 클래스와 다름 )
  • private이 아닌 내부 클래스는 다른 외부 클래스에서 생성할 수 있음
OutClass outClass = new OutClass();
OutClass.InClass inClass = outClass.new InClass();
더보기
package ch01;

class OutClass{
	private int num=10;
	private static int sNum=20; //static 변수는 instance 변수보다 먼저 생성됨
	private InClass inClass; //일단 변수로 만들고, 그것을 객체화하는 것이 new이다 
	
	public OutClass(){  //Out Class가 생성될 때
		inClass=new InClass(); //// 내부 클래스 inClass가 생성된다.
	}
	
	private class InClass{ //Instance Inner Class는 OuterClass가 먼저 생성된 뒤 생성됨
		int iNum=100;
		
//		static int sINum=500; //static을 사용하려면, 정적 내부 클래스에서 사용해야 한다.
		
	 void inTest() {
			System.out.println("OutClass num = " +num + "(외부 클래스의 인스턴스 변수)");
			System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수)");
			System.out.println("InClass inNum = " + iNum + "(내부 클래스의 인스턴스 변수)");
		}
	}
	
	 void usingClass() {
		inClass.inTest(); //내부 클래스 변수를 사용하여 메서드 호출
	}
}


public class InnerTest { //완전 완전 외부 클래스

	public static void main(String[] args) {
		OutClass outClass=new OutClass();
		outClass.usingClass();
		
		System.out.println();
		
//		OutClass.InClass inner = outClass.new InClass(); //OutClass의 InClass에 private를 없애고 굳이 이렇게 쓸 수는 있음
//		System.out.println("외부 클래스 변수를 이용하여 내부 클래스 생성");
//		inner.inTest(); //private 안없애면 오류남
	}

}

- 정적 내부 클래스

  • 외부 클래스 생성과 무관하게 사용할 수 있음
  • 정적 변수, 정적 메서드 사용
  • 정적 내부 클래스 일반 메서드와 정적 메서드에서의 변수 사용

더보기
package ch01;

class OutClass{
	private int num=10;
	private static int sNum=20; //static 변수는 instance 변수보다 먼저 생성됨
	private InClass inClass; //일단 변수로 만들고, 그것을 객체화하는 것이 new이다 
	
	public OutClass(){  //Out Class가 생성될 때
		inClass=new InClass(); //// 내부 클래스 inClass가 생성된다.
	}
	
	private class InClass{ //Instance Inner Class는 OuterClass가 먼저 생성된 뒤 생성됨
		int iNum=100;
		
//		static int sINum=500; //static을 사용하려면, 정적 내부 클래스에서 사용해야 한다.
		
	 void inTest() {
			System.out.println("OutClass num = " +num + "(외부 클래스의 인스턴스(멤버) 변수)");
			System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수)");
			System.out.println("InClass inNum = " + iNum + "(내부 클래스의 인스턴스(멤버) 변수)");
		}
	}
	
	 void usingClass() {
		inClass.inTest(); //내부 클래스 변수를 사용하여 메서드 호출
	}
	 
	 static class InStaticClass {
		 int iNum=100;
		 static int sInNum=200;
		 
		 void inTest() { 
//				System.out.println("OutClass num = " +num + "(외부 클래스의 인스턴스(멤버) 변수)") ;//외부 클래스의 멤버 변수 num이 아직 안만들어졌는데 호출될수도 있어서 에러남
				System.out.println("InClass num = " +iNum + "(본인(정적), 즉 내부 클래스의 인스턴스(멤버) 변수)");
				System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수)");
				System.out.println("InClass inNum = " + sInNum + "(본인(정적), 즉 내부 클래스의 static  인스턴스(멤버) 변수)");
				
				System.out.println("결론적으로 외부클래스의 인스턴스 변수는 사용할 수 없다. 생성이 안되어있을 수도 있으니까");
			}
		 
		 static void sTest() { //static 클래스의 static 메소드는 static 클래스가 생성되지 않아도 static 메소드가 호출 될 수 있다
//				System.out.println("InClass num = " +iNum + "(본인(정적), 즉 내부 클래스의 인스턴스(멤버) 변수)"); //그래서 에러남
				System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수)");
				System.out.println("InStaticClass sInNum = " + sInNum + "(본인(정적), 즉 내부 클래스의 static  인스턴스(멤버) 변수)"); //그래서 에러남
		 }
	 }
}


public class InnerTest { //완전 완전 외부 클래스

	public static void main(String[] args) {
		OutClass outClass=new OutClass();
		outClass.usingClass();
		
		System.out.println();
		
//		OutClass.InClass inner = outClass.new InClass(); //OutClass의 InClass에 private를 없애고 굳이 이렇게 쓸 수는 있음
//		System.out.println("외부 클래스 변수를 이용하여 내부 클래스 생성");
//		inner.inTest(); //private 안없애면 오류남
	
		OutClass.InStaticClass sInClass = new OutClass.InStaticClass(); //외부 클래스에서 정적 클래스는 바로 생성 가능
		sInClass.inTest();
		
		System.out.println();
		
		OutClass.InStaticClass.sTest(); //정적 메소드는 바로 호출 가능하며, 스태틱 변수만 사용 가능
	}

}


- 지역 내부 클래스

  • 지역 변수와 같이 메서드 내부에서 정의하여 사용하는 클래스
  • 메서드의 호출이 끝나면 메서드에 사용된 지역변수의 유효성은 사라짐
  • 메서드 호출 이후에도 사용해야 하는 경우가 있을 수 있으므로 지역 내부 클래스에서 사용하는 메서드의 지역 변수나 매개 변수는 final로 선언됨
  • MyRunnable 클래스를 사용하려면 직접 생성하는 것이 아닌 getRunnable()메서드를 호출하여 생성된 개체를 반환 받아야 함
더보기
package ch01;

class Outer2{
	
	int outNum = 100;
	static int sNum = 200;
	
	Runnable getRunnable(final int i) { //Runnable 인터페이스를 반환한다. 그래서 이 함수의 이름은 getRunnable
	//Runnable 인터페이스 클래스를 스레드화 할 때 run을 제공함
		
		final int num=10;
		
		class MyRunnable implements Runnable{
	
		int localNum=1000;
			
		@Override
		public void run() { //run은 getRunnable()을 받고 나면 또 호출될 여지가 있음
			
//			i=50; //그때, 이러한 변수들이 없을 수도 있으니 여기서는 값을 변경할 수 없다(스택에 잡히면 안된다!)
//			num=20; //그러므로 애초에 final로 선언하고, 변수를 아래처럼  갖다 쓰기만 한다.(외부 지역 변수를 쓸 때 조심!)
			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 = " + Outer2.sNum + "(외부 클래스 정적 변수)");
			
			}
		}
		return new MyRunnable();
	}
}

public class AnonoymousInnerTest {

	public static void main(String[] args) {
		  Outer2 out = new Outer2();
		  Runnable runner = out.getRunnable(100); //getRunnable의 스택에 잡혔던 멤버변수들이 이 명령어가 끝난 뒤 사라짐
		  
		  runner.run(); //이렇게 다시 run()에서 멤버변수들이 불려져버리기 때문에, 상수화 해버린다.
	}

}

- 익명 내부 클래스

  • 이름이 없는 클래스 (위 지역 내부 클래스의 MyRunnable 클래스 이름은 실제로 호출되는 경우가 없음)
  • 클래스의 이름을 생략하고, 주로 하나의 인터페이스나 하나의 추상 클래스를 구현하여 반환(아래 코드가 그 예시)
  • 인터페이스나 추상 클래스 자료형의 변수에 직접 대입하여, 클래스를 생성하거나 지역 내부 클래스의 메서드 내부에서 생성하여 반환 할 수 있음.
  • widget의 이벤트 핸들러에 활용됨(안드로이드 앱 개발 시)

더보기

지역 내부 클래스에서 쓴 코드에서,

MyRunnable()은 맨 끝에 리턴할 때밖에 쓰이지 않으니까

함수 이름을 지우고 새로

return new Runnable()로 바꿔주고,

끝 괄호에 ; 를 추가해준다.

package ch01;

class Outer2{
	
	int outNum = 100;
	static int sNum = 200;
	
	Runnable getRunnable(int i) { //Runnable 인터페이스를 반환한다. 그래서 이 함수의 이름은 getRunnable
	//Runnable 인터페이스 클래스를 스레드화 할 때 run을 제공함
		
		int num=10;
		
		return new Runnable(){

		int localNum=1000;
		
		@Override
		public void run() {
			
			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 = " + Outer2.sNum + "(외부 클래스 정적 변수)");
			
			}		
		};
	}
	
	Runnable runnable = new Runnable() {

		@Override
		public void run() {
			System.out.println("Runnable class");
		}
	};
}

public class AnonoymousInnerTest {

	public static void main(String[] args) {
		  Outer2 out = new Outer2();
		  Runnable runner = out.getRunnable(100); //getRunnable의 스택에 잡혔던 멤버변수들이 이 명령어가 끝난 뒤 사라짐
		  
		  runner.run(); //이렇게 다시 run()에서 멤버변수들이 불려져버리기 때문에, 상수화 해버린다.
	}
}

 

profile

Burninghering's Blog

@개발자 김혜린

안녕하세요! 반갑습니다.