💡 하나의 클래스 내부에 선언된 또 다른 클래스를 의미하며, 두 클래스가 서로 긴밀한 관계가 있거나, 하나의 클래스 또는 메소드에서만 사용되는 클래스일 때 이용되는 기법이다.
// Creature 클래스는 내부 클래스들의 외부 클래스
class Creature {
int life;
// Animal 클래스는 Creature 클래스의 내부 클래스
class Animal {}
// Insect 클래스는 Creature 클래스의 내부 클래스
class Insect {}
public void method() {
Animal animal = new Animal();
Insect insect = new Insect();
}
}
💡 클래스를 논리적으로 그룹화하며, 외부 클래스의 모든 멤버에 자유롭게 접근할 수 있고, 외부에서는 내부 클래스의 세부 구현을 감춰서 클래스 간의 상호작용만 고려하는 것과 같은 코드의 복잡성을 줄일 수 있다.
class Creature {
int life;
public void method() {
// Animal 객체는 오로지 Creature 클래스의 메소드 내에서만 사용된다는 가정
Animal animal = new Animal();
}
}
// 외부에 선언된 클래스
class Animal {}
class Creature {
int life;
// 클래스 멤버 같이 Creature 클래승 안에다 넣어 선언한다.
class Animal {}
public void method() {
Animal animal = new Animal();
}
}
class Creature {
private int life = 50;
// private class로 오로지 Creature 외부 클래스에서만 접근 가능한 내부 클래스로 설정
private class Animal {
private String name = "호랑이";
int getOuter() {
// 외부 클래스의 private 멤버를 제약 없이 접근 가능
return life;
}
}
public void method() {
Animal animal = new Animal();
// Getter 없이 내부 클래스의 private 멤버에 접근이 가능
System.out.println(animal.name); // 호랑이
// 내부 클래스에서 외부 클래스의 private 멤버를 출력
System.out.println(animal.getOuter()); // 50
}
public static void main(String[] args) {
Creature creature = new Creature();
creature.method();
}
}
class Outer {
class InstanceInner { ... } // 인스턴스 클래스
static class StaticInner { ... } // static 클래스
void method1(){
class LocalInner { ... } // local 클래스
}
}
class PocketBall {
// 인스턴스 변수
int size = 100;
int price = 5000;
// 인스턴스 내부 클래스
class PocketMonster {
String name = "이상해씨";
int level = 10;
// Error: 인스턴스 내부 클래스에는 static 변수를 선언할 수 없다.
// static int cost = 100;
// final static은 상수이므로 허용
static final int cost = 100;
public void getPoketMember() {
// 별다른 조치 없이 외부 클래스 멤버 접근 가능
System.out.println(size);
System.out.println(price);
// 내부 클래스 멤버
System.out.println(name);
System.out.println(level);
System.out.println(cost);
}
}
}
public class Main {
public static void main(String[] args) {
// 1. 외부 클래스를 인스턴스화 해주기
PocketBall ball = new PocketBall();
// 2. 외부클래스.내부클래스 형식으로 내부 클래스를 초기화하여 사용할 수도 있다
PocketBall.PocketMonster pocketmon = ball.new PocketMonster();
pocketmon.getPoketMember();
// 1 + 2
PocketBall.PocketMonster poketmon2 = new PocketBall().new PocketMonster();
}
}
// 외부 클래스
public class Main {
public print(String txt) {
System.out.println(txt);
}
// 내부 클래스
class Sub {
public print() {}
}
}
public class Main {
public void print(String txt) {
System.out.println(txt);
}
class Sub {
public void print() {
Main.this.print("외부 클래스 메소드 호출");
System.out.println("내부 클래스 메소드 호출");
}
}
}
public static void main(String[] args) {
Main.Sub s = new Main().new Sub();
s.print();
// 외부 클래스 메소드 호출
// 내부 클래스 메소드 호출
}
public class Main {
static Integer num = new Integer(0); // 정적 필드 변수
class InnerClass { } // 내부 인스턴스 클래스
static class InnerStaticClass { } // 내부 정적 클래스
public static void main(String[] args) {
// 정적 필드 변수는 유일해서 서로 같다.
Integer num1 = Main.num;
Integer num2 = Main.num;
System.out.println(num1 == num2); // true
// 생성된 내부 클래스 인스턴스는 서로 다르다.
Main.InnerClass inner1 = new Main().new InnerClass();
Main.InnerClass inner2 = new Main().new InnerClass();
System.out.println(inner1 == inner2); // false
// 생성된 내부 정적 클래스 인스턴스는 서로 다르다.
Main.InnerStaticClass static1 = new InnerStaticClass();
Main.InnerStaticClass static2 = new InnerStaticClass();
System.out.println(static1 == static2); // false
}
}
class PocketBall {
int size = 100;
static int price = 5000;
// static 내부 클래스
static class PocketMonster {
static String name = "이상해씨";
int level = 10;
public static void getPoketMember() {
// 외부 클래스 인스턴스 멤버 접근 불가능
// System.out.println(size);
// 외부 클래스 정적 멤버 접근 가능
System.out.println(price);
// 내부 클래스 멤버도 정적 멤버만 접근 가능
// System.out.println(level);
System.out.println(name);
}
}
}
public class Main {
public static void main(String[] args) {
// 정적 내부 클래스의 인스턴스는 외부 클래스를 먼저 생성하지 않아도 된다.
PocketBall.PocketMonster poketmon = new PocketBall.PocketMonster();
System.out.println(poketmon.level);
System.out.println(PocketBall.PocketMonster.name);
// 클래스.정적내부클래스.정적메소드()
PocketBall.PocketMonster.getPoketMember();
}
}
class PocketBall {
int size = 100;
int price = 5000;
public void pocketMethod() {
int exp = 5000;
// 메소드 내에서 클래스를 선언
class PocketMonster {
String name = "이상해씨";
int level = 10;
public void getPoketLevel() {
System.out.println(level); // 인스턴스 변수 출력
System.out.println(exp); // 메소드 지역 상수 출력
}
}
// 메소드 내에서 클래스를 선언
class PocketMonster2 {
String name = "리자몽";
int level = 50;
}
new PocketMonster().getPoketLevel();
System.out.println("메소드 실행 완료");
}
}
public void pocketMethod() {
int exp = 5000;
exp = 1;
// 메소드 내에서 클래스를 선언 (final 자동 붙음)
class PocketMonster {
/*final*/ String name = "이상해씨";
/*final*/ int level = 10;
public void getPoketLevel() {
System.out.println(level); // 인스턴스 변수 출력
// System.out.println(exp); // 메소드 지역 변수 출력 -> 컴파일 에러
}
}
}
public class Main {
public static void main(String[] args) {
// Object 클래스를 일회성으로 익명 클래스로 선언하여 변수 o에 저장
Object o = new Object() {
String t = "안녕";
@Override
public String toString() {
System.out.println(this.t);
return "내 마음대로 toString 바꾸기";
}
};
// 익명 클래스의 객체의 오버라이딩한 메서드를 사용
String txt = o.toString();
System.out.println(txt); // 내 마음대로 toString 바꾸기
}
}