개발하는 자몽

내부 클래스 (Inner Class) 본문

Java

내부 클래스 (Inner Class)

jaamong 2022. 7. 25. 15:12

내부 클래스

클래스가 다른 클래스를 포함하는 경우, 내부에 있는 클래스를 내부 클래스라고 한다. 내부 클래스로 선언 시 두 클래스의 멤버들 간에 서로 쉽게 접근할 수 있으며, 외부에는 불필요한 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다(캡슐화).

내부 클래스는 정의되는 위치에 따라 멤버 클래스지역 클래스로 나뉜다. 그리고 각 내부 클래스의 선언 위치에 따라 같은 선언 위치의 변수와 동일한 유효 범위(scope)와 접근성(accessibility)을 갖는다.

 

멤버 클래스

  • 멤버 변수와 동일한 위치에 선언된 내부 클래스
  • `static` 예약어가 붙은 static 멤버와 instance 멤버로 나뉨
  • 동일한 클래스뿐만 아니라 다른 클래스에서도 활용 가능
  • 클래스의 멤버 변수와 성격이 비슷함

지역 클래스

  • 메서드 내에 클래스가 정의되어 있는 경우
  • 이름이 있는 지역 클래스와 이름이 없는 무명 클래스로 나뉨
  • 활용 범위가 메서드 블록 내로 제한되는 특징을 갖는 등 지역 변수와 성격이 비슷함

 

멤버 클래스 > instance 클래스

  • instance 멤버 클래스는 클래스의 멤버와 동일한 위치에서 선언되어 외부 클래스의 instance 멤버처럼 다루어짐
  • 멤버 변수/메서드와 동일한 위치에서 선언되었기 때문에 다른 외부 클래스에서 사용 가능
  • 주로 외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용
  • 내부 클래스의 객체 생성을 위해 외부 클래스 객체 생성 필요
class Outer { //외부 클래스
	public class Inner { //내부 클래스 : 멤버 변수와 동일 위치에 선언
    	...
    }
}

public class InnerClassTest {
	public static void main(String args[]) {
    	Outer outer = new Outer(); //내부 클래스의 객체 생성을 위해 외부 클래스 객체 생성
        Outer.Inner inner = outer.new Inner();
    }
}

 

멤버 클래스 > static 클래스

  • 외부 클래스의 멤버 변수 선언 위치에 선언됨
  • 외부 클래스의 static 멤버처럼 다루어짐
  • 주로 static 멤버, static 메서드에서 사용
  • 외부 클래스의 객체를 생성하지 않고도 내부 클래스 객체 생성 가능
class Outer { //외부 클래스
	public static class StaticInner { //static 내부 클래스
    	...
    }
}

public class StaticInnerClassTest {
	public static void main(String args[]) {
    	Outer.StaticInner sInner = new Outer.StaticInner();
    }
}

 

지역 클래스 > 이름이 있는 지역 내부 클래스(지역 클래스, local class)

  • 외부 클래스의 메서드나 초기화 블록 내부에 정의된 클래스로서 지역 변수와 동일한 범위를 가짐 클래스가 선언된 메서드 블록 내에서만 사용 가능
  • 클래스 이름 명시
class Outer { //외부 클래스
	void doSomething() {
    	class hasNameInner() { //지역 내부 클래스 : 이름 명시
        	...
        }
    }
}

public class hasNameInnerClassTest {
	public void static main(String args[]) {
    	Outer outer = new Outer();
        Outer.hasNameInner inner = outer.new hasNameInner();
    }
}

 

지역 클래스 > 무명 지역 내부 클래스 (익명클래스/익명객체, anonymous class)

  • 클래스의 선언과 객체 생성을 동시에 하는 클래스(일회용)
  • 부모 클래스의 이름 또는 구현하고자 하는 인터페이스의 이름을 사용해서 정의 하나의  클래스로 상속받는 동시에 인터페이스를 구현하거나 둘 이상의 인터페이스 구현 불가능. 단 하나의 클래스를 상속받거나 단 하나의 인터페이스만 구현 가능
  • 이름 없음   생성자 없음.
  • 외부 클래스명$익명객체정의된순번.class 형식으로 클래스 파일명이 결정됨
package Anonymous;

public class Car { //부모 클래스
	void drive() {
    	System.out.println("자동차는 달린다.");
    }
}

public class Anonymous {
 	
    //방법 1 : 필드에 익명자식 객체 생성 
	Car car1 = new Car() { //익명클래스
    	String str = "테슬라";
    	
        void type() {
        	System.out.println("전기차다");
        }
        
        @Override
        void drive() {
        	System.out.println(str + "전기로 달린다.");
        }
    };
    
    //방법 2 : 로컬 변수의 초기값으로 대입
    void test1() { //메서드 내부에서 지역적으로 익명 클래스 생성
    	Car car2 = new Car() {
        	String name = "K7";
            
            void type() {
            	System.out.println("휘발유차다.");
            }
            
            @Override
            void drive() {
            	System.out.println(str + "휘발유로 달린다.");
            }
        };
        car2.drive(); //로컬변수이므로 메서드에서 익명 클래스 바로 사용가능
    }
    
    //방법 3 : 익명클래스 매개변수로 대입
    void test2(Car car) { //부모 클래스를 매개변수로
    	car.drive(); 
    }
}

public class AnonymouseTest {
	public static void main(String args[]) {
    	Anonymous as = new Anonymous();
        
        //방법 1 : 익명클래스 필드 사용
        as.car1.drive();
        
        //방법 2 : 익명클래스 로컬 변수 사용
        as.test1();
        
        //방법 3 : 매개변수로 익명클래스 사용
        as.test2(new Car() {
        	String str = "NEXO";
            
            void type() {
            	System.out.println("수소차다.");
            }
            
            @Override
            void drive() {
            	System.out.println(str + "수소로 달린다.");
            }
        });
        
        //익명클래스 내부에서 새롭게 정의된 필드, 메서드는 부모클래스로 생성된 car1에서 접근 불가
        as.car1.name = "경유차"; //접근불가 : 익명클래스에서 새롭게 정의된 필드
        as.car1.type(); //접근불가 : 익명클래스에서 새롭게 정의된 메서드
        as.car1.drive(); //접근가능 : 부모클래스 Car에서 재정의(오버라이딩)한 메서드
    }
}

 

 

내부 클래스의 제어자와 접근성

내부 클래스도 클래스이기 때문에 `abstract`, `final`과 같은 제어자를 사용할 수 있다. 또한 멤버 변수들처럼 `private`, `protected`와 같은 접근제어자도 사용 가능하다.

class Outer {
	class InstanceInner { //instance class
    	int a = 100;
        static int staticA = 100; //에러 : instance class내에서 static 변수 선언 불가능
        final static int const = 100; //final static은 상수이므로 가능
    }
    
    static class StaticInner { //static class
    	int b = 200;
        static int staticB = 200; //static 클래스내에서 static 멤버 정의 가능
    }
    
    void method() {
    	class LocalInner { //local class
        	int c = 300;
            static int staticC = 300; //에러 : local class내에서 static 변수 선언 불가능
            final static int const = 300; //final static은 상수이므로 가능
        }
    }
}

|Note : final이 붙은 변수는 상수(constant)이므로 어떤 경우라도 static을 붙이는 것이 가능하다.

내부 클래스 중 static 멤버는 static 클래스만 선언이 가능하다. `final static`은 상수이므로 모든 내부 클래스에서 정의가 가능하다.

 

class Outer {
	class IntanceInner {} //instance class
    static class StaticInner {} //static class
    
    InstanceInner in = new InstanceInner(); //instatnce멤버 간에는 서로 직접 접근 가능
    static StaticInner sn = StaticInner(); //static멤버 간에는 서로 직접 접근 가능
    
    static void staticMethod() {
        InstanceInner a = new InstanceInner(); //에러 : static멤버는 instance멤버에 직접 접근 불가능
        StaticInner b = new StaticInner();
        
        //instance 멤버에 접근하려면 아래와 같이 생성해야함
        Outer outer = new Outer(); //instance 클래스는 외부 클래스를 통해 생성해야함
        InstanceInner a = outer.new InstanceInner();
    }
    
    void instanceMethod() {
    	//instance 메서드에서는 instance멤버와 static멤버 모두 접근 가능
    	InstanceInner a = new InstanceInner();
        StaticInner b = new StaticInner();
        
        //지역클래스는 외부에서 접근 불가능
        LocalInner c = new LocalInner();
    }
    
    void Method() {
    	class LocalInner {} //local class
        LocalInner li = new LocalInner();
    }
}

 

instance 클래스는 외부 클래스의 instance 멤버를 객체 생성 없이 사용 가능하지만 static 클래스는 외부 클래스의 instance 멤버를 객체 생성 없이 사용할 수 없다. 

instance 클래스는 static 클래스의 멤버들을 객체 생성 없이 사용 가능하지만 static 클래스에서는 instance 클래스의 멤버들을 객체생성 없이 사용할 수 없다.

 

아래는 내부 클래스에서 외부 클래스의 변수에 대한 접근성을 보여주는 코드다.

class Outer {
	private int outerP = 100;
    static int outerS = 100;
    
    class IntanceInner {
    	int ii1 = outerP; //외부클래스의 private 멤버 접근 가능
        int ii2 = outerB;
    }
    
    static class StaticInner {
    	int si1 = outerP; //에러 : static클래스는 외부 클래스의 인스턴스 멤버에 접근 불가능
        static si2 = outerS;
    }
    
    void method() {
    	int n = 100;
        final int m = 100; //jdk1.8부터 final 생략가능
        
        class LocalInner {
        	int li1 = outerP;
            int li2 = outerS;
            
            //외부 클래스의 지역변수는 final이 붙은 변수(상수)만 접근 가능
            int li3 = n; //jdk1.8부터 에러 아님
            int li4 = m;
        }
    }
}

지역클래스는 해당 클래스가 포함된 메서드에 정의된 `final`이 붙은 지역 변수만 사용할 수 있다. 메서드가 끝나고 지역변수가 소멸된 시점에서도 지역 클래스의  instance가 소멸된 지역변수를 참조하려는 경우가 발생할 수 있기 때문이다.

JDK1.8부터 지역클래스에서 접근하는 지역변수 앞에 `final`을 생략할 수 있게 되었다. 컴파일러가 자동으로 붙여준다. 따라서 해당 변수의 값이 바뀌는 코드가 있다면 컴파일 에러가 발생한다.

 

 

 

 

참고

 

[JAVA] 내부 클래스(Inner Class)

[ 요약 ] - 내부 클래스는 정의되는 위치에 따라 멤버 클래스와 지역 클래스로 나뉜다. - 멤버 클래스는 멤버 변수와 동일한 위치에 선언된 내부 클래스를 의미한다. - 멤버 클래스는 static 예약어

data-make.tistory.com

 

Java의 정석 ) 내부 클래스(inner class) 와 익명 클래스(anonymous class)

1. 내부 클래스(inner class) - 내부 클래스는 주로 AWT나 Swing과 같은 GUI 애플리케이션의 이벤트 처리 외에는 잘 사용하지 않을 정도로 사용빈도가 높지 않다. 기본 원리와 특징을 이해하는 정도까지

h2de6n.tistory.com

 

[Java] 익명객체(익명클래스)란? (이 글 하나로 한방에 정리!)

익명객체(익명클래스) 란? 이번시간에는 자바 익명객체(익명클래스)에 대해서 알아보도록 하겠습니다. 익명객체(익명클래스) 말그대로.. 이름이 없는 객체? 클래스?,,,그래서 무명클래스라고도

limkydev.tistory.com

 

'Java' 카테고리의 다른 글

isEqualTo, isSameTo, isInstanceOf  (0) 2022.08.11
람다식, 스트림(Lambda, Stream)  (0) 2022.07.26
Class 클래스, forName(), Reflection 프로그래밍  (0) 2022.07.20
Wrapper 클래스  (0) 2022.07.19
[객체 지향] is-a, has-a  (0) 2022.07.16
Comments