Java Study #13 - 이너 클래스와 이너 인터페이스

allzeroyou·2022년 2월 25일
0

Java

목록 보기
13/14

클래스 안에는 단 4개만 들어갈 수 있는데,
1. 필드
2. 메서드
3. 생성자
4. 클래스(이너)
가 해당된다.

이때, 이너클래스는 클래스 내부에 포함되는 인스턴스 멤버 이너 클래스, 정적 멤버 이너 클래스 그리고 지역 이너 클래스로 나뉜다.

  • 인스턴스 멤버와 정적 멤버 이너 클래스는 필드, 메서드 처럼 클래스의 멤버

  • 지역 이너 클래스메서드 내에서 정의!

인스턴스 멤버 이너 클래스

인스턴스 멤버 이너 클래스는 이름에서 처럼 인스턴스, 즉 객체 내부에 멤버 형태로 존재.
접근지정자와 상관없이 아우터 클래스의 멤버를 사용할 수 있다는 점을 활용하기 위해 보통 이너 클래스를 사용.

🔧 디스플레이 클래스 내부에서 색 조정 클래스를 포함하는 것처럼 이너 클래스가 아우터 클래스와 밀접하게 관련돼 있을 때 주로 사용

소스파일(.java)이 1개라 하더라도 컴파일 하면 각 클래스별로 바이트 코드(.class) 파일이 생성되는데, 이너 클래스도 마찬가지.

class 아우터 클래스{
	class 이너 클래스{
    	//...
    }
 }

위에 코드를 생각해보자.
아우터 클래스의 바이트 코드로 '아우터 클래스.class', 인스턴스 멤버 이너 클래스의 바이트 코드로 '아우터 클래스$이너 클래스.class'파일이 생성됨

이너클래스는 파일명처럼 독립적으로 사용 x, 아우터 클래스를 사용해야만 사용 가능.

인스턴스 이너 클래스 객체 생성하기

  1. 이너 클래스의 객체를 생성하기 위해서 먼저 아우터 클래스의 객체를 생성
  2. 생성한 아우터 클래스 객체의 참조변수를 이용해 객체 내붸 이너 클래스 생성자를 다음과 같이 호출!

🔧 이는 인스턴스 필드나 메서드를 사용하기 위해 클래스의 객체를 먼저 생성하는 것과 같은 원리

인스턴스 멤버 이너 클래스의 객체 생성 방법

**아우터 클래스 아우터 클래스 참조 변수 = new 아우터 클래스();
**아우터 클래스.이너 클래스 이너 클래스 참조변수 = 아우터 클래스 참조 변수.new 이너 클래스();

실습

B 클래스를 A 클래스의 뱃속에 넣음으로써 특별히 A 클래스의 객체생성없이 B 에서 필드, 메서드 사용 가능.
클래스 A의 1번째 멤버는 필드, 2번째 멤버는 메서드, 3번째 멤버는 (인스턴스 이너)클래스이기에 멤버 사이에서 변수 사용이 자유로움.


인스턴스 이너클래스에서 멤버의 접근법은 그냥 가져다가 쓰면 되는 것.


A 객체가 생성되야 A 클래스 안 인스턴스 멤버 사용이 가능.
a라는 객체 생성 후 이제서야 b 클래스의 생성자 호출 가능.(바로는 불가)
마치, 껍데기(a) 객체가 만들어져야 안(b)을 사용할 수 있는 거라고 생각하자.

이때 B의 사용가능한 객체의 목록을 보면 a의 클래스 멤버를 사용가능함.
d는 private한 접근지정자이기에 그 클래스 안에서만 사용 가능해, 목록에 없음.


이너클래스 객체를 생성할 때, 자료형이 B가 아니라 A.B이다.
생성되는 바이트 코드가 A$B.class이기 때문이다.
이너클래스는 B b 와 같이 단독 불가. 반드시 A.B b와 같이 아우터 클래스를 이용해야 선언가능.

마찬가지로 a 클래스 안에 있는 걸 객체로 만들기때문에, new A.B가 아니라 a.new B()이다.


만들어진 객체 b의 메서드를 호출하면 인스턴스 이너클래스의 메서드를 호출한다.

아우터 클래스 객체 참조하기

인스턴스 이너 클래스에서는 아우터 클래스의 모든 멤버를 자기 것인양 사용 가능!
그렇다면 아우터 클래스의 필드나 메서드의 동일한 이름을 이너 클래스 안에 정의했을 때 어느 것을 참조할까?(아우터 / 이너)

당연히 이너 클래스의 필드/메서드가 참조되는데, 참조 객체 명을 생략할 때는 컴파일러가 알아서 this 키워드를 추가.

이너 클래스 내부에서 아우터 클래스의 멤버를 참조하고 싶을 때는
> 아우터 클래스명.this

를 명시적으로 붙여 사용.

클래스 B의 내부에서 bcd메서드에서 a,b,abc() 처럼 참조 변수 없이 멤버를 사용하거나 this.a/this.b/this.abc()를 사용하면 B 클래스의 필드 a,b/ 메서드 abc()를 의미.

아우터 클래스의 필드/메서드를 참조하기 위해서는 A.this.a/A.this.b/A.this.abc()처럼 앞에 아우터 클래스명.this를 꼭 붙여준다.

정적 멤버 이너 클래스

이너 클래스 앞에 static 키워드가 붙은 것.
아우터 클래스의 정적 멤버에만 접근 가능, 정적의 특성임.

즉, 아우터 클래스의 객체를 생성 하지 않아도 정적 이너 클래스의 객체를 생성해 사용할 수 있어야 하므로 아우터 클래스의 멤버 중 객체 생성 없이 바로 사용할 수 있는 정적 멤버만 정적 이너 클래스 내부에서 사용 가능

정적 이너 클래스 객체 생성

컴파일 이후 인스턴스 멤버 클래스와 동일하게 '아우터 클래스.class', '아우터 클래스$이너 클래스.class'의 바이트 코드 파일 생성.
정적 이너 클래스도 말 그래도 정적 멤버이므로 클래스명으로 바로 접근 가능!

정적 멤버 이너 클래스의 객체 생성 방법
아우터 클래스.이너 클래스 이너 클래스 참조 변수 = new 아우터 클래스.이너 클래스();


static의 기본의미는 객체를 생성하지 않아도 사용 가능!
outer class의 static이라고 선언된 것은 inner class 내부에서 객체를 생성하지 않아도 사용가능.
but 인스턴스 멤버들은 객체를 만들어야 사용가능하기 때문에 오류 발생.

즉, static은 static만 포함 가능!

인스턴스 이너 클래스에서 객체를 만드려면, outer class의 객체를 먼저 생성한 다음 이너 클래스의 객체를 생성해야 했다.
그러나 static 이너 클래스의 경우, 객체를 생성하지 않고 바로 객체 사용이 가능하다.
하지만 inner class이기에 type은 항상 outer class의 이름.inner class의 이름임을 잊지 말자.

지역 이너 클래스

클래스의 멤버가 아닌 메서드 내에서 정의되는 클래스.
지역 변수 처럼 정의된 메서드 내에서 사용 가능.
일반적으로 선언 이후 바로 객체를 생성해 사용하며 메서드가 호출될 때만 메모리에 로딩.
그러면 정적 클래스가 될 수 없는데, 그 이유는 객체가 생성돼야 사용할 수있는 메서드가 호출되기 전에 정적 영역에 로딩될 수 있어야 하기 때문이다.

지역 클래스를 컴파일하면 생성되는 클래스 명이 조금 독특하다.
'아우터 클래스$+숫자+지역 이너 클래스.class'와 같이 지역 이너 클래스 이름 앞에 숫자가 포함된 이름으로 클래스 파일이 생성된다.
이때, 왜 숫자가 포함될까?
지역 이너 클래스는 메서드 내에서만 정의하고 사용 가능하다. 여러 개의 메서드에서 동일한 이름의 지역 이너 클래스를 생성할 수도 있기 때문에 숫자를 포함, 동일한 지역 클래스명이 있을 때 1부터 증가한다.

지역 이너 클래스 객체 생성하기

다른 이너 클래스처럼 아우터 클래스 멤버를 접근 지정자와 상관없이 사용 가능.
추가로 자신이 정의된 메서드의 지역변수도 클래스 내부에서 사용할 수 있음.
단, 지역변수를 사용할 때는 반드시 해당 지역 변수가 final로 선언되어야!
만일 final로 선언되지 않은 지역변수를 사용시 컴파일러가 강제로 해당 지역 변수에 final 부여
지역 이너 클래스는 클래스가 정의된 메서드 안에서만 객체 생성 가능.

지역 이너 클래스의 객체 생성 방법
지역 이너 클래스 지역 이너 클래스 참조 변수 = new 지역 이너 클래스();

b는 abc메서드의 지역변수이다. 따라서 final 선언이 되는데, 지역 이너 클래스에서 값을 변경하려고 하니 오류 발생.

지역 이너 클래스명이 중복될 때 클래스가 어떻게 생성될까?
실제 생성되는 class 파일을 보러 가보자.


지역이너클래스는 숫자가 붙는걸 알 수 있고, 먼저 만들어진 c 클래스에 1이, 나중에 중복된 이름으로 만들어진 c 클래스 앞에 2가 붙은 걸 확인.

익명 이너 클래스

말 그대로 '이름을 알 수 없는' 이너 클래스이다.
정의된 위치에 따라 분류 할 수 있는데, 클래스의 중괄호 바로 아래 사용했을 때는 인스턴스 익명 이너 클래스, 메서드 내부에서 사용 했을때는 지역 익명 이너 클래스.

정적 익명 이너 클래스는 왜 없나요?
정적 이려면 객체 생성 없이 클래스 명만으로 객체를 생성해야 하는데, 익명 이너 클래스의 경우 이름을 알 수 없기 때문이다!

익명은 내가 자식클래스가 만드는것이 아닌 컴파일러가 자식클래스를 만들면서 이름을 알려주지 않기 때문에, 익명 클래스이다.


C는 인터페이스라 객체를 생성을 못하기때문에, 자식클래스이자 인스턴스 이너클래스인 B를 만들었고 C를 보충해서 객체로 사용했다.


a클래스의 abc 메서드를 호출하게 되면,
1. a 클래스의 abc 메서드로 간다.
2. c.bcd()에 의해 인터페이스의 bcd()메서드로 간다.
3. bcd()는 abstract 메서드라 완성된 메서드로 간다. b 클래스의 bcd()메서드가 출력된다.


C를 상속받아 만든 자식클래스의 이름을 모르기때문에 익명클래스임.
익명이너클래스인 이유는, 앞의 b를 대체하는 클래스이고 클래스 밖이 아닌 안에 만들었기 때문에.

익명 이너 클래스를 활용한 인터페이스 타입의 입력매개변수 전달

인터페이스 타입의 입력매개변수로 익명 이너 클래스를 이용해 생성한 객체를 전달하는 방법을 알아보자.

A타입의 객체를 생성해야 한다.


객체를 생성하면, b 클래스의 메서드로 implements되어 매개변수 전달이 출력.


참조변수를 단 한번만 쓸거면 굳이 만들지 않고 매개변수로 바로 전달하는 방법이 있다.


A라고 하는 객체를 cde()를 사용하기위해 만들어야한다.

객체 만드는 2가지 방법
1. 자식클래스
2. 익명이너클래스 생성

참조변수명에 담거나 담지 않는 방법이 있다.
실제로는 4번을 많이 사용(이벤트 처리 등)

이너 인터페이스

이너 클래스와 마찬가지로 인터페이스를 클래스내부에 정의하는 것은 해당 클래스에 의존적인 기능을 수행할 때.

이너 인터페이스의 정의와 특징

주요한 특징은 정적 이너 인터페이스만 존재 가능.
만일 static 제어자를 생략 시 컴파일러가 자동 추가.
컴파일하면 이너 클래스와 같이 '아우터 클래스 명$이너 인터페이스명.class'형태로 바이트 코드인 .class 파일이 생성됨.


객체 생성 방법은 2가지 방법이 있는데, 첫번째는 자식클래스 직접 생성이다.
이때 어느 이너클래스/인터페이스와 마찬가지로 사용할때, 이름 앞에 아우터클래스명.을 적는걸 잊지말자.
그 후 main 메서드에서 객체를 생성할때 type은 A.B / C가 둘다 되는 이유는 다형성 덕분.


두번째는 익명이너클래스 생성이다.
이때 객체를 생성할때 static 이너 인터페이스이기 때문에, 겉 껍데기(아우터 클래스)의 객체를 생성하지 않고도 객체를 만들 수 있다.

profile
모든 건 zero 부터, 차근차근 헛둘헛둘

0개의 댓글