본문으로 바로가기

내부 클래스(inner class)

category Java언어 2018. 8. 21. 18:50

 

수많은 포스팅과 기본서에 여러 용어가 무질서하게 섞여서 사용된다. 어떤 글에선 중첩 클래스와 내부 클래스, 이너클래스를 구분하여 설명하고 또다른 글에서는 같은 개념으로 소개한다. 혼란을 겪는 사람들을 위해 우선 중첩 클래스 ,이너 클래스, 내부 클래스라는 용어에 대해 정리해보려 한다. 우선 내부 클래스는 영어로 inner class이다. 즉 내부 클래스와 이너 클래스는 같은 것이다. 그렇다면 이제 중첩 클래스와 내부 클래스만 구분하면 혼란을 없앨 수 있다. 중첩 클래스(nested class)와 내부 클래스(inner class)는 어떤 차이가 있을까?

 


 

1) 어떤 글에선 이너 클래스(inner)클래스는 중첩 클래스(nested class)의 한 부분으로서 정적 중첩 클래스를 이너 클래스라고 설명한다.

 

2) 그리고 대표적인 기본서인 '이것은 자바다'에서는 이너 클래스라는 용어를 사용하지 않고 중첩 클래스라는 용어를 이용해 개념을 설명해     나간다. 이너 클래스라는 말은 한번도 나오지 않는다.

인스턴스 멤버 클래스

정적 멤버 클래스

로컬 클래스

익명 클래스

 

3) 반대로 또다른 기본서 '자바의 정석'에서는 이너 클래스라는 용어만 사용한다.

인스턴스 클래스(instance class)

스태틱 클래스(static class)

지역 클래스(local class)

익명 클래스(anonymous class)

 


 

영문자료를 번역하는 와중에 일어난 분열인지 오류인지 또는 Java언어가 발전해 가며 파생된 새로운 개념인지는 모르겠지만, 일부러 복잡하게 따로 생각해야하는 특별한 이유는 없다. 따라서 그냥 두 용어가 같다고 생각하는게 좋다. 중첩 클래스(nested class)란 클래스 내부에 클래스가 있는 하나의 형태이고, 그 안에 있는 클래스를 내부 클래스(inner class)라고 생각하는게 편할 것 같다. 필자는 그 중에서도 내부 클래스라는 용어를 선택해서 설명하도록 하겠다.

 

 


 

 

1. 내부 클래스(inner class)란

클래스안에 선언된 또 하나의 클래스를 말한다.

 

 

2. 내부 클래스(inner class)의 장점

1) 외부 클래스와 내부 클래스가 서로의 멤버에 접근하기 쉽다.

2) 외부에는 불필요한 클래스를 은닉함으로써 코드의 복잡성을 줄일 수 있다.(캡슐화)

 

 

3. 내부 클래스(inner class)의 종류

- 클래스는 field, counstructor, method로 구성된다. 각각의 구성요소는 선언하는 위치가 암묵적으로 정해져 있다.

- 클래스가 선언된 위치, static키워드의 유무 등에 따라 내부 클래스가 구분한다.

- 각각의 영역에 선언된 변수와 같은 특성을 지닌다. 따라서 변수와 관련지어 생각하면 이해에 도움이된다.

 

1) 인스턴스 클래스(instance class)

2) 스태틱 클래스(static class)

3) 로컬 클래스(local class)

4) 익명 클래스(anonymous class)

 

*C언어에서는 main()의 시작부분, 즉 중괄호 바로 다음이 변수선언부 이다. 다른곳에 변수를 선언하면 에러가 발생한다.

 C언어는 절차지향 언어이기 때문에 코드를 컴파일할때 코드의 처음부터 끝까지 순서대로 컴파일 해나간다. 이때 변수들의 메모리를 확보하는 영역을 main함수의 중괄호 바로 뒷부분, 실행문이 오기전으로 정해 놓았기 때문에 그 이외의 영역에서 변수선언시 컴파일러가 메모리를 확보하지 못한다. (windows운영체제의 컴파일러는 지원하는 경우도 있는데 이는 C컴파일러가 아니라 C++c컴파일러라서 그렇다. 하지만 절차지향적인 특징에 따라 변수선어부에서 선언해주는것이 바람직함) 반면 Java에서는 이러한 제약에서 벗어나서 중괄호가 있는 곳이면 어디든 변수선언이 가능하지만 통상적으로 선언되는 위치에 선언하는 것이 눈으로 확인하기 편할 것 이다.

 

 


 

 

 

1)인스턴스 클래스(instance class)

- field(클래스의 멤버 변수)선언부에 위치하고 static 키워드가 없는 내부 클래스.(인스턴스 멤버 클래스라고도 한다.)

- 외부 클래스의 멤버이기 때문에 외부 클래스의 객체 먼저 생성한 후 내부 클래스의 객체를 생성하여 사용해야 한다.

- 인스턴스 클래스 내부에는 instance member만 선언할 수 있다. static member는 선언 불가.

- 컴파일시 생성되는 클래스 파일명 : "외부 클래스 $ 내부 클래스.class"

- 주로 외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용될 목적으로 선언된다.

 


 

class A{

//field

 class B{                 <-필드선언 부

                           

}                           

//constructor

 

//method

 

}

 


 

A a = new A();    //외부 클래스 생성 -> A.class

 

A.B b = a.new B();    //내부 클래스 생성 -> A $ B.class

 

 


 

 

2)스테틱 클래스(static class)

- field(클래스의 멤버 변수)선언부에 위치하고 static 키워드가 붙은 내부 클래스.(스테틱 멤버 클래스라고도 한다.)

- 외부 클래스의 멤버이지만, static이 붙어있기 때문에 외부 클래스의 객체를 생성하지 않고도 내부 클래스의 객체를 생성 수 있다.

- 스테틱 클래스 내부에는 instance member와 static member 모두 선언 할 수 있다. -> 무슨차이가 있지..?

- 컴파일시 생성되는 클래스 파일명 : "외부 클래스 $ 내부 클래스.class"

 


 

class A{

//field

 static class B{          <-필드선언 부

      int data1;         

      static int data2;  

}                           

//constructor

 

//method

 

}

 


 

A a = new A();    //외부 클래스 생성 -> A.class    외부 클래스 객체 생성없이 클래스 명으로 내부 클래스 객체 생성

 

A.B b = new A.B();    //내부 클래스 생성 -> A $ B.class

 

b.data1 = 10;        //인스턴스 멤버에 접근

A.B.data2 = 20;     //정적 멤버에 접근(내부 클래스 객체도 생성할 필요 x)


 

 

*스테틱 클래스의 내부에는 instance member와 static member 모두 선언할 수 있는데 어떤 의미가 있을까?

 

static class는 정적 클래스로서 객체를 생성하지 않아도 메모리가 할당되서 어차피 내부의 멤버 모두 메모리에 잡힐터인데 내부의 instance member와 static member를 구분하여 모두 선언 가능하다고 한 이유가 무엇일까? static키워드의 의미차이 때문이다.


 

class A{

 

//field

static class B{

int data1;            ?

static int data2;    ?

 

void setData1(){

}

static void setData2(){

}

}

 

}


 

 

*class에 붙는 static과 멤버에 붙는 static의 의미차이

클래스에 붙는 static과 클래스의 멤버, 즉 field와 method에 붙는 static키워드의 의미는 다르다.

익히 알고 있듯이 멤버에 붙는 static키워드는 정적으로 메모리를 잡아준다는 의미로서 컴파일 타임에 컴파일러가 메모리를 할당해 주기 때문에 객체생성 없이도 사용 가능하다는 의미이다. 반면

클래스에 붙는 static은 외부 클래스의 객체 생성없이 내부 클래스의 객체를 생성할 수 있다는 의미이다.

 

 

*스테틱 클래스 내부에 선언된 정적멤버의 활용

System.out.println에서 객체를 생성하지 않고 해당 메소드를 사용할 수 있는 이유는 무엇인가?

우선 객체생성과 상관없이라는 말을보면 무언가 static과 관련이 있을 것 같다.

 

java에서 클래스에 static을 붙일수 없지만 내부 클래스에는 static을 붙일 수 있다. 내부 클래스의 스테틱 클래스는 멤버 클래스로서 외부 클래스의 멤버와 같은 성질을 가졌기 때문이다. 즉, out은 System클래스의 스태틱 클래스이고 println()메소드는 스테틱 메소드이기 때문에 System.out.println()이 가능한 것이다. 그런데 out이 내부 클래스라면 첫 문자가 대문자여야 하는데 아니다. 그래서 API를 찾아보니 아니었다... out은 System클래스의 정적 참조 변수 즉, static PrintStream out 이었다. 아마

public static final PrintfStream out = new PrintStream(); 이라고 되어있지 않을까 싶다.

아무튼 틀렷지만 이러한 접근도 가능하다는 것을 말하기 위해 이 글을 적었다.


 

 

3)로컬 클래스(local class)

-외부 클래스의 메소드 내부에 위치하는 클래스. (지역변수와 같은 성질을 지님)

- 접근제한자와 static을 붙일 수 없다. 메소드 내부에서만 사용되므로 접근을 제한할 필요가 없고, 원래 메소드 내에는 static멤버를

  선언할 수 없기 때문이다.

- 컴파일시 생성되는 클래스 파일명 : "외부 클래스 $1 내부 클래스.class"(지역클래스는 같은이름으로 여러개 생성가능->숫자로구분)

 


 

class A{

//field

 

//constructor

 

//method

method1(){           

class B{        <- 메소드의 지역변수 선언부

       int data;

}                

 

B b = new B();

b.data = 10;

 

}

 

 

}


 

3)익명 클래스(anonymous class)

- 익명 클래스는 클래스를 정의하는 동시에 객체를 생성할때 만들어지는 클래스이다.

 

- 익명 자식 클래스 : 부모 클래스를 상속받아 작성함과 동시에 객체를 생성할 때

 부모 클래스 타입으로 필드를 선언하고, 익명 자식 객체를 대입하여 사용

 부모 클래스타입  변수명 = new 부모클래스명(){구현내용};

 

A a = new 부모클래스명(){

@overridding

method(){

}

}

 

- 익명 구현 클래스 : 인터페이스를 구현함과 동시에 객체를 생성할 때

 인터페이스 타입으로 필드를 선언하고, 익명 구현 객체를 대입하여 사용

 인터페이스 타입  변수명 = new 인터페이스명(){구현내용};

 

A a = new 인터페이스명(){

@overridding

method(){

}

}

 

- 외부 클래스명 $ 1.class (숫자로 구분)

 

-안드로이드 같은 UI 프로그래밍에서 각 객체의 클릭 이벤트를 처리하기 위해 많이 사용된다.


 

*new뒤에 오는 부모클래스명()과 인터페이스명()는 생성자 인가?

.

new연산자 뒤에 생성자를 호출함으로서 객체생성이 가능하다. 그런데 위의 코드를 보면 interface명이온다.

interface로도 객체생성이 가능한 것인가? 가능하다면 interface도 생성자를 가지는 것인가?

 

결론을 말하자면  interface는 객체를 만들 수 없고, 위의 코드에서 interface명()은 생성자가 아니다.  

그렇다면 위 코드의 정체와 내부적 작동원리에 대해 알아보겠다.

 

모든 클래스는 Object클래스를 상속받는다. 객체생성과 소멸에 관련된 모든 것들은 Object클래스에 내재되어 있다. 즉, 생성자 또한 Object클래스안에 포함된다. 따라서 Object클래스를 상속받지 않는 인터페이스는 생성자가 없다.

 

그렇다면 인터페이스명(){}는 무엇인가? 익명클래스를 작성함과 동시에 객체를 생성하도록하는 Java의 문법같은 것이다.

* 제네릭의 <>처럼... 가끔 매개변수는 ()인데 왜 <>를 왜쓰는 거냐고 묻는 사람들이 있는데 이것도 그냥 지원해주는 기능이라서인데 어떻게 설명할 수 있는 방법이 없다...

 

인터페이스를 구현하는 동시에 객체를 만들때 인터페이스명이 있는데 어쨰서 익명구현객체라고 하는지 궁금했었다.

원래는 클래스가 인터페이스를 구현한 후 인터페이스를 구현한 클래스로 객체를 만들어야하는데 위의 코드는 인터페이스를 바로구현해서 구현한 클래스명이 없이 객체를 만들기 때문에 익명구현객체라고 부르는 것이다. 즉, 여기서 익명이란 클래스명을 말한다. (인터페이스를 구현한 구현클래스명 없이 구현객체를 생성한다는 의미)

익명자식객체도 같다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

*어나니먼스

:

new 추상클래스명(){

바로구현

}

 

 

*중첩 인터페이스 도 어나니먼스가 가능하다... 헐