내부 클래스(Inner Class)
내부 클래스란 클래스 내부에 선언된 또 다른 클래스를 말한다. 보통 클래스 선언시 메인 클래스 외부에 선언하거나 따로 독립적인 클래스 파일을 생성해 선언한다.
class Main {
}
class B {
}
내부 클래스는 클래스 내에 선언 된다는 점을 제외하고는 일반적인 클래스와 크게 다르지 않다. 클래스 내부에 클래스를 선언하는 이유는 두 클래스가 서로 긴밀한 관계가 있거나 하나의 클래스 또는 메서드 에서만 사용되는 클래스이기 때문이다.
내부 클래스로 선언 하면 두 클래스의 멤버들 간에 서로 쉽게 접근할 수 있다는 장점과 외부에서는 불필요한 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다는 장점을 얻을 수 있다.
class Main { // 외부 클래스
class B { // 내부 클래스
}
}
내부 클래스의 장점
1. 객체를 생성하지 않아도 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
2. 서로 관련 있는 클래스를 논리적으로 묶어서 표한함으로써, 코드의 캡슐화를 증가시킵니다.
3. 외부에서는 내부 클래스에 접근할 수 없으므로, 코드의 복잡성을 줄일 수 있다.
내부 클래스의 종류와 특징
내부 클래스의 종류와 유효범위는 변수가 선언된 위치에 따라 인스턴스변수, Static변수, 지역변수로 나뉘듯 선언된 위치, static 키워드의 유무에 따라 4 가지로 구분된다.
내부 클래스 | 특징 |
인스턴스 클래스 (instance class) | 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 인스턴스 멤버처럼 다루어진다. 주로 외부 클래스의 인스턴스멤버들과 관련된 작업에 사용될 목적으로 선언된다. |
스태틱 클래스 (static class) | 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 static 멤버처럼 다루어진다. 주로 외부 클래스의 static 멤버, 특히 static메서드에서 사용된 목적으로 선언된다. |
지역 클래스 (local class) | 외부 클래스의 메서드나 초기화블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있다. |
익명 클래스 (anonymous class) | 클래스의 선언과 객체의 생성을 동시에 하는 이름 없는 클래스(일회용) |
일반 클래스는 접근 제어자로 (default)와 public만 사용이 가능하지만 내부 클래스는 abstract나 final 과 같은 제어자를 사용할 수 있을 뿐만 아니라 멤버 변수에 사용 가능한 접근 제어자(private, protected)도 사용이 가능하다.
class Outer {
int iv = 0;
static int cv = 0;
class InstanceInner { // 인스턴스 내부 클래스
int iv = 100;
static int cv = 100; // static 변수를 선언할 수 없다.
final static int CONST = 100; // final static은 상수이므로 허용된다
}
static class StaticInner { // 스태틱 내부 클래스
int iv = 200;
static int cv = 200; // static 클래스만 static멤버를 정의할 수 있다.
}
void methodA() { // 지역 내부 클래스
class LocalInner {
int iv = 300;
static int cv = 300; // static 변수를 선언할 수 없다.
final static int CONST = 100; // 상수는 허용된다
}
}
}
인스턴스 클래스(Instance Class)
외부 클래스의 멤버변수 선언부에 선언 되어 주로 외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용될 목적으로 선언된다.
- 외부 클래스의 멤버로 취급 되어 외부 클래스의 객체를 먼저 생성한 후 내부 클래스의 객체를 생성이 가능하다.
- 인스턴스 내부 클래스는 static 변수를 정의할 수 없지만 인스턴스 멤버, final static(상수)만 선언할 수 있다.
class Outer {
int iv = 0;
static int cv = 0;
// 인스턴스 내부 클래스
class InstanceInner {
int iv = 100;
int cv = 200; // 인스턴스 변수
// static int cv = 100; // static 변수를 선언할 수 없다.
final static int CONST = 100; // 상수는 허용된다
public void methodA() {
// 이름이 같은 외부 클래스 인스턴스 멤버 접근
System.out.println(Outer.this.iv);
// 이름이 같은 외부 클래스 스태틱 멤버 접근
System.out.println(Outer.this.cv);
// 내부 클래스 멤버 접근
System.out.println(iv);
System.out.println(cv);
}
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.InstanceInner II = outer.new InstanceInner();
// 한줄로 표현이 가능하다.
// Outer.InstanceInner oII = new Outer().new InstanceInner();
II.methodA();
}
}
위 Main 클래스의 예시는 내부 클래스를 다른 클래스에서 직접 사용하는 것으로, 일반적으로 외부 클래스에서만 사용하는 것이 일반적이므로 보기 드문 예시이다.
스태틱 클래스(Static Class)
외부 클래스의 멤버변수 선언부에 선언 되어 주로 외부 클래스의 static 멤버, 특히 static메서드에서 사용된 목적으로 사용되는 Static 키워드가 붙은 내부 클래스를 말한다.
- 스태틱 내부 클래스는 외부 클래스의 인스턴스 멤버에는 직접 접근이 불가능하고 static 멤버에만 접근할 수 있다.
- 스태틱 클래스 내부에는 인스턴스 멤버와 static 멤버 모두 선언할 수 있다.
- 스태틱 클래스만 static멤버를 가질 수 있다.
class Outer {
int iv = 0;
static int cv = 0;
// 스태틱 내부 클래스
static class StaticInner {
int sIiv = 200;
static int sIcv = 200; // static 클래스만 static멤버를 정의할 수 있다.
public static void methodA(){
// 외부 클래스 인스턴스 맴버 접근 불가능
// System.out.println(iv);
// 외부 클래스 스태틱 멤버 접근 가능
System.out.println(cv);
// 내부 클래스 인스턴스 맴버 접근 불가능
// System.out.println(sIIv);
// 내부 클래스 스태틱 맴버 접근 가능
System.out.println(sIcv);
}
}
}
public class Main {
public static void main(String[] args) {
// 스태틱 내부 클래스는 외부 클래스의 인스턴스를 생성하지 않아도 된다.
Outer.StaticInner SI = new Outer.StaticInner();
Outer.StaticInner.methodA();
}
}
static 키워드를 사용했지만 static 변수와 다르게 메모리에 한번만 생성되지 않고 일반 클래스처럼 초기화시 다른 객체가 만들어진다. 다만 외부 인스턴스를 선언 해야하는 작업 없이 내부 클래스의 인스턴스를 바로 생성할 수 있다는 차이가 있다.
내부 클래스에서 에서 외부 클래스의 멤버를 사용하지 않는다면 static 클래스로 선언하는 것이 바람직하다. static으로 선언하지 않는 내부 클래스는 외부 클래스를 선언 한뒤 내부 클래스를 선언해야 하는 과정에서 외부 클래스의 인스턴스에 대한 참조를 갖게 되어 여러 문제를 야기하기 때문이다.
지역 클래스(Local class)
외부 클래스의 메서드나 초기화블럭 안에 선언되며 선언된 영역 내부에서만 한정적으로 사용한다.
- 메소드 내부에서만 사용되어 접근을 제어할 필요가 없기 때문에 접근 제어자를 사용할 수 없다.
- 메소드 내부에는 static 멤버를 선언할 수 없기 때문에 static을 붙일 수 없다.
class Outer {
int iv = 0;
static int cv = 0;
void methodA() {
// 지역 변수
int lv = 0;
final int Lv = 0;
// 지역 내부 클래스
class LocalInner {
int localIv = Lv;
// static int localCv = 300; // static 변수를 선언할 수 없다.
final static int CONST = 100; // 상수는 허용된다
public void methodA(){
// 외부 클래스의 인스턴스멤버와 static멤버 모두 접근 가능
System.out.println(cv);
System.out.println(iv);
// 지역 클래스가 포함된 메서드애 정의된 지역변수 접근 가능
// 단, final이 붙은 지역변수만 접근이 가능, JDK 1.8부터 생략 가능
System.out.println(lv);
// 지역 클래스의 인스턴스 변수 접근 가능
System.out.println(localIv);
}
}
}
}
로컬 클래스의 지역 변수 접근
void methodA() {
// 지역 변수
int lv = 0;
final int Lv = 0;
// 지역 내부 클래스
class LocalInner {
public void methodA(){
// 지역 클래스가 포함된 메서드애 정의된 지역변수 접근 가능
// 단, final이 붙은 지역변수만 접근이 가능, JDK 1.8부터 생략 가능
System.out.println(lv);
}
}
}
지역 클래스가 포함된 메서드에 정의된 지역변수도 사용할 수 있다. 단, final이 붙은 지역변수만 접근이 가능하다. constant pool에서 따로 관리되는 상수와 달리 메서드 내부의 지역 변수는 메서드 종료와 함께 소멸한다. 지역변수가 소멸된 시점에도 지역 클래스의 인스턴스가 소멸된 지역변수를 참조하려는 경우가 발생할 수 있기 떄문이다. JDK1.8 부터 값이 바뀌지 않는 변수는 상수로 간주하여 지역 클래스에서 접근하는 지역 변수 앞에 final을 생략할 수 있게 바뀌었다, 다만 생략을 할 수 있게 한 것일 뿐 해당 변수의 값이 바뀌는 문장이 있으면 컴파일 에러가 발생한다.
익명 클래스
익명 클래스는 기존의 클래스/인터페이스를 메서드 내에서 일회용 선언하여 필요한 메서드를 재정의 하여 사용하는 기법이다.
- 익명 클래스는 이름이 존재하지 않는 내부 클래스이다.
- 클래스의 선언과 동시에 단 하나의 객체만을 생성하는 일회용 클래스이다.
- 생성자가 존재 하지 않다. 조상클래스/구현인터페이스의 이름을 사용하여 정의한다.
- 단 하나의 클래스나 인터페이스만을 구현할 수 있다.
new 조상클래스이름() {
// 멤버 선언
}
new 구현인터페이스이름() {
// 멤버 선언
}
public class Main {
public static void main(String[] args) {
Integer [] arr = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
// 정수의 역순 정렬
// Comparator인터페이스의 구현
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer e1, Integer e2) {
return e2 - e1;
}
});
}
}
내부 클래스의 컴파일
일반적인 클래스는 컴파일시 클래스 파일이 생성된다. 내부 클래스는 일반적인 클래스와 생성되는 컴파일 파일명이 다르다.
외부클래스$내부클래스.class
일반적인 클래스
Outer.class
내부 클래스(인스턴스 클래스, 스태틱 클래스)
Outer$StaticInner.class
Outer$InstanceInner.class
지역 내부 클래스는 다른 메서드 내에 같은 이름의 지역변수를 사용하는 것과 같이 같은 이름의 내부 클래스가 존재할 수 있기 때문에 내부 클래스 명 앞에 숫자가 붙는다.
class Outer {
void MethodA(){
class LocalInner {}
}
void MethodB(){
class LocalInner {}
}
}
외부클래스$숫자 + 내부클래스.class
지역 내부 클래스
Outer$1LocalInner.class
Outer$2LocalInner.class
익명 클래스는 클래스명이 업기 때문에 숫자로만 구분한다.
외부클래스$숫자.class
익명 내부 클래스
Outer$1.class
Outer$1.class
'Java' 카테고리의 다른 글
[JAVA] Object 클래스 (0) | 2023.10.28 |
---|---|
[JAVA] 자바의 에러(error)와 예외(exception) (0) | 2023.10.25 |
[JAVA] 인터페이스(Interface) (1) | 2023.09.19 |
[JAVA] 추상 클래스(Abstract class) (0) | 2023.09.02 |
[JAVA]객체 지향 프로그래밍(Object Oriented Programming) (0) | 2023.08.30 |