내부 클래스(Inner Class)

조용근·2024년 1월 23일

자바 정리

목록 보기
12/21

내부 클래스


A는 B의 외부 클래스, B는 A의 내부 클래스

  • 클래스 내부에 선언된 다른 클래스
  • 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근 가능
    //객체 생성 없이도 B 클래스는 A 멤버로 접근이 가능하다.
  • 코드의 복잡성을 줄인다.(캡슐화)
class A{
    int i=100;
    B b = new B();//객체 생성

}

class B{
    void method(){
        A a = new A();//객체 생성
        System.out.println(a.i);
    }

}

class C{
    B b = new B();//객체 생성

}


public class InnerTest {
    public static void main(String[] args) {
        B b= new B();//객체 생성
        b.method();
    }
}

위와 같이 어느 클래스에서 다른 클래스로 접근하려면, 반드시 객체를 생성해야만 했다.

class A{ //A는 B의 외부 클래스
    int i=100;
    B b = new B();
    class B{ // B는 A의 외부 클래스, B는 A 안에서만 사용 가능(캡슐화)
        void method(){
            /*A a = new A();
            System.out.println(a.i);*/
            System.out.println(i); //객체 생성없이 외부 클래스 멤버 접근
            
    }

}

/*class C{
    B b = new B();

}*/
    
public class InnerTest {
    public static void main(String[] args) {
      /*  B b= new B();
        b.method();*/
    }
}

A 클래스는 B 클래스를 멤버처럼 사용 가능하다.

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

class Outer{
	private class InstanceInner{}
    protected static class StaticInner{}
    void myMethod(){
    	class localInner{}
    }
}

내부 클래스의 경우 private,protected,default,public 모두 사용 가능하다.

class Ex{
    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 myMethod(){
        class localInner{
            int iv = 300;
            static int cv = 300; //static 변수를 선언할 수 없다.
            final static int CONST = 300;   //final static은 상수이므로 선언 가능
            int i = LocalInner.CONST; // 이건 가능
            
        }
     public static void main(String[] args) {
        System.out.println(InstanceInner.CONST);
        System.out.println(StaticInner.cv);
        //System.out.println(LocalInner.CONST);
        //지역 내부 클래스의 static  상수는 메서드 내에서만 사용 가능하다.
    }

    }
    
  • static 멤버를 정의할려면 반드시 static 클래스를 선언해야 한다.
    //static은 객체 생성없이 사용이 가능한 것인데, 만약 static 클래스가 아닌 상태에서 static 멤버를 정의하면, 객체를 생성해야만 사용 가능한 것으로 된다.
  • static 내부 클래스에서는 외부 클래스의 인스턴스 멤버에 접근할 수 없다.
  • 내부 클래스도 기존 클래스와 마찬가지로 인스턴스 멤버가 static 멤버로 접근이 가능하지만, static 멤버가 인스턴스로 멤버로 접근하는 것은 불가능하다.

인스턴스 멤버 클래스

package study.first;

public class Study {

	public static void main(String[] args) {

		Outer p = new Outer(); // Outer 클래스 인스턴스 생성
		Outer.Inner c = p.new Inner(); // Outer.Inner 클래스 인스턴스 생성
		System.out.println(c.a);
		c.print();
	}
}

class Outer {

	class Inner {

		int a = 1;

		void print() {
			System.out.println(a + " 함수");
		}
	}
}
class PocketBall {
    // 인스턴스 변수
    int size = 100;
    int price = 5000;

    // 인스턴스 내부 클래스
    class PocketMonster {
        String name = "이상해씨";
        int level = 10;
        
        // static int cost = 100; - 에러! 인스턴스 내부 클래스에는 static 변수를 선언할 수 없다.
        static final int cost = 100; // final static은 상수이므로 허용

        public void getPoketMember() {
            // 별다른 조치없이 외부 클래스 맴버 접근 가능
            System.out.println(size);
            System.out.println(price);

            // 내부 클래스 멤버
            System.out.println(name);
            System.out.println(level);
            System.out.println(cost);
        }
    }
}
public class Main {
    public static void main(String[] args) {
        
        PocketBall ball = new PocketBall(); // 먼저 외부 클래스를 인스턴스화 해주고
        PocketBall.PocketMonster poketmon = ball.new PocketMonster(); // 외부클래스.내부클래스 형식으로 내부클래스를 초기화 하여 사용할 수도 있다
        poketmon.getPoketMember();
        
        // 위의 단계를 한줄로 표현
        PocketBall.PocketMonster poketmon2 = new PocketBall().new PocketMonster();
    }
}
  • static이 없는 내부 클래스
  • 외부 클래스 선언 후에 내부 클래스 선언 가능(내부 클래스는 외부 클래스의 멤버이기 때문)
  • 인스턴스 클래스 내부에는 인스턴스 멤버만 선언 가능(static 멤버는 선언 불가능)

내/외부 클래스 메서드 이름이 같을 때 외부 클래스 메서드 호출

// 외부 클래스
public class Main {

    public print(String txt) {
        System.out.println(txt);
    }

	// 내부 클래스
    class Sub {
        public print() {         
        }
    }
}
public class Main {

    public void print(String txt) {
        System.out.println(txt);
    }

    class Sub {
        public void print() {
            Main.this.print("외부 클래스 메소드 호출");
            System.out.println("내부 클래스 메소드 호출");
        }
    }
}
public static void main(String[] args) {
    Main.Sub s = new Main().new Sub();
    s.print();
    /*      
    외부 클래스 메소드 호출
    내부 클래스 메소드 호출
    */
}
  • 클래스 상속관계에서는 super() 을 사용해서 부모 메서드를 호출했다. 하지만, 내부-외부 클래스는 상속 관계가 아니므로 다른 방법을 사용한다.
  • 클래스명//외부 클래스.this.메서드명() 를 사용해서 인스턴스 메서드를 호출할 수 있다.

정적 멤버 클래스

package study.first;

public class Study {

	public static void main(String[] args) {

		Outer.Inner c = new Outer.Inner();
		System.out.println(c.a);
		c.print();

	}
}

class Outer {

	static class Inner {

		int a = 1;

		void print() {
			System.out.println(a + " 함수");
		}
	}
}
  • 일반 클래스와 달리 내부 클래스는 static 선언이 가능하다.
  • static이 선언되어 이미 클래스의 코드가 메모리에 준비된 상태이기 때문에 바로 인스턴스화 할 수 있다. //즉, 외부클래스의 인스턴스 유무와 상관없이 인스턴스를 생성할 수 있다.
class PocketBall {
    int size = 100;
    static int price = 5000;

    // static 내부 클래스
    static class PocketMonster {
        static String name = "이상해씨";
        int level = 10;

        public static void getPoketMember() {
            // 외부 클래스 인스턴스 맴버 접근 불가능
            // System.out.println(size);
            
            // 외부 클래스 스태틱 멤버 접근 가능
            System.out.println(price);
			
            // 내부 클래스 멤버도 스태틱 맴버만 접근 가능
            System.out.println(name);
            // System.out.println(level);
        }
        
    }
}
public class Main {
    public static void main(String[] args) {
        // 스태틱 내부 클래스의 인스턴스는 외부 클래스를 먼저 생성하지 않아도 된다.
        PocketBall.PocketMonster poketmon = new PocketBall.PocketMonster();
        System.out.println(poketmon.level);
        System.out.println(PocketBall.PocketMonster.name);

        // 클래스.정적내부클래스.정적메소드()
        PocketBall.PocketMonster.getPoketMember();
    }
}
  • static이 붙은 내부 클래스
  • static 클래스 내부에 instance, satic 멤버 모두 선언 가능
  • static에서 외부 클래스의 인스턴스 멤버 접근 불가능
  • 기존의 내부 인스턴스 클래스처럼 외부 인스턴스를 선언하고 내부 인스턴스를 선언하지 않고 바로 내부 인스턴스를 선언할 수 있다.

지역 클래스

package study.first;

public class Study {

	public static void main(String[] args) {

		Outer p = new Outer();
		p.print();
	}
}

class Outer {

	void print() {

		class Inner {
			int a = 1;
		}

		Inner c = new Inner();
		System.out.println(c.a);

	}
}
  • 메서드 안에서만 사용 가능한 클래스이다.
  • 메서드 종료 시 메서드 안의 내용이 모두 사라지게 되므로, 다른 곳에서 접근/사용이 불가능하다.
class PocketBall {
    int size = 100;
    int price = 5000;

    public void pocketMethod() {
        int exp = 5000;
        
        // 메소드 내에서 클래스를 선언
        class PocketMonster {
            String name = "이상해씨";
            int level = 10;

            public void getPoketLevel() {
                System.out.println(level); // 인스턴스 변수 출력
                System.out.println(exp); // 메소드 지역 상수 출력
            }
        }
		
        // 메소드 내에서 클래스를 선언
        class PocketMonster2 {
            String name = "리자몽";
            int level = 50;
        }

        new PocketMonster().getPoketLevel();
        System.out.println("메소드 실행 완료");
    }
}
  • 접근 제한자(public, private, default, protected) 사용 불가능// 메서드 내에서만 사용할 것이므로 , 접근 제한이 불필요
  • static 사용 불가능 // 메서드 내에 static 멤버 선언하는 것은 불가능
  • 위 코드에서 변수 exp 앞에 final을 생략했는데, 이는 JDK 1.8부터 컴파일러가 자동으로 붙여주기 때문이다.
    // 해당 변수(exp)의 값(5000)을 바꾸면 상수에서 변수로 바뀌기 때문에, 지역 내부 클래스에 접근이 불가능하여 오류가 나므로 주의해야 한다.

클래스의 캡슐화

기존 클래스 작성 방법


class Creature {
    int life;

    public void method() {
        Animal animal = new Animal(); // Animal 객체는 오로지 Creature 클래스의 메소드 내에서만 사용된다고 가정 
        // ...
    }
}

// 외부에 선언된 클래스
class Animal {

}

내부 클래스 작성 방법

class Creature {
    int life;
	
    // 클래스 멤버 같이 Creature 클래스 안에다 넣어 선언한다.
    class Animal {

    }

    public void method() {
        Animal animal = new Animal(); // Animal 객체는 오로지 Creature 클래스의 메소드 내에서만 사용된다고 가정 
        // ...
    }
}

이렇게 클래스 안에 넣어주게 되면, 클래스간의 상관관계를 빠르게 파악할 수 있다.(가독성 UP)

private 제어자를 사용한 내부 클래스

class Creature {
    private int life = 50;
	
    // private class 로, 오로지 Creature 외부 클래스에서만 접근 가능한 내부 클래스로 설정
    private class Animal {
        private String name = "호랑이";

        int getOuter() {
            return life; // 외부 클래스의 private 멤버를 제약 없이 접근 가능
        }
    }

    public void method() {
        Animal animal = new Animal(); 

        // Getter 없이 내부 클래스의 private 멤버에 접근이 가능
        System.out.println(animal.name); // 호랑이

        // 내부 클래스에서 외부 클래스이 private 멤버를 출력
        System.out.println(animal.getOuter()); // 50
    }
}

이렇게 내부 클래스를 사용하는 궁극적인 목적은 해당 클래스안에서만 사용할거면, 굳이 다른 곳에서 선언하지 말고, 안에서 선언하기 위함이다.

profile
Today I Learn

0개의 댓글