Java 내부 클래스는 다른 클래스의 본문 내에 정의됩니다. Java 내부 클래스는 private, public, protected 또는 기본 액세스로 선언될 수 있지만 외부 클래스는 오직 public 또는 기본 액세스만 가질 수 있습니다. Java 중첩 클래스는 두 가지 유형으로 나뉩니다.
-
정적 중첩 클래스
중첩 클래스가 정적인 경우 정적 중첩 클래스라고합니다. 정적 중첩 클래스는 외부 클래스의 정적 멤버에만 액세스 할 수 있습니다. 정적 중첩 클래스는 다른 최상위 클래스와 동일하며, 단지 패키징 편의를 위해 중첩됩니다. 정적 클래스 객체는 다음 문장으로 생성할 수 있습니다.
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
-
자바 내부 클래스
모든 비정적 중첩 클래스를 자바에서 내부 클래스라고합니다. 자바 내부 클래스는 클래스의 객체와 관련이 있으며 외부 클래스의 모든 변수 및 메서드에 액세스 할 수 있습니다. 내부 클래스는 인스턴스와 관련이 있기 때문에 이러한 클래스에는 정적 변수가 없을 수 있습니다. 자바 내부 클래스의 객체는 외부 클래스 객체의 일부이며 내부 클래스의 인스턴스를 만들려면 먼저 외부 클래스의 인스턴스를 만들어야합니다. 자바 내부 클래스는 다음과 같이 인스턴스화 할 수 있습니다;
OuterClass outerObject = new OuterClass(); OuterClass.InnerClass innerObject = outerObject.new InnerClass();
두 가지 특별한 종류의 자바 내부 클래스가 있습니다.
-
지역 내부 클래스
클래스가 메소드 본문에 정의되면 이를 지역 내부 클래스라고 합니다. 지역 내부 클래스는 객체와 관련이 없기 때문에 해당 클래스에는 private, public 또는 protected 접근 제어자를 사용할 수 없습니다. 허용된 유일한 수정자는 abstract 또는 final입니다. 지역 내부 클래스는 정의된 범위에서 포함 클래스의 모든 멤버 및 로컬 final 변수에 액세스할 수 있습니다. 또한 정의된 메소드의 비-final 로컬 변수에도 액세스할 수 있지만 이를 수정할 수는 없습니다. 따라서 메소드 내부 클래스에서 비-final 로컬 변수의 값을 출력하려고 하면 허용되지만 해당 값을 변경하려고 하면 컴파일 시간 오류가 발생합니다. 지역 내부 클래스는 다음과 같이 정의할 수 있습니다:
package com.journaldev.innerclasses; public class MainClass { private String s_main_class; public void print() { String s_print_method = ""; // 메소드 내부의 지역 내부 클래스 class Logger { // 포함 클래스 변수에 액세스 가능 String name = s_main_class; // 비-final 메소드 변수에 액세스 가능 String name1 = s_print_method; public void foo() { String name1 = s_print_method; // 아래 코드는 컴파일 시간 오류를 발생시킵니다: // Local variable s_print_method defined in an enclosing scope must be final or effectively final // s_print_method= ":"; } } // 메소드 내부에서 지역 내부 클래스를 사용하기 위해 클래스를 인스턴스화 Logger logger = new Logger(); } }
우리는 블록 내에서도 지역 내부 클래스를 정의할 수 있습니다. 예를 들어, static 블록, if-else 블록 등이 있습니다. 그러나 이 경우 클래스의 범위는 매우 제한됩니다.
public class MainClass { static { class Foo { } Foo f = new Foo(); } public void bar() { if(1 < 2) { class Test { } Test t1 = new Test(); } // 아래는 클래스의 범위 때문에 오류가 발생합니다 //Test t = new Test(); //Foo f = new Foo(); } }
-
익명 내부 클래스
이름이없는 로컬 내부 클래스는 익명 내부 클래스로 알려져 있습니다. 익명 클래스는 정의 및 인스턴스화가 단일 문에서 이루어집니다. 익명 내부 클래스는 항상 클래스를 확장하거나 인터페이스를 구현합니다. 익명 클래스에는 이름이 없으므로 익명 클래스에 대한 생성자를 정의할 수 없습니다. 익명 내부 클래스는 정의된 지점에서만 액세스할 수 있습니다. 익명 내부 클래스를 어떻게 생성하는지 정의하는 것은 약간 어렵습니다. 아래의 테스트 프로그램에서 실시간 사용법을 살펴보겠습니다.
이것은 자바 내부 클래스, 정적 중첩 클래스, 로컬 내부 클래스 및 익명 내부 클래스를 정의하는 방법을 보여주는 자바 클래스입니다. OuterClass.java
package com.journaldev.nested;
import java.io.File;
import java.io.FilenameFilter;
public class OuterClass {
private static String name = "OuterClass";
private int i;
protected int j;
int k;
public int l;
//OuterClass 생성자
public OuterClass(int i, int j, int k, int l) {
this.i = i;
this.j = j;
this.k = k;
this.l = l;
}
public int getI() {
return this.i;
}
//static 중첩 클래스, OuterClass의 정적 변수/메서드에 액세스할 수 있음
static class StaticNestedClass {
private int a;
protected int b;
int c;
public int d;
public int getA() {
return this.a;
}
public String getName() {
return name;
}
}
//내부 클래스, 비정적이며 외부 클래스의 모든 변수/메서드에 액세스할 수 있음
class InnerClass {
private int w;
protected int x;
int y;
public int z;
public int getW() {
return this.w;
}
public void setValues() {
this.w = i;
this.x = j;
this.y = k;
this.z = l;
}
@Override
public String toString() {
return "w=" + w + ":x=" + x + ":y=" + y + ":z=" + z;
}
public String getName() {
return name;
}
}
//로컬 내부 클래스
public void print(String initial) {
//로컬 내부 클래스 inside the method
class Logger {
String name;
public Logger(String name) {
this.name = name;
}
public void log(String str) {
System.out.println(this.name + ": " + str);
}
}
Logger logger = new Logger(initial);
logger.log(name);
logger.log("" + this.i);
logger.log("" + this.j);
logger.log("" + this.k);
logger.log("" + this.l);
}
//익명 내부 클래스
public String[] getFilesInDir(String dir, final String ext) {
File file = new File(dir);
//익명 내부 클래스 implementing FilenameFilter interface
String[] filesList = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(ext);
}
});
return filesList;
}
}
여기에는 자바에서 내부 클래스를 인스턴스화하고 사용하는 방법을 보여주는 테스트 프로그램이 있습니다. InnerClassTest.java
package com.journaldev.nested;
import java.util.Arrays;
//중첩 클래스는 쉬운 인스턴스화를 위해 import에서 사용할 수 있음
import com.journaldev.nested.OuterClass.InnerClass;
import com.journaldev.nested.OuterClass.StaticNestedClass;
public class InnerClassTest {
public static void main(String[] args) {
OuterClass outer = new OuterClass(1,2,3,4);
//static 중첩 클래스 예제
StaticNestedClass staticNestedClass = new StaticNestedClass();
StaticNestedClass staticNestedClass1 = new StaticNestedClass();
System.out.println(staticNestedClass.getName());
staticNestedClass.d=10;
System.out.println(staticNestedClass.d);
System.out.println(staticNestedClass1.d);
//내부 클래스 예제
InnerClass innerClass = outer.new InnerClass();
System.out.println(innerClass.getName());
System.out.println(innerClass);
innerClass.setValues();
System.out.println(innerClass);
//로컬 내부 클래스를 사용하여 메서드 호출
outer.print("Outer");
//익명 내부 클래스를 사용하여 메서드 호출
System.out.println(Arrays.toString(outer.getFilesInDir("src/com/journaldev/nested", ".java")));
System.out.println(Arrays.toString(outer.getFilesInDir("bin/com/journaldev/nested", ".class")));
}
}
위의 자바 내부 클래스 예제 프로그램의 출력은 다음과 같습니다.
OuterClass
10
0
OuterClass
w=0:x=0:y=0:z=0
w=1:x=2:y=3:z=4
Outer: OuterClass
Outer: 1
Outer: 2
Outer: 3
Outer: 4
[NestedClassTest.java, OuterClass.java]
[NestedClassTest.class, OuterClass$1.class, OuterClass$1Logger.class, OuterClass$InnerClass.class, OuterClass$StaticNestedClass.class, OuterClass.class]
OuterClass를 컴파일할 때 내부 클래스, 로컬 내부 클래스 및 static 중첩 클래스에 대해 별도의 클래스 파일이 생성됨에 유의하십시오.
Java 내부 클래스의 이점
- 클래스가 하나의 클래스에만 유용한 경우 중첩 및 함께 유지하는 것이 합리적입니다. 클래스의 패키징에 도움이 됩니다.
- Java 내부 클래스는 캡슐화를 구현합니다. 내부 클래스는 외부 클래스의 private 멤버에 접근할 수 있으며 동시에 내부 클래스를 외부 세계로부터 숨길 수 있습니다.
- 작은 클래스를 최상위 클래스 내에 유지함으로써 코드를 사용하는 곳에 가깝게 유지하고 코드를 더 읽기 쉽고 유지보수하기 쉽게 만듭니다.
자바 내부 클래스에 대한 설명은 여기까지입니다.
Source:
https://www.digitalocean.com/community/tutorials/java-inner-class