Language/JAVA

[JAVA] 중첩 클래스

IT수정 2024. 9. 4. 22:38

객체지향 프로그램에서는 클래스 간에 서로 긴밀한 관계를 맺고 상호작용한다. 클래스가 여러 클래스와 관계를 맺는 경우에는 독립적으로 선언하는 것이 좋으나, 특정 클래스만 관계를 맺을 경우에는 중첩 클래스로 선언하는 것이 유지보수에 도움이 되는 경우가 많다.

 

중첩클래스란 클래스 내부에 선언한 클래스를 말하는데, 중첩 클래스를 사용하면 클래스의 멤버를 쉽게 사용할 수 있고 외부에는 중첩 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다는 장점이 있다. 중첩 클래스는 선언하는 위치에 따라 두 가지로 분류된다. 클래스의 멤버로서 선언되는 중첩 클래스를 멤버 클래스라고 하고, 메서드 내부에서 선언되는 중첩 클래스를 로컬 클래스라고 한다.

선언 위치에 따른 분류 선언 위치 객체 생성 조건
멤버 클래스 인스턴스
멤버 클래스
class A {
   class B {...}
}
A 객체를 생성해야만
B 객체를 생성할 수 있음
정적
멤버 클래스
class A {
   static class B {...}
}
}
A 객체를 생성하지 않아도
B 객체를 생성할 수 있음
로컬 클래스 class A {
   void method() {
       class B {...}
}
}
method가 실행될 때만
B 객체를 생성할 수 있음

 

중첩 클래스도 하나의 클래스이기 때문에 컴파일하면 바이트코트 파일이 별도로 생성된다. 멤버 클래스일 경우 바이트코드 파일 이름은 다음과 같이 결정된다.

A $ B .class

 

로컬 클래스일 경우에는 다음과 같이 $1이 포함된 바이트코드 파일이 생성된다.

A $1 B .class

 

인스턴스 멤버 클래스

[public] class A {
	[public | private] class B {
    }
    }

 

접근 제한자에 따른 인스턴스 멤버 클래스의 접근 범위는 다음과 같다.

구분 접근 범위
public class B {} 다른 패키지에서 B 클래스를 사용할 수 있다.
class B {} 같은 패키지에서만 B 클래스를 사용할 수 있다.
private class B {} A 클래스 내부에서만 B 클래스를 사용할 수 있다.

 

인스턴스 멤버 클래스 B는 주로 A 클래스 내부에서 사용되므로 private 접근 제한자를 갖는 것이 일반적이다. B 객체는 A 클래스 내부에 어디에서나 생성할 수는 없고, 인스턴스 필드값, 생성자, 인스턴스 메서드에서 생성할 수 있다. A 객체가 있어야 B 객체를 생성할 수 있기 때문이다.

public class A {
	class B {}
    
    B field = new B();
    
    A() {
    	B b = new B();
        }
   
   void method() {
   	B b = new B();
    }
  }

 

B 객체를 A 클래스 외부에 생성하려면 default 또는 public 접근 제한을 가져야 하고, A 객체를 먼저 생성한 다음 B 객체를 생성해야 한다.

A a = new A(); // A 객체 생성
A.B b = a.new B();

 

정적 멤버 클래스

[public] class A {
	[public | private] static class B {
    }
  }

 

정적 멤버 클래스 B는 A 클래스 내부에서 사용되기도 하지만, A 클래스 외부에서 A와 함께 사용되는 경우가 많기 때문에 주로 default 또는 public 접근 제한을 가진다. B 객체는 A 클래스 내부 어디든 객체를 생성할 수 있다.

 

A 클래스 외부에서 B 객체를 생성하려면 A 객체 생성 없이 A 클래스로 접근해서 B 객체를 생성할 수 있다.

A.B b = new A.B();

 

로컬 클래스

생성자 또는 메소드 내부에서 다음과 같이 선언된 클래스를 로컬 클래스라고 한다.

[public] class A {
	public A() {
    	class B { }
        }
   
   	public void method() {
    	class B { }
        }
    }

 

로컬 클래스는 생성자와 메서드가 실행될 동안에만 객체를 생성할 수 있다. 또한 로컬 변수를 로컬 클래스에서 사용할 경우 로컬 변수는 final 특성을 갖게 되므로 값을 읽을 수만 있고 수정할 수 없게 된다. 이것은 로컬 클래스 내부에서 값을 변경하지 못하도록 제한하기 때문이다.

 

바깥 멤버 접근

중첩 클래스는 바깥 클래스와 긴밀한 관계를 맺으면서 바깥 클래스의 멤버(필드, 메소드)에 접근할 수 있다. 하지만 중첩 클래스가 어떻게 선언되었느냐에 따라 접근 제한이 있을 수 있다.

구분 바깥 클래스의 사용 가능한 멤버
인스턴스 멤버 클래스 바깥 클래스의 모든 필드와 메서드
정적 멤버 클래스 바깥 클래스의 정적 필드와 정적 메서드

 

정적 멤버 클래스는 바깥 객체가 없어도 사용 가능해야 하므로 바깥 클래스의 인스턴스 필드와 인스턴스 메서드는 사용하지 못한다.

 

바깥 클래스의 객체 접근

중첩 클래스 내부에서 this는 해당 중첩 클래스의 객체를 말한다. 만약 중첩 클래스 내부에서 바깥 클래스의 객체를 얻으려면 바깥 클래스 이름에 this를 붙여주면 된다.

바깥클래스이름.this

 

중첩 인터페이스

중첩 인터페이스는 클래스의 멤버로 선언된 인터페이스를 말한다. 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 객체를 만들기 위해서 이다. 중첩 인터페이스는 다음과 같이 선언된다.

class A {
	[public | private][static] interface B {
    	//상수 필드
        //추상 메서드
        //디폴트 메서드
        //정적 메서드
        }
    }

 

중첩 인터페이스는 암시적으로 static 이므로 생략해도 항상 A 객체 없이 B 인터페이스를 사용할 수 있다.

 

익명 객체

익명 객체는 이름이 없는 객체를 말한다. 명시적으로 클래스를 선언하지 않기 때문에 쉽게 객체를 생성할 수 있다는 장점이 있다. 익명 객체는 필드값, 로컬 변숫값, 매개변숫값으로 주로 사용된다.

 

익명객체는 클래스를 상속하거나 인터페으스를 구현해야만 생성할 수 있다. 클래스를 상속해서 만들 경우 익명 자실 객체라고 하고, 인터페이스를 구현해서 만들 경우 익명 구현 객체라고 한다.

 

익명 자식 객체

익명 자식 객체는 부모 클래스를 상속받아 다음과 같이 생성된다. 이렇게 생성된 객체는 부모 타입의 필드, 로컬 변수, 매개변수 값으로 대입할 수 있다.

new 부모생성자(매개값, ...) {
	//필드
    //메서드
}

 

중괄호 블록안에블록 안에 필드와 메서드는 익명 자식 객체가 가져야 할 멤버로, 중괄호 블록 안에서만 사용할 수 있다. 익명 자식 객체는 부모 타입에 대입되므로 부모 타입에 선언된 멤버만 접근할 수 있기 때문이다. 중괄호 블록 안에는 주로 부모 메서드를 재정의 하는 코드가 온다.

public class Tire {
	public void roll() {
    	System.out.print("일반 타이어");
        }
    }
public class Car {
	private Tire tire1 = new Tire();
    
    private Tire tire2 = new Tire() {
    	@Override
        public void roll() {
        	System.out.println("익명 자식 타이어 1");
            }
        }
   	//메서드(필드 이용)
    public void run1() {
   	tire1.roll();
    tire2.roll();
    
    //메서드(로컬 변수 이용)
    public void run2() {
    	Tire tire = new Tire() {
        	@Override
        public void roll() {
        	System.out.println("익명 자식 타이어 2");
            }
        }
        
   //메서드(매개변수 이용)
   public void run3(Tire tire) {
   		tire.roll();
        }
    }
public class CarExample {
	public static void main(String[] args) {
    	Car car = new Car();
        
        car.run1();
        
        car.run2();
        
        car.run3(new Tire() {
        	@Override
            public void roll() {
            	System.out.println("익명 자식 타이어 3");
                }
             });
          }
      }

 

실행 결과

일반 타이어
익명 자식 타이어1
익명 자식 타이어2
익명 자식 타이어3

 

익명 구현 객체

익명 구현 객체는 인터페이스를 구현해서 다음과 같이 생성된다. 이렇게 생성된 객체는 인터페이스 타입의 필드, 로컬변수, 매개변수의 값으로 대입할 수 있다. 익명 구현 객체는 안드로이드와 같은 UI 프로그램에서 이벤트를 처리하는 객체로 많이 사용된다.

new 인터페이스() {
	//필드
    //메서드
    }

 

'Language > JAVA' 카테고리의 다른 글

[JAVA] 라이브러리  (2) 2024.09.05
[JAVA] 예외 처리  (0) 2024.09.05
[JAVA] 인터페이스  (0) 2024.09.04
[JAVA] 봉인된 클래스  (0) 2024.09.04
[JAVA] 추상 클래스  (1) 2024.09.04