Langauge/Java-basic
[Java] 이너 클래스
강잇
2022. 7. 22. 02:50
이너 클래스(Inner Class)
- 이너 클래스는 클래스 내부에 포함되는 클래스로, 외부 클래스와 내부 클래스가 서로 연관되어 있을 때 사용한다.
- 이너 클래스를 사용하면 접근 지정자와 관계없이 외부 클래스의 멤버들에 쉽게 접근할 수 있고, 코드의 복잡성을 줄일 수 있다.
- 또한 외부적으로 불필요한 데이터를 감출 수 있어 캡슐화를 달성하는데 유용하다.
- 이너 클래스는 인스턴스 멤버 이너 클래스, 정적 멤버 이너 클래스, 지역 이너 클래스로 나뉜다.
- 이너 클래스도 클래스이기 때문에 바이트 코드(.class) 파일이 생성된다.
// 외부 클래스
class Outer {
// 인스턴스 멤버 이너 클래스
class Inner {
}
// 정적 멤버 이너 클래스
static class StaticInner {
}
void run() {
// 지역 이너 클래스
class LocalInner {
}
}
}
인스턴스 멤버 이너 클래스와 정적 멤버 이너 클래스
- 클래스 내부에 멤버의 형태로 존재함
- 외부 클래스의 모든 접근 지정자의 멤버에 접근 가능
- 바이트 코드 파일명 : '아우터클래스명$이너클래스명.class'
- 이너 클래스 내부에서 아우터 클래스의 멤버를 참조하려면 '아우터 클래스명.this.'를 이용하여 참조할 수 있다.
- 정적 멤버 이너 클래스는 인스턴스 멤버 이너 클래스에 static 키워드가 붙은 것으로 static의 특성을 따른다.(정적 멤버, 객체 생성 없이 사용 등)
인스턴스 이너 클래스 객체 생성
- 인스턴스 이너 클래스는 아우터 클래스 내부에 있는 멤버처럼 활용된다.
- 따라서 이너 클래스의 인스턴스를 생성하기 위해서는 아우터 클래스의 인스턴스를 먼저 생성한 후 참조하여야 생성할 수 있다.
// 인스턴스 이너 클래스 객체 생성
아우터 클래스 참조 변수 = new 아우터 클래스(); // 아우터 클래스 인스턴스 생성
아우터 클래스.이너 클래스 참조 변수 = 아우터 클래스 참조 변수.new 이너 클래스();
// 예시
public class Main {
public static void main(String[] args) {
A a = new A();
A.B ab = A.new B(); // A 클래스 내부에 있는 B 생성자 호출
}
}
class A {
class B {
...
}
}
public class Main {
public static void main(String[] args) {
System.out.println("외부 클래스를 사용하여 이너 클래스 기능 호출");
Outer outer = new Outer();
outer.testClass();
System.out.println();
System.out.println("이너 클래스 객체 생성하여 이너 클래스 기능 호출");
Outer.InClass inner = outer.new InClass();
inner.Test();
inner.print();
}
}
class Outer {
private int num = 1;
private static int sNum = 2;
private InClass inClass;
public Outer() {
inClass = new InClass();
}
class InClass {
int inNum = 10;
void Test() {
System.out.println("Outer num = " + num + "(외부 클래스의 인스턴스 변수)");
System.out.println("Outer sNum = " + sNum + "(외부 클래스의 정적 변수)");
}
void print() {
System.out.println("Inner inNum = " + inNum + "(이너 클래스의 인스턴스 변수)");
}
}
public void testClass() {
inClass.Test();
inClass.print();
}
}
정적 이너 클래스 객체 생성
- 인스턴스 생성 방법은 인스턴스 이너 클래스와 동일
- 추가적으로 static 키워드로 인해 인스턴스 생성 없이 이너 클래스의 멤버를 사용할 수 있다.
// 정적 이너 클래스 객체 생성
아우터 클래스.이너 클래스 참조 변수 = new 아우터 클래스.이너 클래스();
// 객체 생성 없이 멤버 사용
아우터 클래스.이너 클래스.멤버;
// 예시
public class Main {
public static void main(String[] args) {
// 객체 생성 후 메서드 호출
A.B ab = new A.B();
ab.print();
// 객체 생성 없이 메서드 호출
A.B.print();
}
}
class Outer { // 아우터 클래스
static class StaticInner { // 정적 이너 클래스
static void print() {
System.out.println("정적 이너 클래스");
}
}
}
지역 이너 클래스
- 지역 이너 클래스는 메서드 내에서 정의되는 클래스다.
- 지역 이너 클래스는 선언 이후 바로 인스턴스를 생성하여 사용하며, 메서드가 호출될 때만 메모리에 로딩된다. -> 정적 클래스 선언 불가
- 바이트 코드 파일명 : '아우터 클래스$+숫자+지역 이너클래스.class'와 같이 생성된다.
숫자가 붙는 이유 : 지역 이너 클래스는 메서드 내에서만 정의하여 메서드 호출을 통해 인스턴스 생성 및 사용하므로 여러 개의 메서드에서 동일한 클래스명을 가진 지역 이너 클래스를 생성할 수 있다.
따라서 동일한 클래스명을 가진 바이트 코드가 생성되었을 때 구분하기 위해 숫자로 마킹하는 것.
지역 이너 클래스 객체 생성
- 아우터 클래스 멤버 + 자신이 정의된 메서드 지역 변수 사용 가능
- 지역 이너 클래스에서 지역 변수를 사용하기 위해서는 해당 지역 변수가 final로 선언돼 있어야 하며, final로 선언되지 않은 지역 변수는 지역 이너 클래스에서 사용할 때 컴파일러가 강제로 final로 선언한다.
- -> 클래스 메모리와 스택 메모리의 차이로, 메서드의 지역 변수는 메서드가 종료되면 소멸되는 특징을 가지고 있다.
따라서 소멸된 지역 이너 클래스의 인스턴스가 소멸된 지역 변수를 참조하려는 경우가 발생할 수 있기 때문에 final로 선언하여 변수를 따로 관리하게 하는 것.
// 지역 이너 클래스의 객체 생성
지역 이너 클래스 참조 변수 = new 지역 이너 클래스();
class A {
void abc() {
class B {
}
B b = new B();
}
}
class Outer { //외부 클래스
int num = 5;
void test() {
int num2 = 6;
class LocalInClass { //지역 내부 클래스
void getPrint() {
System.out.println(num);
System.out.println(num2);
}
}
LocalInClass localInClass = new LocalInClass();
localInClass.getPrint();
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
outer.test();
}
}