내부 클래스
내부 클래스란 쉽게 말해 클래스 안에 선언된 또 다른 클래스이다.
내부 클래스를 이용하면 외부적으로 불필요한 데이터를 감출수 있으며 외부클래스의 멤버들에 쉽게 접근할 수 있어 코드의 복잡성을 줄일 수 있다.
내부 클래스의 종류로는 인스턴스 내부 클래스, 정적 내부 클래스, 지역 내부 클래스, 로컬 내부 클래스가 있다.
종류 | 선언 위치 | 사용 가능 변수 |
인스턴스 내부 클래스(instance inner class) | 외부 클래스의 멤버변수 선언위치에 선언(멤버 내부 클래스) | 외부 인스턴스 변수, 외부 전역 변수 |
정적 내부 클래스(static inner class) | 외부 클래스의 멤버변수 선언위치에 선언(멤버 내부 클래스) | 외부 전역 변수 |
지역 내부 클래스(local inner class) | 외부 클래스의 메서드나 초기화블럭 안에 선언 | 외부 인스턴스 변수, 외부 전역 변수 |
익명 내부 클래스(anonymous inner class) | 클래스의 선언과 객체의 생성을 동시에 하는 일회용 익명 클래스 | 외부 인스턴스 변수, 외부 전역 변수 |
인스턴스 내부 클래스
인스턴스 내부 클래스는 외부 클래스에서 생성된 객체의 내부에 멤버의 형태로 존재하며 외부 클래스의 모든 변수와 메서드에 접근할수 있다.
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
System.out.println("외부 클래스 사용하여 내부 클래스 기능 호출");
outer.testClass(); // 내부 클래스 기능 호출
}
}
class Outer { //외부 클래스
private int num = 1; //외부 클래스 인스턴스 변수
private static int sNum = 2; // 외부 클래스 정적 변수
private InClass inClass; // 내부 클래스 자료형 변수 선언
public Outer() {
inClass = new InClass(); //외부 클래스 생성자
}
class InClass { //인스턴스 내부 클래스
int inNum = 10; //내부 클래스의 인스턴스 변수
void Test() {
System.out.println("Outer num = " + num + "(외부 클래스의 인스턴스 변수)");
System.out.println("Outer sNum = " + sNum + "(외부 클래스의 정적 변수)");
}
}
public void testClass() {
inClass.Test();
}
}
// 출력값
외부 클래스 사용하여 내부 클래스 기능 호출
Outer num = 1(외부 클래스의 인스턴스 변수)
Outer sNum = 2(외부 클래스의 정적 변수)
코드의 흐름을 천천히 살펴 보자면
- 메인 클래스에서 외부 클래스 인스턴스(Outer) 생성
- 외부 클래스 생성자를 통해 내부클래스의 인스턴스(InClass)를 생성한뒤 외부클래스의 인스턴스 변수(inClass)에 할당
- 이후 메인 클래스에서 외부 클래스 메서드 testClass() 호출
- 이후 내부 클래스의 메서드 inClass.Test()호출
- 내부클래스 메서드인 inClass.Test()에서 외부 클래스 전역변수인 인스턴스변수(num)과 클래스변수(sNum)에 접근
위처럼 정석적인 방법이 아닌, 의외의 상황도 구현해 보았다.
메인 클래스에서는 인스턴스 내부 클래스의 생성은 불가능했으며 외부클래스의 메서드에 내부클래스의 인스턴스를 리턴해주어 메인클래스에서도 접근이 가능했다. (수정) 메인클래스에서도 다른 클래스의 내부클래스의 인스턴스를 생성 할수 있었다.
public class Main {
public static void main(String[] args) {
Outer outerClass = new Outer(); // 외부 클래스 생성
outerClass.makeInnerClass().innerVariable = true;
// 내부클래스의 변수 및 메서드에 접근 가능
Outer.Inner inner = outerClass.new Inner();
/*(수정내용) Main클래스에서도 내부클래스의 인스턴스 생성이 가능했다.
외부 클래스를 먼저 생성한뒤 연산자 포인터(.)를 이용해 생성된 외부클래스에 접근한뒤
내부클래스를 생성해주며 참조타입은 외부타입.내부타입으로 명시한다.
*/
}
}
class Outer{
Inner makeInnerClass(){ //메서드의 타입을 내부 클래스로 지정
Inner innerClass = new Inner(); // 메서드에서 내부클래스 생성 후 리턴
return innerClass;
}
class Inner{
boolean innerVariable;
}
}
마지막으로 this는 해당 객체를 의미한다. 예를들어 내부 인스턴트 클래스 내에서 this를 사용할경우 내부 인스턴트 클래스에서 생성된 객체를 의미하며 외부 인스턴트 클래스 또한 마찬가지다. this가 객체를 의미하므로 인스턴스화는 필연적이다. 그러므로 모든 정적 멤버들에게는 해당이 안된다.
정적(static) 내부 클래스
내부 클래스명에 static을 붙여주면 정적 내부 클래스가 되며 정적 내부 클래스는 모든 인스턴스 자원을 사용하지 못한다. 그 이유는 이전 포스트에서 설명한 바와 같이 메모리 저장 영역과 시기의 차이 때문이다.
한 가지 더 다른 점은 main 클래스에서도 외부 클래스의 객체를 생성하지 않고 바로 내부 정적 클래스의 객체를 생성 할수 있다는 점이다.
public class Main {
public static void main(String[] args) {
Outer.StaticInClass a = new Outer.StaticInClass(); //정적 이너 클래스의 객체 생성
a.test();
}
}
class Outer { // 외부 클래스
private int num = 3; // 외부 클래스의 인스턴스 변수
private static int sNum = 4;
void getPrint() {
System.out.println("인스턴스 메서드");
}
static void getPrintStatic() {
System.out.println("스태틱 메서드");
}
static class StaticInClass { // 정적 내부 클래스
void test() {
System.out.println("Outer sNum = " +sNum + "(외부 클래스의 정적 변수)");
getPrintStatic();
// num 과 getPrint() 는 정적 멤버가 아니라 사용 불가.
}
}
}
//출력값
Outer sNum = 4(외부 클래스의 정적 변수)
스태틱 메서드
마무리
복잡하다... 블로그를 작성중 갑자기 깨닳음(?)을 얻어 이해가 됬다. 그냥 쉽게 내부 외부를 가리지 않고 static이 정의된 모든 메서드와 클래스들은 어떠한 인스턴스 매서드나 클래스 혹은 변수까지도 접근이 불가하다.
class Test {
Test2 test2;
boolean a = true;
void method(){
method2();
test2.b = true;
}
void method2(){
test2 = new Test2();
test2.b = true;
}
class Test2{
boolean b = true;
void innerMethod(){
method();
}
}
}
위와 같이 모든 메서드들과 내부클래스를 얽히고 설키게 만들고 저들중 하나만이라도 static으로 만들경우 모든 것들을 static으로 만들어 주어야 됬다. 혹은, static이 들어간 멤버에서 인스턴스 멤버를 호출하지 않으면 됬다.
물론 인스턴스에서 static을 호출할경우에는 잘 작동 되었다.
마지막으로 둘의 공통점으로는 메인 클래스에서 내부 클래스의 데이터에 접근할때는 항상 연산자 포인터( .) 를 이용하여 외부클래스부터 접근해야 된다는 점이다. ex) outerClass.makeInnerClass().innerVariable = true;
'Java > Java' 카테고리의 다른 글
객체지향 프로그래밍 심화(다형성, 추상화) (0) | 2023.03.01 |
---|---|
객체지향 프로그래밍 심화(상속, 캡슐화) (0) | 2023.02.28 |
자바의 객체지향 프로그래밍 기초 (0) | 2023.02.23 |
제어문(Control Flow Statements) - (조건문, 반복문) (0) | 2023.02.20 |
콘솔 입출력 (I/O, Input/Output) (0) | 2023.02.20 |