
프로그래머스 데브코스 3주차 진행 중, 배운 자바 문법
내용이 너무 많아서 중간으로 뺀 Inner Class
내부 클래스 (중첩 클래스)
하나의 클래스(Outer) 내부에 선언된 또 다른 클래스(Inner)!
보통 두 클래스가 서로 긴밀한 관계 or 하나의 클래스또는 메소드에서만 사용되는 클래스
class Creature {
private int age = 50;
private class Animal {
private String name = "오리";
int getOuter() { return age; }
}
public void method() {
Animal animal = new Animal();
System.out.println(animal.name);
System.out.println(animal.getOuter());
}
}
보통 우린 Creature 클래스를 부모 클래스로 두고, Animal 클래스가 extends를 통해 상속을 받는다.
근데 Animal 클래스를 필요는 한데, 한번만 사용하고 안쓸거면 굳이 클래스를 만들 이유가 없지 않은가?
그래서 Inner(내부)로 선언하면 편리하다.
방금 위에서 말했던 예시다.
보통 동물-오리 관계는 동물이 더 큰(부모) 범위고, 오리는 그 안에 속해있는(자식) 종류다.
그럼 자바의 extends를 통해 파일을 따로 만들어서 상속을 받아야 한다.
하지만 Inner Class로 선언하게 되면, 함께 관리하는 것이 가능해 유지보수 면에서도 좋다.
또한 새로운 클래스를 생성하지 않으니, 패키지 파일들을 간소화 시킬 수 있다.
class Creature {
private int age = 50;
private class Animal {
private String name = "오리";
int getOuter() { return age; }
}
public void method() {
Animal animal = new Animal();
System.out.println(animal.name);
System.out.println(animal.getOuter());
}
}
다시 이 코드를 보면, age는 private 변수다. Animal 클래스도 private가 걸려있다.
즉, 내부 클래스에 private를 적용하면서, 외부 접근을 차단하면서도 내부의 private 변수를 제어할 수 있는 1석 2조의 타이트한 캡슐화를 할 수 있다.
일단 종류는 크게 4가지다.
| 클래스 종류 | 설명 |
|---|---|
| 인스턴스 클래스 | Outer의 멤버변수 선언 위치에 선언 |
| 스태틱 클래스 | 인스턴스와 비슷하나, static 멤버처럼 사용 |
| 지역 클래스 | Outer의 메서드나 초기화블럭 안에 선언, 선언된 블럭 영역 내부에서만 사용 가능 |
| 익명 클래스 | 클래스의 선언과 객체 생성을 동시에 하는 클래스 (일회용 인듯..) |
※ Outer: 외부 클래스
-> 클래스의 멤버 변수 선언부에 위치, static 키워드가 없는 내부 클래스
-> 인스턴스 클래스 내부에는 instance 멤버만 선언 가능
-> (주의!) 인스턴스 내부 클래스에는 static 변수를 선언할 수 없다.
class PocketBall {
int size = 100;
int price = 5000;
// 인스턴스 내부 클래스
class PocketMonster {
String name = "고라파덕"; // ? 얜 오리너구리인가
int level = 10;
// final static은 상수이므로 허용
static final int cost = 100;
public int getPoketMember() { return price; }
}
}
외부 클래스를 인스턴스화 하면, 외부 클래스의 코드가 메모리에 올라온다.
이 때, 내부 클래스의 코드도 같이 메모리로 올라온다. 이때, 내부 클래스의 인스턴스를 생성할 수 있다.
-> 인스턴스와 비슷하나, static 멤버처럼 사용
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(level);
System.out.println(price);
System.out.println(name);
}
}
}
static 클래스로 선언하게 되면, 일단 static 변수를 제외한 나머지 멤버들은 전부 접근할 수 없다...
이 친구는 좀 특이하다.
static 클래스는, 키워드가 static만 들어갔을 뿐이지, 우리가 알던 static 멤버와는 전혀 다른 녀석이라고 한다.
static이라고 해서, 메모리에 한번만 로드되는 객체가 아니다.
내부 클래스의 인스턴스를 바로 생성할 수 있다는 차이만 존재한다.
// 인스턴스 클래스
Main.InnerClass inner1 = new Main().new InnerClass();
// 스태틱 클래스
Main.InnerStaticClass static1 = new InnerStaticClass();

Static이 아닌 내부 클래스는 외부와 연결된 '외부참조' 를 가지고 있어서 메모리를 더 먹게 된다고 한다.
그래서 IntelliJ에서도 static으로 권장한다.
-> Outer의 메서드나 초기화블럭 안에 선언, 선언된 블럭 영역 내부에서만 사용 가능
-> 지역 변수처럼 해당 메서드 내부에서만 한정적으로 사용
-> 접근제한자와 static을 붙일 수 없다.
class PocketBall {
public void pocketMethod() {
int exp = 5000;
// 메소드 내에서 클래스를 선언
class PocketMonster {
String name = "고라파덕";
int level = 10;
public void getPoketLevel() {
System.out.println(level); // 인스턴스 변수 출력
System.out.println(exp); // 메소드 지역 상수 출력
}
}
}
}
메소드 내의 모든 내용은 스택안에 생성됐다가 메소드 종료 시 사라진다.
당연히 다른 곳에서는 접근 및 사용 자체가 불가능하다.
메소드 내에서 생성 후에 메소드 종료 시, Heap에 있는 실제 데이터도 GC(가비지 컬렉터)가 다 치워버린다.
-> 클래스의 선언과 객체 생성을 동시에 하는 클래스 (일회용 인듯..)
public static void main(String[] args) {
Object test = new Object() {
@Override
public String toString() {
return "고라파덕은 오리인가 아닌가?";
}
};
// 익명 클래스의 객체의 오버라이딩한 메서드를 사용
String txt = test.toString();
System.out.println(txt);
}
// 출력값: 고라파덕은 오리인가 아닌가?
Object 클래스를 일회성 익명 클래스로 선언했다.
그리고 변수 test에 저장했다.
이 익명 클래스는 나중에 람다식에 나오는 개념이다.
람다 함수
프로그래밍 언어에서 사용되는 개념으로 익명 함수(Anonymous functions)를 지칭하는 용어