공부했던 자료 정리하는 용도입니다.

재배포, 수정하지 마세요.

 

 

 

 

 

내부 클래스(Inner Class)

 내부 클래스는 클래스 내에 선언된다는 점을 제외하고는 일반적인 클래스와 다르지 않다. AWT나 Swing과 같은 GUI 애플리케이션의 이벤트 처리 외에는 잘 사용되지 않는다.

 

 

내부 클래스란?

내부 클래스의 장점

1. 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
2. 코드의 복잡성을 줄일 수 있다. (캡슐화)

내부 클래스는 클래스 내에 선언된 클래스이다. 두 클래스가 서로 긴밀한 관계가 있을 때 내부 클래스를 사용한다. 한 클래스를 다른 클래스의 내부 클래스로 선언하면 두 클래스 멤버들 간에 서로 쉽게 접근할 수 있고, 외부에는 불필요한 클래스를 감춰서 코드의 복잡성을 줄일 수 있다는 장점이 있다.

 

 

class A{    //외부클래스
	...
    class B{	//내부클래스
    	...
    }
	...
}

위의 코드에서  B  A 의 내부 클래스가 되고  A  B 의 바깥에 있는 외부 클래스가 된다. 이때 내부 클래스인  B 는 외부 클래스인  A 를 제외하고는 다른 클래스에서 잘 사용되지 않는 것이어야 한다. (외부 클래스가 아닌 다른 클래스에서 내부 클래스에 접근해야 한다면 내부 클래스로 선언해서는 안 되는 클래스를 내부 클래스로 선언했다는 의미이다.)

 

 

 

내부 클래스의 종류와 특징

내부클래스 특징
인스턴스 클래스
(instance class)
외부 클래스의 멤버변수 선언 위치에 선언하며, 외부 클래스의 인스턴스 멤버처럼 다뤄진다. 주로 외부 클래스의 인스턴스멤버들과 관련된 작업에 사용될 목적으로 선언된다.
스태틱 클래스
(static class)
외부 클래스의 멤버변수 선언 위치에 선언하며, 외부 클래스의 static멤버처럼 다뤄진다. 주로 외부 클래스의 static멤버, 특히 static메서드에서 사용될 목적으로 선언된다.
지역 클래스
(local class)
외부 클래스의 메서드나 초기화블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있다.
익명 클래스
(anonymous class)
클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스(일회용)

내부 클래스의 종류는 변수의 선언 위치에 따른 종류와 같다. 

 

 

 

 

내부 클래스의 선언

class Outer{
	class InstanceInner { }	//인스턴스클래스
	static class StaticInner { }	//스태틱클래스
    
    void method1(){
    	class LocalInner{ } 	//지역클래스
    }
}

각 내부 클래스의 선언 위치에 따라 같은 선언 위치의 변수와 동일한 유효 범위(scope)와 접근성(accessibility)을 갖는다.

 

 

 

 

내부 클래스의 제어자와 접근성

class Outer{
	//내부클래스에 제어자를 붙일 수 있다.
	private class InstanceInner { }	
	protected static class StaticInner { }	
    
    void method1(){
    	class LocalInner{ } 
    }
}

내부 클래스는 외부 클래스의 멤버와 같이 간주되고, 인스턴스 멤버와  static 멤버 간의 규칙이 내부 클래스에도 똑같이 적용된다. 그리고 내부 클래스도 클래스이기 때문에  abstract  final 과 같은 제어자를 사용할 수 있을 뿐만 아니라 멤버 변수들처럼  private  protected 와 접근제어자도 사용 가능하다.

 

내부 클래스중에서 스태틱 클래스(static class)만  static 멤버를 가질 수 있다. 드문 경우지만 내부 클래스에  static 변수를 선언해야 한다면 스태틱 클래스로 선언해야 한다. 다만  final  static 이 동시에 붙은 변수는 상수(constant)이므로 모든 내부 클래스에서 정의가 가능하다.

 

 

 

package test;

class test{
	class InstanceInner {}	//내부클래스(인스턴스 클래스)
	static class StaticInner {}	//내부클래스(스태틱 클래스)
	
	//인스턴스 멤버 간에는 서로 직접접근 가능
	InstanceInner iv = new InstanceInner();	
	
	//static 멤버들 간에도 서로 직접접근이 가능
	static StaticInner cv = new StaticInner();
	
	static void staticMethod() {
		//static멤버는 인스턴스멤버에 직접 접근할 수 X
		//InstanceInner obj1 = new InstanceInner();
		StaticInner obj2 = new StaticInner();
		
		//접근하려면 아래와 같이 객체를 생성해야함
		//인스턴스 클래스는 외부 클래스를 먼저 생성해야만 생성할 수 있다.
		test outer = new test();
		InstanceInner obj1 = outer.new InstanceInner();
	}
	
	void instanceMethod() {
		//인스턴스 메서드에서는 인스턴스멤버와 static멤버 모두 접근가능
		InstanceInner obj1 = new InstanceInner();
		StaticInner obj2 = new StaticInner();
		
		//메서드 내에 지역적으로 선언된 내부클래스는 외부에서 접근할 수 X
		//LocalInner lv = new LocalInner();
	}
	
	void method() {
		class LocalInner{}
		LocalInner lv = new LocalInner();
	}
}

인스턴스 클래스는 외부 클래스의 인스턴스 멤버를 객체 생성 없이 바로 사용할 수 있지만, 스태틱 클래스는 외부클래스의 인스턴스 멤버를 객체 생성 없이 사용할 수 없다. 또한 인스턴스 클래스는 스태틱 클래스의 멤버들을 객체 생성 없이 사용할 수 있지만, 스태틱 클래스에서는 인스턴스 클래스의 멤버들을 객체 생성 없이 사용할 수 없다.

 

 

 

package test;

class test{
	private int outerIv = 0;
	static int outerCv = 0;
	
	class InstanceInner{
		int iiv = outerIv;	//내부클래스에서 외부클래스의 private멤버도 접근할 수 있다.
		int iiv2 = outerCv;
	}
	
	static class StaticInner{
		//스태틱 클래스는 외부클래스의 인스턴스멤버에 접근할 수 X
		//int siv = outerIv;
		static int scv = outerCv;
	}
	
	void method() {
		int lv = 0;
		final int LV = 0;	//JDK1.8부터 final생략가능
		
		class LocalInner{
			int liv = outerIv;
			int liv2 = outerCv;
			
			//외부 클래스의 지역변수는 final이 붙은 변수(상수)만 접근가능하다.
			//int liv3 = lv;	//에러(jdk1.8부터는 에러 아님)
			int liv4 = LV;
		}
	}
}
  • 인스턴스 클래스( InstanceInner )는 외부 클래스( test )의 인스턴스 멤버이기 때문에 인스턴스 변수  outerlv  static 변수  outerCv 를 모두 사용할 수 있다. ( outerlv 의 접근제어자가  private 일지라도 사용 가능)

  • 스태틱 클래스( StaticInner )는 외부 클래스( test )의  static 멤버기 때문에 외부 클래스의 인스턴스 멤버인 outerlv  InstanceInner 를 사용할 수 없고  static 멤버인  outerCv 만을 사용할 수 있다.

  • 지역 클래스( LocalInner )는 외부 클래스의 인스턴스 멤버와 static 멤버를 모두 사용할 수 있으며, 지역 클래스가 포함된 메서드에 정의된 지역변수도  final 이 붙은 지역변수면 접근 가능하다(메서드가 수행을 마쳐서 지역변수가 소멸된 시점에도, 지역 클래스의 인스턴스가 소멸된 지역변수를 참조하려는 경우가 발생할 수 있기 때문)

  • JDK1.8부터 지역 클래스에서 접근하는 지역 변수 앞에  final 을 생략할 수 있다. 컴파일러가 자동으로 붙여주는 것뿐이라 해당 변수의 값이 바뀌는 문장이 있으면 컴파일 에러가 발생한다.

 

 

 

class Outer{
	void method1(){
    	class LocalInner {}
    }
    
    void method2(){
    	class LocalInner {}
    }
}

내부 클래스가 컴파일되면 클래스 파일명이  외부 클래스명$내부 클래스명.class 형식으로 된다. 다만 서로 다른 메서드 내에서는 같은 이름의 지역변수를 사용하는 것이 가능한 것처럼, 지역 내부 클래스는 다른 메서드에 같은 이름의 내부 클래스가 존재할 수 있기 때문에 내부 클래스명 앞에 숫자가 붙는다.

(위의 코드를 컴파일하면  Outer.class  Outer$1LocalInner.class  Outer$2LocalInner.class 인 3개의 클래스 파일이 생성됨)

 

 

package test;

class Outer{
	int value = 10;	//Outer.this.value
	
	class Inner{
		int value = 20;	//this.value
		
		void method() {
			int value = 30;	//value
			System.out.println("value : " + value);
			System.out.println("this.value : " + this.value);
			System.out.println("Outer.this.value : " + Outer.this.value);
		}
	}
}

class test{
	public static void main(String[] args) {
		Outer outer = new Outer();
		Outer.Inner inner = outer.new Inner();
		inner.method();
	}
}

 

실행결과

내부 클래스와 외부 클래스에 선언된 변수의 이름이 같을 때 변수 앞에  this 또는  외부 클래스명.this 를 붙여서 구별할 수 있다.

 

 

 

 

익명 클래스(Anonymous class)

new 부모클래스 이름(){
	//멤버선언
}

//다른방법
new 구현인터페이스이름(){
	//멤버선언
}

익명 클래스는 클래스의 선언과 객체의 생성을 동시에 하기 때문에 단 한 번만 사용될 수 있고 오직 하나의 객체만을 생성할 수 있는 일회용 클래스이다. 이름이 없기 때문에 생성자도 가질 수 없으며, 부모 클래스의 이름이나 구현하고자 하는 인터페이스의 이름을 사용해서 정의하기 때문에 하나의 클래스로 상속받는 동시에 인터페이스를 구현하거나 둘 이상의 인터페이스를 구현할 수 없다. 오로지 단 하나의 클래스를 상속받거나 단 하나의 인터페이스만을 구현할 수 있다.

 

 

package test;

class test{
	Object iv = new Object() { void method(){ } };	//익명클래스
	static Object cv = new Object() { void method(){ }};	//익명클래스
	
	void myMethod(){
		Object lv = new Object() { void method(){ } };	//익명클래스
	}
}

익명 클래스는 이름이 없기 때문에  외부 클래스명$숫자.class 의 형식으로 클래스 파일명이 결정된다. 그래서 위의 코드를 컴파일하면(편의상 뺐는데 실행하고 싶으면  main 을 넣어야 함)  test.class 외에  test$1.class  test$2.class  test$3.class 라는 3개의 클래스 파일이 생성된다.  

 

 

 

+ Recent posts