[Java] - 내부 클래스
* 내부클래스(inner class)
: 클래스 내부에 선언한 클래스로 클래스 내부에서만 사용할 목적으로 만든다.(외부 클래스에서만 사용하기 위해)
↔ 외부클래스(out class)
<내부 클래스 종류>
=> 내부 클래스의 유형은 변수의 유형과 유사
- 인스턴스 내부 클래스(instance inner class)
- 외부 클래스 생성 후 내부 클래스 생성
- 내부 클래스에 정적 변수, 정적 메서드 사용 불가 -> 인스턴스가 동적으로 생성되기 때문에(인스턴스보다 먼저 정적 변수가 만들어질 수 없다.)
//외부 클래스
class OutClass{
//멤버 변수
private int num = 10; //외부클래스 - private 변수
private static int sNum = 20; //외부클래스 - 정적 변수
private InClass inClass; //내부클래스 자료형 변수 (외부클래스 디폴트 생성자에서
//쓰려고)
//외부클래스 - 디폴트 생성자
public OutClass() {
inClass = new InClass(); //외부클래스 생성된 후 내부클래스 쌩성
}
//내부 클래스 (인스턴스 내부 클래스 : 인스턴스가 위치하는 곳에 있는 클래스)
class InClass{
//멤버 변수
int inNum = 100; //내부클래스 - 인스턴스 변수
// static int sInNum = 200; //인스턴스 내부 클래스에 정적 변수 선언 불가
//메서드
//1 - 테스트 일반 메서드
void inTest()) { //인스턴스 내부 클래스의 일반 메서드
System.out.println("외부클래스 인스턴스 변수 num: " + num);
System.out.println("외부클래스 스태틱 변수 sNum: " + sNum);
}
//2 - 테스트 스태틱 메서드(불가능)
// static void sTest() {} //인스턴스 내부 클래스에 정적 메서드 선언 불가
//3 - 클래스 사용 일반 메서드
public void usingClass() {
inClass.inTest();
}
}
}
* 멤버변수와 인스턴스 변수는 같은말
- 외부 클래스 참조변수 -> 내부클래스 메서드 사용 가능
// 외부 클래스로 내부클래스 메서드 접근
OutClass outClass = new OutClass();
outClass.usingClass();
- 내부 클래스 private아니면 다른 클래스에서도 생성이 문법적으로는 가능함 (보통은 public보다 private으로 내부클래스가 있는 외부클래스에서만 사용함)
//외부클래스에서 내부클래스를 참조하여 내부클래스의 인스턴스를 생성해 쓸 수 있음
OutClass outClass = new OutClass());
OutClass.InClass inClass = outClass.new InClass();
- 클래스 내부에서만 사용할 목적이면 private 선언
- 정적 내부 클래스(static inner class)
: 외부 클래스의 생성과 무관하며 ( static은 new 생성 없이 클래스명으로 직접접근이 가능하다.)
정적 변수, 정적 메서드를 사용한다.
<정적 내부 클래스 메서드에서 변수 사용 여부>
class OutClass{
//외부클래스 변수
private int num = 10; //인스턴스 변수
private static int sNum = 20; //정적 변수 = static 변수
//정적 내부 클래스
static class InStaticClass{
//정적 내부 클래스 인스턴스변수
int inNum = 100;
// 정적 내부 클래스 정적변수
static int sInNum = 200;
//정적 내부 클래스의 일반메서드
void inTest() {
//num += 10; 외부클래스의 인스턴스 변수는 사용 못함
System.out.println("정적내부클래스 인스턴스 변수 inNum: "+ inNum);
System.out.println("정적내부클래스 스태틱 변수 sInNum: "+ sInNum);
System.out.println("외부클래스 스태틱 변수 sNum: "+ sNum);
}
//정적 내부 클래스의 static메서드
static void sTest() {
//num += 10; 외부클래스의 인스턴스 변수는 사용 못함
//inNum += 10; 정적 내부 클래스의 인스턴스 변수는 정적 메서드에서 사용 못함
System.out.println("외부클래스 스태틱 변수 sNum: "+ sNum);
System.out.println("정적내부클래스 스태틱 변수 sInNum: "+ sInNum);
}
}
}
OutClass( InStaticclass ( void inTest(), static void sTest() )
=> 외부클래스 안에 정적 내부클래스, 정적 내부클래스 안에 일반 메서드와 정적 메서드가 있는 상태
★static은 static끼리 써야한다.
여기서 void inTest() 일반 메서드의 경우
- 정적 내부 클래스에서 선언된 모든 변수(정적 변수, 인스턴스 변수)를 사용할 수 있지만
- 정적 내부 클래스 밖에서(외부클래스) 선언된 인스턴스 변수는 사용할 수 없다.
=> 왜냐하면 인스턴스 변수가 정적 내부클래스를 거쳐서 일반메서드로 들어와야하는데 정적 내부 클래스는 외부의 인스턴스 변수를 사용할 수 없기 때문이다. (외부 클래스의 static변수는 정적 내부클래스를 거쳐도 아무 문제가 없으므로 void inTest()에서 사용 가능하다.)
+) static 클래스에 인스턴스 변수를 선언하는 것은 가능하나 사용은 불가능!
✔️다른 클래스에서 정적 내부 클래스 사용하는 방법
1. 외부 클래스 생성 없이 바로 정적 내부클래스 생성가능
OutClass.InStaticClass sInClass = new OutClass.InStaticClass();
2. 정적 내부 클래스에 선언한 메서드, 변수가 private아닌 경우 바로 사용
OutClass.InStaticClass.sTest();
- 지역 내부 클래스(local inner class)
: 메서드 내부에서 정의하여 사용하는 클래스 (지역 변수처럼)
* 지역 변수
- 함수 호출과 동시에 Stack영역에 생성되고 함수가 끝남과 동시에 Stack영역에서 소멸된다.
*지역 내부 클래스에서 사용하는 메서드의 지역변수
- 클래스이기 때문에 지역변수는 final로 선언되어 Stack영역이 아닌 상수풀로 옮겨진다. 따라서 함수가 끝나도 소멸되지 않는다. => 지역변수 값 변경 불가
=> 메서드 안에 클래스가 있으면 지역변수는 상수가 된다!
📄예제) Runnable 인터페이스 구현
*MyRunnable은 Runnable인터페이스를 구현한 지역 내부클래스
+) Runnable인터페이스 : 스레드를 구현하기 위한 템플릿
//외부 클래스
class Outer {
//멤버변수
int outNum = 100; //외부클래스 - 인스턴스 변수
static int sNum = 200; //외부클래스 - 정적 변수
//Runnable 메서드 - run()메서드 재정의 필요
Runnable getRunnable(int i) { //지역변수 : num, i
//지역변수
int num = 100;
//내부 클래스(지역 내부 클래스 : 메서드 안 지역변수가 위치하는 곳에 있음)
class MyRunnable implements Runnable {
//멤버변수
int localNum = 10; //내부클래스 - 인스턴스 변수
//메서드 - run() 구현
@Override
public void run() {
// num = 200; //지역변수가 상수로 바뀌어 값 변경 불가
// i = 100; //지역변수가 상수로 바뀌어 값 변경 불가
System.out.println("i =" + i); //참조는 가능
System.out.println("num =" + num); //참조는 가능
}
}
return new MyRunnable(); //메서드의 반환형이 Runnable이므로
} //Runnable형의 객체 생성 후 반환해야함
}
*MyRunnable 지역 내부 클래스 사용
Outer out = new Outer();
Runnable runner = out.getRunnable(10); //Runnable 형 객체로 getRunnable()호출
runner.run();
=> getRunnable()메서드 호출이 끝났으므로 지역변수는 지워져 더이상 참조할 수 없어야하는데 run()메서드에서 지역변수 i, num이 사용된다. 이게 가능한 이유는 지역변수가 상수로 처리되기 때문이다.
- 익명 내부 클래스(anonymous inner class)
: 이름이 없는 클래스로 주로 하나의 인터페이스나 하나의 추상 클래스를 구현하는데 사용(일회용)
(안드로이드 위젯의 이벤트 핸들러 구현에 많이 씀)
*이벤트 핸들러 : 사용자의 터치, 버튼 클릭, 텍스트 입력 등 사용자와의 상호작용 이벤트를 처리하는 코드 블록 또는 함수
<생성하는 방법>
- 지역 내부 클래스가 있는 메서드(Runnable getRunnable()) 내부에서 생성(생성자를 반환하여 생성)
//외부 클래스
class Outter2 {
//메서드 - Runnable형
Runnable getRunnable(int i) {
//지역 변수
int num = 100;
//내부 클래스(익명 내부 클래스 : 이름 없는 클래스)
return new Runnable() { //MyRunnable 클래스 이름 빼고 클래스 바로 생성
//메서드 - run()구현
@Override
public void run() {
System.out.println(i);
System.out.println(num);
}
}; //익명 내부 클래스 끝에 세미콜론 붙여줘야함
}
}
=> 사용 : getRunnable() 호출 후 내부 클래스 메서드인 run()참조
Outter2 out = new Outter2();
Runnable runnerable = out.getRunnable(10); //인스턴스 생성
reunnerable.run();
- 인터페이스/추상클래스 자료형의 변수에 직접 대입하여 생성
//외부 클래스
class Outter2 {
//메서드 - Runnable형
Runnable getRunnable(int i) {
//지역 변수
int num = 100;
//내부 클래스(익명 내부 클래스 : 이름 없는 클래스)
Runnable runner = new Runnable() { //인스턴스 바로 생성
@Override
public void run() {
System.out.println("Runnable이 구현된 익명 클래스 변수");
}
}; //익명 내부 클래스 끝에 세미콜론 붙여야 함
}
}
=> 사용 : 외부클래스의 내부클래스를 참조하고 내부클래스의 run()을 참조
Outter2 out = new Outter2();
out.runner.run();
- 내부 클래스 요약