Inner class

Inner class(내부 클래스)
inner class는 말 그대로 “클래스 내부에 선언한 클래스” 이다.
이는 외부 클래스와 밀접한 관련이 있거나, 다른 외부 클래스와 협력할 일이 없는 경우에 사용한다.
class ABC{ // 외부 클래스
class In{ // 인스턴스 내부 클래스
static class SIn{ // 정적 내부 클래스
}
}
public void abc(){
class Local{ // 지역 내부 클래스
}
}
}
내부클래스에는
인스턴스 내부 클래스, 정적 내부 클래스, 지역 내부 클래스, 익명 내부 클래스가 있다.
    -Instance inner class
내부 클래스는 인스턴스 변수를 선언할 때와 같은 위치에 선언하며,
외부 클래스 내부에서만 생성하여 사용하는 객체를 선언할 때 쓴다.
(다른 외부 클래스에서 사용할 일이 없는 경우 내부 인스턴스 클래스로 정의)
InnerTest.java
package innerclass;
//외부 클래스
class OutClass {
private int num=10;
private static int sNum=20;
//내부 클래스 자료형 변수를 먼저 선언
private InClass inClass;
//외부클래스 생성 후 내부클래스 사용 가능
public OutClass() {
inClass=new InClass();
}
class InClass{
int inNum=100;
//인스턴스 내부클래스에는 static 변수선언 불가능
void inTest() {
System.out.println("OutCLass num= "+num+"(외부클라스의 인스턴스 변수)");
System.out.println("OutCLass sNum= "+sNum+"(외부 클래스의 정적 변수)");
}
//인스턴스 내부클래스에는 static 함수 선언 불가능
}
public void usingClass() {
inClass.inTest();
}
}
public class InnerTest{
public static void main(String[] args) {
OutClass outClass=new OutClass();
System.out.println("외부 클래스 이용하여 내부 클라스 기능 호출");
outClass.usingClass();
}
}
외부 클래스가 선언된 이후에 내부 클래스가 선언되어야 한다.
따라서 클래스의 생성과 상관없이 사용할 수 있는 정적 변수는 인스턴스 내부 클래스에서 선언할 수 없다.
private 이 아닌 내부클래스의 경우 외부 클래스가 아닌 다른 클래스에서도
아래와 같이 내부 클래스를 생성할 수 있다.
OutClass outClass=new OutClass();
OutCLass.InClass inClass=OutClass.new InClass();
따라서 어떤 클라스 내부에서만 사용할 목적이라면 내부 클래스를
private으로 선언해야 한다.
    -static inner class
내부 클래스가 외부 클래스 생성과 무관하게 사용할 수 있어야 하고,
정적 변수도 사용할 수 있어야 한다면 정적 내부 클래스를 사용하면 된다.
InnerTest.java
package innerclass;
class OutClass {
private int num=10;
private static int sNum=20;
static class InStaticClass{
int inNum=10;
static int sInNum=20;
//정적 내부 클래스의 일반 메서드
void inTest() {
//외부 클래스의 인스턴스 변수는 사용할 수 없다.
System.out.println("InStaticClass inNum= "+inNum+"(내부 클래스의 인스턴스 변수 사용)");
System.out.println("InStaticClass sInNum= "+sInNum+"(내부 클래스의 정적 변수 사용)");
System.out.println("OutClass sNum= "+sNum+"(외부 클래스의 정적 변수 사용");
}
//정적 내부 클래스의 정적 메서드
static void sTest() {
//외부 클래스와 내부 클래스의 인스턴스 변수는 사용할 수 없다.
System.out.println("OutClass sNum= "+sNum+"(외부 클래스의 정적 변수 사용");
System.out.println("InStaticClass sInNum= "+sInNum+"(내부 클래스의 정적 변수 사용)");
}
}
}
public class InnerTest{
public static void main(String[] args) {
OutClass.InStaticClass sInClass=new OutClass.InStaticClass();
System.out.println("정적 내부 클래스 일반 메서드 호출");
sInClass.inTest();
System.out.println();
System.out.println("정적 내부클래스의 정적 메서드 호출");
OutClass.InStaticClass.sTest();
}
}
정적 내부 클래스는 외부 클래스를 생성하지 않고 바로 정적 내부 클래스 생성 가능하다.
정적 내부 클래스 메서드 변수 유형 사용 가능 여부
일반 메서드
void inTest()
외부 클래스의 인스턴스 변수(num) X
외부 클래스의 정적 변수(sNum) O
정적 내부 클래스의 인스턴스 변수(inNum) O
정적 내부 클래스의 정적 변수(sInNum) O
정적 메서드
static void aTest()
외부 클래스의 인스턴스 변수(num) X
외부 클래스의 정적 변수(sNum) O
정적 내부 클래스의 인스턴스 변수(inNum) X
정적 내부 클래스의 정적 변수(sInNum) O
정적 내부 클래스는 외부 클래스를 생성하지 않고도 내부 클래스 자료형으로 바로 선언하여 생성할 수 있다.
OutClass.InStaticClass sInClass=new OutClass.InStaticClass();
또한 정적 내부 클래스에 선언한 메서드나 변수가 private이 아닌 경우 다른 클래스에서 바로 사용할 수 있다.
OutClass.InstaticClass.sTest();
    -Local inner class
지역 내부 클래스는 지역 변수처럼 메서드 내부에 클래스를 정의하여 사용하는 것을 말한다.
LocalInnerTest.java
package innerclass;
class Outer{
int outNum=100;
static int sNum=200;
Runnable getRunnable(int i) {
int num=100;
//지역 내부 클래스
class MyRunnable implements Runnable{
int localNum=100;
@Override
public void run() {
//지역 변수는 상수로 바뀌므로 num은 변경 불가능하다.
//매개변수 지역 변수처럼 상수로 빠뀌므로 i는 변경 불가능하다.
System.out.println("i ="+i);
System.out.println("num= "+num);
System.out.println("localNum= "+localNum);
System.out.println("outNum= "+outNum+"(외부 클래스 인스턴스 변수");
System.out.println("Outer.sNum= "+Outer.sNum+"(외부 클래스 정적 변수)");
}
}
return new MyRunnable();
}
}
public class LocalInnerTest {
public static void main(String[] args) {
Outer out=new Outer();
Runnable runner=out.getRunnable(10);
runner.run();
}
}
지역 내부 클래스에서는 지역 변수와 매개변수가 상수로 바뀌므로 변경할 수 없다.

java 7 이전에는 지역 내부 클라스는 상수만을 처리할 수 있었다.
하지만 java 8부터는 자동으로 컴파일 할 때 final 예약어가 추가 된다.
    -Anonymous inner class
익명 클래스는 클래스 이름을 사용하지 않는 클래스이다.
아래의 코드는 이전의 LocalInnerTest.java의 MyRunnalbe(int i) 메서드를
익명 클래스로 표현한 코드이다.
package innerclass;
class Outer2{
//익명 내부 클래스, Runnable 인터페이스 생성
Runnable getRunnable(int i) {
int num=100;
return new Runnable() {
@Override
public void run() {
System.out.println(i);
System.out.println(num);
}
}; // 세미콜론(;)을 이용하여 익명 클래스의 끝을 알림
}
//인터페이스나 추상 클래스형 변수를 선언하고클래스를 생성해 대입하는 방법
Runnable runner=new Runnable() {
@Override
public void run() {
System.out.println("Runnable이 구현한 익명 클래스 변수");
}
}; // 세미콜론(;)을 이용하여 익명 클래스의 끝을 알림
}
public class AnonymousInnerTest {
public static void main(String[] args) {
Outer2 out=new Outer2();
Runnable runnerable=out.getRunnable(10);
runnerable.run();
out.runner.run();
}
}
익명 내부 클래스는 예전에 자바 UI에서 이벤트를 처리하는데 많이 사용하였다.
현재는 안드로이드 프로그래밍에서 위젯의 이벤트를 처리하는 핸들러를 구현할 때 사용한다.

아래 코드는 버튼을 눌렀을 때 “hello” 메세지를 출려하는 코드이다.
(버튼을 누르면 발생하는 이벤트 핸들러는 new View.OnCLickListener() 이다.
botton1.setOnClickListener=new View.OnClickListener(){
public boolean onClick(View v){
Toast.maekText(getBaseContext(),"hello",Toast.LENGTH_LONG).show();
return treu;
}
};
종류 구현 위치 사용할 수 있는
외부 클래스 변수
생성 방법
인스턴스 내부 클래스 외부 클래스 멤버 변수와 동일 외부 인스턴스 변수
외부 전역 변수
외부 클래스를 먼저 만든
후 내부 클래스 생성
정적 내부 클래스 외부 클래스 멤버 변수와 동일 외부 전역 변수 외부 클래스와 무관하게 생성
지역 내부 클래스 메서드 내부에 구현 외부 인스턴스 변수
외부 전역 변수
메서드를 호출할 때 생성
익명 내부 클래스 메서드 내부에 구현
변수에 대입하여 직접 구현
외부 인스턴스 변수
외부 전역 변수
메서드를 호출할 때 생성되거나,
인터페이스 타입변수에 대입할
 때 new 예약어를 통해 생성