1. 내부 클래스란? (inner class) = 중첩 클래스
클래스 내부에 선언한 클래스로 이 클래스를 감싸고 있는 외부 클래스와 밀접한 연관이 있는 경우가 많고,
다른 외부 클래스에서 사용할 일이 거의 없는 경우에 내부 클래스로 선언해서 사용함
내부 클래스의 종류 :
1) 인스턴스 내부 클래스, 정적(static) 내부 클래스, 지역(local) 내부 클래스 (멤버 변수 (=인스턴스 변수)와 선언 위치, 생성주기 등 똑같음)
2) 익명(anonymous) 내부 클래스
2. - 인스턴스 내부 클래스
- 내부적으로 사용할 클래스를 선언 (private으로 선언하는 것을 권장, 안하면 외부에서도 사용가능하니까)
- 외부 클래스가 생성된 후 생성됨 ( 정적 내부 클래스와 다름 )
- private이 아닌 내부 클래스는 다른 외부 클래스에서 생성할 수 있음
<code />
OutClass outClass = new OutClass();
OutClass.InClass inClass = outClass.new InClass();
더보기
<java />
package ch01;
class OutClass{
private int num=10;
private static int sNum=20; //static 변수는 instance 변수보다 먼저 생성됨
private InClass inClass; //일단 변수로 만들고, 그것을 객체화하는 것이 new이다
public OutClass(){ //Out Class가 생성될 때
inClass=new InClass(); //// 내부 클래스 inClass가 생성된다.
}
private class InClass{ //Instance Inner Class는 OuterClass가 먼저 생성된 뒤 생성됨
int iNum=100;
// static int sINum=500; //static을 사용하려면, 정적 내부 클래스에서 사용해야 한다.
void inTest() {
System.out.println("OutClass num = " +num + "(외부 클래스의 인스턴스 변수)");
System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수)");
System.out.println("InClass inNum = " + iNum + "(내부 클래스의 인스턴스 변수)");
}
}
void usingClass() {
inClass.inTest(); //내부 클래스 변수를 사용하여 메서드 호출
}
}
public class InnerTest { //완전 완전 외부 클래스
public static void main(String[] args) {
OutClass outClass=new OutClass();
outClass.usingClass();
System.out.println();
// OutClass.InClass inner = outClass.new InClass(); //OutClass의 InClass에 private를 없애고 굳이 이렇게 쓸 수는 있음
// System.out.println("외부 클래스 변수를 이용하여 내부 클래스 생성");
// inner.inTest(); //private 안없애면 오류남
}
}
3. - 정적 내부 클래스
- 외부 클래스 생성과 무관하게 사용할 수 있음
- 정적 변수, 정적 메서드 사용
- 정적 내부 클래스 일반 메서드와 정적 메서드에서의 변수 사용

더보기
<java />
package ch01;
class OutClass{
private int num=10;
private static int sNum=20; //static 변수는 instance 변수보다 먼저 생성됨
private InClass inClass; //일단 변수로 만들고, 그것을 객체화하는 것이 new이다
public OutClass(){ //Out Class가 생성될 때
inClass=new InClass(); //// 내부 클래스 inClass가 생성된다.
}
private class InClass{ //Instance Inner Class는 OuterClass가 먼저 생성된 뒤 생성됨
int iNum=100;
// static int sINum=500; //static을 사용하려면, 정적 내부 클래스에서 사용해야 한다.
void inTest() {
System.out.println("OutClass num = " +num + "(외부 클래스의 인스턴스(멤버) 변수)");
System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수)");
System.out.println("InClass inNum = " + iNum + "(내부 클래스의 인스턴스(멤버) 변수)");
}
}
void usingClass() {
inClass.inTest(); //내부 클래스 변수를 사용하여 메서드 호출
}
static class InStaticClass {
int iNum=100;
static int sInNum=200;
void inTest() {
// System.out.println("OutClass num = " +num + "(외부 클래스의 인스턴스(멤버) 변수)") ;//외부 클래스의 멤버 변수 num이 아직 안만들어졌는데 호출될수도 있어서 에러남
System.out.println("InClass num = " +iNum + "(본인(정적), 즉 내부 클래스의 인스턴스(멤버) 변수)");
System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수)");
System.out.println("InClass inNum = " + sInNum + "(본인(정적), 즉 내부 클래스의 static 인스턴스(멤버) 변수)");
System.out.println("결론적으로 외부클래스의 인스턴스 변수는 사용할 수 없다. 생성이 안되어있을 수도 있으니까");
}
static void sTest() { //static 클래스의 static 메소드는 static 클래스가 생성되지 않아도 static 메소드가 호출 될 수 있다
// System.out.println("InClass num = " +iNum + "(본인(정적), 즉 내부 클래스의 인스턴스(멤버) 변수)"); //그래서 에러남
System.out.println("OutClass sNum = " + sNum + "(외부 클래스의 스태틱 변수)");
System.out.println("InStaticClass sInNum = " + sInNum + "(본인(정적), 즉 내부 클래스의 static 인스턴스(멤버) 변수)"); //그래서 에러남
}
}
}
public class InnerTest { //완전 완전 외부 클래스
public static void main(String[] args) {
OutClass outClass=new OutClass();
outClass.usingClass();
System.out.println();
// OutClass.InClass inner = outClass.new InClass(); //OutClass의 InClass에 private를 없애고 굳이 이렇게 쓸 수는 있음
// System.out.println("외부 클래스 변수를 이용하여 내부 클래스 생성");
// inner.inTest(); //private 안없애면 오류남
OutClass.InStaticClass sInClass = new OutClass.InStaticClass(); //외부 클래스에서 정적 클래스는 바로 생성 가능
sInClass.inTest();
System.out.println();
OutClass.InStaticClass.sTest(); //정적 메소드는 바로 호출 가능하며, 스태틱 변수만 사용 가능
}
}

4. - 지역 내부 클래스
- 지역 변수와 같이 메서드 내부에서 정의하여 사용하는 클래스
- 메서드의 호출이 끝나면 메서드에 사용된 지역변수의 유효성은 사라짐
- 메서드 호출 이후에도 사용해야 하는 경우가 있을 수 있으므로 지역 내부 클래스에서 사용하는 메서드의 지역 변수나 매개 변수는 final로 선언됨
- MyRunnable 클래스를 사용하려면 직접 생성하는 것이 아닌 getRunnable()메서드를 호출하여 생성된 개체를 반환 받아야 함
더보기
<java />
package ch01;
class Outer2{
int outNum = 100;
static int sNum = 200;
Runnable getRunnable(final int i) { //Runnable 인터페이스를 반환한다. 그래서 이 함수의 이름은 getRunnable
//Runnable 인터페이스 클래스를 스레드화 할 때 run을 제공함
final int num=10;
class MyRunnable implements Runnable{
int localNum=1000;
@Override
public void run() { //run은 getRunnable()을 받고 나면 또 호출될 여지가 있음
// i=50; //그때, 이러한 변수들이 없을 수도 있으니 여기서는 값을 변경할 수 없다(스택에 잡히면 안된다!)
// num=20; //그러므로 애초에 final로 선언하고, 변수를 아래처럼 갖다 쓰기만 한다.(외부 지역 변수를 쓸 때 조심!)
System.out.println("i =" + i);
System.out.println("num = " +num);
System.out.println("localNum = " +localNum);
System.out.println("outNum = " + outNum + "(외부 클래스 인스턴스 변수)");
System.out.println("Outter.sNum = " + Outer2.sNum + "(외부 클래스 정적 변수)");
}
}
return new MyRunnable();
}
}
public class AnonoymousInnerTest {
public static void main(String[] args) {
Outer2 out = new Outer2();
Runnable runner = out.getRunnable(100); //getRunnable의 스택에 잡혔던 멤버변수들이 이 명령어가 끝난 뒤 사라짐
runner.run(); //이렇게 다시 run()에서 멤버변수들이 불려져버리기 때문에, 상수화 해버린다.
}
}
5. - 익명 내부 클래스
- 이름이 없는 클래스 (위 지역 내부 클래스의 MyRunnable 클래스 이름은 실제로 호출되는 경우가 없음)
- 클래스의 이름을 생략하고, 주로 하나의 인터페이스나 하나의 추상 클래스를 구현하여 반환(아래 코드가 그 예시)
- 인터페이스나 추상 클래스 자료형의 변수에 직접 대입하여, 클래스를 생성하거나 지역 내부 클래스의 메서드 내부에서 생성하여 반환 할 수 있음.
- widget의 이벤트 핸들러에 활용됨(안드로이드 앱 개발 시)

더보기
지역 내부 클래스에서 쓴 코드에서,
MyRunnable()은 맨 끝에 리턴할 때밖에 쓰이지 않으니까
함수 이름을 지우고 새로
return new Runnable()로 바꿔주고,
끝 괄호에 ; 를 추가해준다.
<java />
package ch01;
class Outer2{
int outNum = 100;
static int sNum = 200;
Runnable getRunnable(int i) { //Runnable 인터페이스를 반환한다. 그래서 이 함수의 이름은 getRunnable
//Runnable 인터페이스 클래스를 스레드화 할 때 run을 제공함
int num=10;
return new Runnable(){
int localNum=1000;
@Override
public void run() {
System.out.println("i =" + i);
System.out.println("num = " +num);
System.out.println("localNum = " +localNum);
System.out.println("outNum = " + outNum + "(외부 클래스 인스턴스 변수)");
System.out.println("Outter.sNum = " + Outer2.sNum + "(외부 클래스 정적 변수)");
}
};
}
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Runnable class");
}
};
}
public class AnonoymousInnerTest {
public static void main(String[] args) {
Outer2 out = new Outer2();
Runnable runner = out.getRunnable(100); //getRunnable의 스택에 잡혔던 멤버변수들이 이 명령어가 끝난 뒤 사라짐
runner.run(); //이렇게 다시 run()에서 멤버변수들이 불려져버리기 때문에, 상수화 해버린다.
}
}
'JAVA' 카테고리의 다른 글
6-3.객체지향 프로그래밍 vs 람다식 차이 (0) | 2022.02.26 |
---|---|
6-2.람다식(Lambda expression)과 함수형 인터페이스 (0) | 2022.02.25 |
5-10.컬렉션 프레임워크 (0) | 2022.02.22 |
5-9.제네릭 메서드 (0) | 2022.02.21 |
5-8.T extends 클래스 (0) | 2022.02.21 |