[자바]/자바

[Java] 내부 클래스, 네스티드(Nested) 클래스, 익명 클래스

쿠릉쿠릉 쾅쾅 2022. 1. 27. 17:07
728x90

 

 

 

 

📍 학습목표
네스티드(Nested)클래스 대해서 알아보자

 

 

네스티드(Nested) 클래스 (내부 클래스)

  • 클래스 내에 정의되는 모든 클래스
  • 내부 클래스도 클래스이기 때문에 abstractfinal과 같은 제어자를 사용할 수 있다. 또, 멤버변수처럼 private, protected와 같은 접근제어자도 사용 가능하다.
  • 내부 클래스 중에서 static 클래스만 static 멤버를 가질 수 있다. 다만, final과 static이 동시에 붙은 변수는 상수(constant)이므로 모든 내부 클래스에서 정의가 가능하다.
  • 네스티드 클래스는 static의 선언 여부를 기준으로 나눌 수 있다.
    • Static 네스티드 클래스 (스태틱 클래스)
    • Non-static 네스티드 클래스 (이너(Inner) 클래스)
      • 멤버 클래스 (인스턴스 클래스)
      • 로컬 클래스 ( 지역 클래스)
      • 익명 클래스 (익명 클래스)

 

 

Static 네스티드 클래스

  • 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 static 멤버처럼 다뤄진다.
  • 주로 외부 클래스의 static 메서드와 static 변수와 작업한다.
  • Static 클래스는 외부 클래스의 인스턴스 변수와 메서드에 접근이 불가능하다.
더보기

예제

public  class Prac {	
	
    public static void main (String[] args) {    
    	
    	Outer.Nested1 nst1 = new Outer.Nested1();
    	nst1.add(5);
    	
    	Outer.Nested2 nst2 = new Outer.Nested2();
    	System.out.println(nst2.get());
    	
    }
    
}

class Outer {
	private static int num = 0;
	
	static class Nested1 {  // static 클래스
		void add(int n) {num += n;}
	}
	
	static class Nested2 {  // static 클래스
		int get() {return num;}
	}
	
}
5
  • Nested1 클래스와 Nested2 클래스는 Outer의 static 멤버 num에 접근하고 있다. num 멤버변수가 private로 선언되어 있어도 접근이 가능하다.
  • Static 클래스 내에는 외부 클래스의 static 멤버변수와 메소드에만 접근이 가능하다.

 

내부 클래스

  • 내부 클래스는 static이 붙지 않는 내부 클래스다.
  • 내부 클래스는 3가지 종류의 클래스로 나눌 수 있다.
    • 인스턴스 클래스
    • 지역 클래스
    • 익명 클래스

 

🔍 인스턴스 클래스

  • 외부 클래스의 인스턴스 변수, 인스턴스 메소드와 동일한 위치에 선언된다.
  • 외부 클래스의 인스턴스 멤버처럼 다뤄진다.
  • 주로 외부 클래스의 인스턴스 멤버들과 작업한다.
  • 클래스의 정의를 감춰야할 때 유용하게 사용한다.
더보기

예제

public  class Prac {	
	
    public static void main (String[] args) {    

    	Outer o1 = new Outer();
    	Outer o2 = new Outer();
    	
    	// o1 기반으로 두 인스턴스 생성
    	Outer.Member o1m1 = o1.new Member();
    	Outer.Member o1m2 = o1.new Member();

    	// o2 기반으로 두 인스턴스 생성
    	Outer.Member o2m1 = o2.new Member();
    	Outer.Member o2m2 = o2.new Member();
    	
    	// o1 기반으로 생성된 두 인스턴스의 메서드 호출
    	o1m1.add(5);
    	System.out.println(o1m1.get());
    	
    	// o2 기반으로 생성된 두 인스턴스의 메서드 호출
    	o2m1.add(7);
    	System.out.println(o2m1.get());
    	
    }
    
}

class Outer {
	private int num = 0;
	
	class Member {
		void add(int n) {num += n;}
		int get() {return num;}
	}
	
}
5
7
  • Member 클래스의 인스턴스는 외부 클래스의 인스턴스에 종속적이다.

 


 

예제2

public  class Prac {	
	
    public static void main (String[] args) {    
    	Papers p = new Papers("서류 출력");
    	Printable prn = p.getPrinter();
    	prn.print();
    }
}

interface Printable {
	void print();
}

class Papers {
	private String con;
	
	public Papers(String s) {con = s;}
	
	// 인스턴스 클래스 생성 및 반환
	public Printable getPrinter() {
		return new Printer();
	}
	
	//인스턴스 클래스 정의
	private class Printer implements Printable {
		public void print() {
			System.out.println(con);
		}
	}
	
}
서류 출력
  • Papers 클래스의 외부에서는 getPrinter() 메서드가 어떠한 인스턴스의 참조 값을 반환하는지 알지 못한다. 다만 반환되는 참조 값의 인스턴스가 Printable을 구현하고 있어서 Printable의 참조 타입으로 다룰 수 있다는 것만 알 수 있다. 이러한 상황을 '클래스의 정의가 감추어진 상황'이라고 한다.
  • 이렇게 클래스의 정의를 감추면, getPrinter() 메서드가 반환하는 인스턴스가 다른 클래스의 인스턴스로 변경되어도 Papers 클래스 외부의 코드는 조금도 수정할 필요가 없다. (코드에 유연성 추가)
  • Iterator<E> 인터페이스가 그런 예다.

 

 

🔍 지역 클래스 

  • 외부 클래스의 메서드나 초기화 블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있다.
  •  지역 클래스는 외부 클래스의 인스턴스 멤버와 static 멤버를 모두 사용할 수 있다.
  • 지역 클래스가 포함된 메서드에 정의된 지역변수도 사용할 수 있다. 단, final이 붙은 지역변수만 접근 가능하다
    • 메서드가 수행을 마쳐서 지역변수가 소멸된 시점에도, 지역 클래스의 인스턴스가 소멸된 지역변수를 참조하려는 경우가 발생할 수 있기 때문이다.
    • 메소드 내에서 생성된 객체는 메소드 실행이 끝나도 힙 메모리에 존재해서 계속 사용할 수 있다. 그러나 매개 변수나 지역 변수는 메소드 실행이 끝나면 스택 메모리에서 사라지기 때문에 객체에서 사용할 수 없게 되는 문제가 발생한다.
    • JDK1.8부터 지역 클래스에서 접근하는 지역 변수 앞에 final을 생략할 수 있게 바뀌었다. 대신 컴파일러가 자동으로 붙여준다. 만약에 해당 변수의 값이 바뀌는 문장이 있으면 컴파일 에러가 발생한다.
더보기

예제

public  class Prac {	
	
    public static void main (String[] args) {    
    	Papers p = new Papers("서류 출력");
    	Printable prn = p.getPrinter();
    	prn.print();
    }
}

interface Printable {
	void print();
}

class Papers {
	private String con;
	
	public Papers(String s) {con = s;}
	
	// 지역 클래스 생성 및 반환
	public Printable getPrinter() {
		
		// 지역 클래스 정의
		class Printer implements Printable {
			public void print() {
				System.out.println(con);
			}
		}
		
		return new Printer();
		
	}
	
}
서류 출력
  • Printer 클래스가 getPrinter() 메서드 안에 선언되었다.
  • 이렇듯 메서드 내에 클래스를 정의하면 해당 메서드 내에서만 인스턴스 생성이 가능하다.
  • 인스턴스 클래스보다 클래스를 더 깊이, 특정 블록 안으로 감추는 효과가 있다.

 


 

예제2

class Inner {
	
	private int outerIv = 0;
	
	void myMethod() {
		int lv = 0;
		final int lv2 = 0;   // JDK1.8부터 final 생략 가능
		
		class LocalInner {
//			lv = 5;  // 에러.
			
		}
		lv=5;
		
	}
	
}
  • 외부 클래스의 메서드 내에서 정의된 지역 클래스는 final이 붙은 지역변수만 접근 가능하다. (JDK1.8부터 지역변수에 final 생략 가능)
  • 지역클래스인 LocalInner 클래스가 포함되고 있는 myMethod() 메서드의 지역변수를 참조하는 순간 해당 지역 변수는 final이 붙은 상수로 바뀐다. (final 생략 가능)

 

🔍 익명 클래스

  • 클래스의 선언과 객체의 생성을 동시에 하는 이름 없는 클래스다. (일회용)
  • 생성자를 선언할수도 없으며, 오로지 한 개의 클래스 또는 한 개의 인터페이스를 상속 받거나 구현할 수 있다.

 

💡 익명 클래스 정의 2가지 방법

new 조상클래스() {
    // 멤버 선언
};
new 인터페이스() {
    // 멤버 선언
};

 

더보기

예제

public  class Prac {	
	
    public static void main (String[] args) {    
    	Papers p = new Papers("서류 출력");
    	Printable prn = p.getPrinter();
    	prn.print();
    }
}

interface Printable {
	void print();
}

class Papers {
	private String con;
	
	public Papers(String s) {con = s;}
	
	// 지역 클래스 생성 및 반환
	public Printable getPrinter() {
		
		return new Printable() {
			public void print() {System.out.println(con);};
		};
		
	}
	
}
서류 출력
  • getPrinter() 메서드 안에 지역 클래스를 선언하지 않고 익명 클래스로 선언하였다.

 

 

 

 

 

728x90