클래스 내부에 정의된 클래스
외부 클래스의 멤버처럼 사용되며, 외부 클래스의 필드/메서드에 자유롭게 접근할 수 있습니다.
class Outer {
private int number = 10;
class Inner {
void print() {
System.out.println(number); // 외부 클래스 private에도 접근 가능
}
}
}
Inner Class를 사용하는 이유
외부 클래스와 강하게 연결된 기능을 묶기 위함
특정 객체와 강하게 연관된 구조를 만들기 위함
ex ) Map.Entry, Iterator, Button.EventHandler 등등
외부 클래스의 private 멤버 접근이 필요한 경우
Inner class는 외부 클래스의 private 요소에도 접근 가능하므로 캡슐화를 유지한 채 내부 구조를 다룰 수 있습니다
캡슐화
외부에서 사용할 필요가 없는 클래스라면 내부에 숨겨 노출하지 않기 위함
Inner Class의 장점
어느 클래스가 여러 클래스와 관계를 맺지 않고, 하나의 특정 클래스와만 관계를 맺는다면, 외부에 클래스를 새로 작성하는 것이 아니라 내부 클래스로 작성할 수 있습니다.
이를 통해 가독성과 유지보수성 또한 증가합니다.
Inner Class의 종류
class Outer{
class InstanceInner { ... } // 인스턴스 클래스
static class StaticInner { ... } // 스태틱 클래스
void method1(){
class LocalInner { ... } // 지역 클래스
}
}
[ 인스턴스 클래스 ]
외부 클래스의 멤버 변수 선언 위치에 선언하며, 외부 클래스의 인스턴스 멤버처럼 다뤄지는 Inner Class
주로 외부 클래스의 인스턴스 멤버에 강하게 결합된 기능을 구현할 때 사용합니다.
외부 클래스의 멤버로 취급되기 때문에 외부 클래스의 객체를 먼저 생성 후 내부 클래스의 객체 생성이 가능합니다.
인스턴스 클래스 내부에서는 static 멤버를 선언할 수 없습니다.
⇒ static 멤버는 클래스 로딩 시점에 메모리에 올라가야 하지만, 인스턴스 이너 클래스는 외부 클래스 인스턴스가 먼저 생성되어야만 함께 생성될 수 있기 때문
( static final 상수는 허용됨 → 컴파일 시 상수로 인라인 되기 때문 )
내부 클래스에서 외부 클래스와 동일한 이름의 메서드를 정의한 경우, 외부 클래스의 메서드를 호출하려면 외부클래스명.this.메서드명() 형태로 접근해야 합니다.
public class Parent {
private String parentName;
private void method() {
System.out.println("parent method");
}
class Baby {
private String babyName;
private void method() {
Parent.this.method();
System.out.println("baby method");
}
}
}
Parent p = new Parent();
Parent.Baby b = p.new Baby();
[ 스태틱 클래스 ]
외부 클래스의 멤버 변수 선언 위치에 선언하며, 외부 클래스의 static 멤버처럼 다뤄집니다.
statc 클래스 내부에서는 인스턴스 멤버와 static 멤버 모두 선언할 수 있습니다.
일반적인 static 메서드와 동일하게 외부 클래스의 인스턴스 멤버에는 접근이 불가하고 static 멤버에만 접근할 수 있습니다.
public class Parent {
private String parentName;
static int a = 1000;
static class Baby {
static String babyName = "park";
int age = 20;
public static void method() {
System.out.println(a);
System.out.println(babyName);
// System.out.println(age)는 안됨
}
}
}
Parent.Baby b = new Parent.Baby();
System.out.println(Parent.Baby.babyName);
System.out.println(b.age);
static 클래스에서 static 키워드가 존재하기 때문에 기존의 static 필드 변수나 메서드처럼 메모리에 하나만 올라가는 인스턴스로 생각할 수 있지만, 전혀 다르며, 단지 인스턴스 내부 클래스와 달리 외부 클래스의 인스턴스를 먼저 생성할 필요 없이 내부 클래스의 객체를 바로 만들 수 있다는 점이 가장 큰 차이일 뿐입니다.
⚠️ 내부 클래스를 사용하는데, 내부 클래스에서 외부 클래스의 인스턴스를 사용하지 않는다면 static 클래스로 선언해주어야 함
⇒ static이 아닌 내부 인스턴스 클래스는 외부와 연결되어 있기 때문에, 외부 참조를 갖게 되어 메모리를 차지하며 GC 대상에서 제외되어 메모리 관리가 안될 수 있기 때문
⇒ static 내부 클래스는 외부 참조를 하지 않기 때문에 메모리 누수가 발생하지 않습니다.
[ 익명 클래스 ]
클래스 선언과 객체의 생성을 동시에 하는 이름 없는 클래스로, 주로 일회용으로 사용할 때 사용합니다.
단 하나의 객체만을 생성하며, 클래스 선언과 동시에 생성합니다.
Class Animal {
public String bark() {
return "동물이 짖다";
}
}
Class dog extends Animal {
@Override
public String bark() {
return "개가 짖다";
}
}
Animal d = new Dog();
sout(d.bark()) // "개가 짖다"
--- 익명 클래스 사용
Class Animal {
public String bark() {
return "동물이 짖다";
}
}
Animal dog = new Animal() {
@Override
public String bark() {
return "개가 짖다";
}
}
dog.bark(); // "개가 짖다"
익명 클래스는 재사용할 필요 없이 일회성으로 사용하여 따로 클래스를 생성하지 않아도 된다는 장접이 있습니다.
주의 ) 익명 클래스 내부에서 새로 정의한 메서드는 외부에서 사용이 불가능
부모 클래스에 해당 메서드가 선언되어 있지 않기 때문
Animal dog = new Animal() {
@Override
public String bark() {
return "개가 짖다";
}
public String ex() {
return "ex";
}
}
dog.ex() // 안됨
[ 지역 클래스 ]
외부 클래스의 메서드나 초기화 블럭 안에 선언하며, 선언된 메서드 블록 영역 내부에서만 사용할 수 있습니다.
지역 변수처럼 해당 메서드 내부에서만 한정적으로 사용되며, 접근 제한자와 static을 붙일 수 없습니다.
( 메서드 내부에서만 사용되므로 접근을 제한할 필요가 없고, 원래 메서드 내에서는 static을 붙일 수 없기 때문 )
public class Parent {
private String parentName;
int a = 1000;
public void pMethod() {
class Baby {
String babyName = "park";
int age = 20;
public void bMethod() {
System.out.println(parentName);
System.out.println(babyName);
}
}
new Baby().bMethod();
System.out.println("메서드 실행 완료");
}
}