[24.10.01] TIL

yy·2024년 9월 30일

개발일지

목록 보기
114/122

java 공부중

중첩 클래스

  • 중첩클래스 : 클래스 안에 클래스

중첩클래스 종류

  • 정적 중첩 클래스 -> 정적 변수와 같은 위치에 선언 (static붙음)
  • 내부 클래스
    • 내부 클래스 -> 인스턴스 변수와 같은 위치
    • 지역 클래스 -> 지역 변수와 같은 위치
    • 익명 클래스
  • 바깥 클래스 입장에서 볼 때 안에 있는 클래스가 나의 인스턴스에 소속이 되는지에 대한 여부 차이
  • 정적 중첩 클래스 : 바깥 클래스의 인스턴스에 소속X (박스 안 박스)
  • 내부 클래스: 바깥 클래스의 인스턴스에 소속O (내 몸 안의 심장)

정적 중첩 클래스

  • static이 붙고, 중첩된 클래스와 바깥 클래스와의 연관이 없음. (바깥 클래스 인스턴스에 소속 X)
  • private 접근제어자에 접근이 가능하다는 것빼고는 그냥 다른 클래스와 차이가 없다.
  • 중첩 클래스에 private 접근 제어자로 되어있는 것을 보고 중첩 클래스는 바깥 클래스에서만 사용하는 것으로 개발자가 인지가 바로됨.

    public class NestedOuter {
        private static int outClassValue = 3;
        private int outInstanceValue = 2;

        static class Nested {
            private int nestedInstanceValue = 1;

            public void print() {
                // 자신의 멤버에 접근 
                System.out.println(nestedInstanceValue);

                // 바깥 클래스의 인스턴스 멤버에는 접근 불가능.
                //System.out.println(outInstanceValue);

                // 바깥 클래스의 클래스 멤버에는 접근 가능. private도 접근 가능
                System.out.println(NestedOuter.outClassValue);
            }
        }
    }
    
    
    // 인스턴스 생성할 때
    public static void main(String[] args) {
        NestedOuter outer = new NestedOuter();
        NestedOuter.Nested nested = new NestedOuter.Nested();
        nested.print();
        System.out.println("nestedClass = " + nested.getClass());
    }



내부 클래스

  • 내부 클래스 : 바깥 클래스의 인스턴스의 멤버에 접근
  • 지연 클래스: 내부 클래스의 특징 + 지역 변수에 접근
  • 익명 클래스 : 지역 클래스의 특징 + 클래스의 이름없는 특별한 클래스
  • static 안붙고, 바깥 클래스의 인스턴스에 소속 O
    public class InnerOuter {
        private static int outClassValue = 3;
        private int outInstanceValue = 2;

        class Inner {
        private int innerInstanceValue = 1;

            public void print() {
                // 자신의 멤버에 접근
                System.out.println(innerInstanceValue);

                // 외부 클래스의 인스턴스 멤버에 접근 가능, private도 접근 가능
                System.out.println(outInstanceValue);

                // 외부 클래스의 클래스 멤버에는 접근 가능. private도 접근 가능 
                System.out.println(InnerOuter.outClassValue);
                }
        }
    }
    // 인스턴스 생성할 때
    public static void main(String[] args) {
		InnerOuter outer = new InnerOuter();
        //인스턴스값을 참조해야하기 때문에 인스턴스 생성 후 outer.new Inner()로 내부 클래스가 생성되어야함.
		InnerOuter.Inner inner = outer.new Inner();
        inner.print();
		System.out.println("innerClass = " + inner.getClass());     
		}

  • 내부 인스턴스가 바깥 인스턴스 안에 생성되는게 아니라 바깥 인스턴스가 생성된 후 바깥 인스턴스의 참조값을 가지고 내부 인스턴스가 바깥 인스턴스와 따로 생성이 됨.

만약 바깥 클래스와 내부 클래스가 같은 이름의 변수를 가지면 어떻게 할까?

  • this와 클래스명을 통해서 접근하면 됨.
    public class ShadowingMain {
        public int value = 1;

        class Inner {
            public int value = 2;

            void go() {
                int value = 3;
                System.out.println("value = " + value); // 3
                System.out.println("this.value = " + this.value); //2
                System.out.println("ShadowingMain.value = " +
                        ShadowingMain.this.value); // 1 
            }
        }

        public static void main(String[] args) {
            ShadowingMain main = new ShadowingMain();
            Inner inner = main.new Inner();
            inner.go();
        }
    }

지역 클래스

  • 내부 클래스의 종류 중 하나로 내부 클래스의 특징을 가짐.
  • 지역 변수와 같이 코드 블럭 안에서 정의 (메서드 안에 지역 클래스 만들 수 있음)
  • 지역 클래스도 일반 클래스와 같이 인터페이스 구현할 수 있음.
public class LocalOuterV1 {
    private int outInstanceVar = 3;

    public void process(int paramVar) {
        int localVar = 1;
        class LocalPrinter { // 지역 클래스에 접근 제어자 사용 불가능.
            int value = 0;

            public void printData() {
                System.out.println("value=" + value); 
                System.out.println("localVar=" + localVar);
                System.out.println("paramVar=" + paramVar); //파라미터도 지역변수에 포함됨.
                System.out.println("outInstanceVar=" + outInstanceVar);
            }
        }
        LocalPrinter printer = new LocalPrinter();
        printer.printData();
    }

//인스턴스 생성
    public static void main(String[] args) {
        LocalOuterV1 localOuter = new LocalOuterV1();
        localOuter.process(2);
    }
}

지역 변수 캡처

public class LocalOuterV3 {
    private int outInstanceVar = 3;

    public Printer process(int paramVar) {
        int localVar = 1;  //지역 변수는 스택 프레임이 종료되는 순간 함께 제거된다. 
        
        class LocalPrinter implements Printer {
        	int value = 0;
        
            @Override
            public void print () {
                System.out.println("value=" + value);
    			//인스턴스는 지역 변수보다 더 오래 살아남는다.
                System.out.println("localVar=" + localVar);
                System.out.println("paramVar=" + paramVar);
                System.out.println("outInstanceVar=" + outInstanceVar);
            }
    }

    Printer printer = new LocalPrinter();
	//printer.print()를 여기서 실행하지 않고 Printer 인스턴스만 반환한다.
	return printer;
}

public static void main(String[] args) {
        LocalOuterV3 localOuter = new LocalOuterV3();
        Printer printer = localOuter.process(2);
   	 	//printer.print()를 나중에 실행한다. process()의 스택 프레임이 사라진 이후에 실행         
    	printer.print();
	} 
}

위의 코드를 실행하면 아래와 같은 결과가 나온다.

value=0
localVar=1
paramVar=2
outInstanceVar=3

  • 지역변수의 생명주기는 짧고, 지역 클래스를 통해 생성한 인스턴스의 생명주기는 길어서 결과가 아래와 같이 나올 수가 없다. 왜냐면 인스턴스 생성하기 전에 지역변수는 사라져 버리니까!

  • 근데 여전히 변수가 출력이 되는 이유는 인스턴스를 생성할 때 지역 변수를 캡처해두기때문이다.

  • 지역 변수 캡처해두고 나서 지역 변수의 값을 변경하면 동기화문제가 생기는데 이를 방지하기 위해서 지역 클래스가 접근하는 지역변수는 절대로 값이 변하면 안된다. 그래서 final을 적어주거나 사실상 final이다. (final을 안적어도 final처리됨)


익명 클래스

  • 지역 클래스 중 하나로 클래스의 이름이 없음

  • 기존 클래스: 선언과 생성을 별도로 진행

  • 익명 클래스: 클래스 이름 생략하여 선언과 생성을 한번에 처리

  • 부모 클래스를 상속받거나, 인터페이스를 구현해야함. 익명 클래스는 상위 클래스나 인터페이가 필요.

  • 생성자 가질 수 없고, 기본 생성자만 사용됨.

  • 클래스를 별도 정의하지 않고도 인터페이스나 추상 클래스를 즉석에서 구현할 수 있어서 간결해지지만 복잡하거나 재사용이 필요한 경우는 별도 클래스를 정의하는게 좋음.

// Printer라는 인터페이스가 있다 치고

public class AnonymousOuter {
    private int outInstanceVar = 3;

    public void process(int paramVar) {
        int localVar = 1;
        
        //익명 클래스
        Printer printer = new Printer() { 
            int value = 0;

            @Override
            public void print() {
                System.out.println("value=" + value);
                System.out.println("localVar=" + localVar);
                System.out.println("paramVar=" + paramVar);
                System.out.println("outInstanceVar=" + outInstanceVar);
            }
        };

        printer.print();
        System.out.println("printer.class=" + printer.getClass());
    }

    public static void main(String[] args) {
        AnonymousOuter main = new AnonymousOuter();
        main.process(2);
    }
}
profile
시간이 걸릴 뿐 내가 못할 건 없다.

0개의 댓글