Java - 기본기

23. Inner Class, 제네릭 자료형

TerianP 2022. 3. 13.
728x90

1. InnerClass

- innerclass 란 이너 클래스로 불리는 하나의 클래스로, 클래스나 인터페이스 내부에 선언되는 클래스이다.

- 쉽게 이야기하면 클래스 안에 선언된 클래스로, 코드를 보다 읽기 쉽고, 재사용 가능하도록 하는 장점이 있다.

- 바깥쪽에 선언된 outer class 와 안쪽에 선언된 innerClass 는 서로 밀접한 관련이 있다.

 

1) 기본 innerClass

  • 기본 innerClass 말 그대로 가장 기본적인 innerClass 이다.
  • 아주 단순하게 클래스 안에 클래스를 선언하는 방법으로 사용한다.
  • InnerClass 의 인스턴스를 생성할때는 다음과 같이 사용한다.
  • innerClass 는 outer 클래스의 안쪽에 존재하기 때문에 당연히 outerClass 에서 선언된 모든 변수와 메서드를 사용할 수 있다.
public class InnerClass {
	int a = 10;
	static int b = 20;
	private int c = 30;
	private static int d = 40;
	
	class num {
		void print() {
			System.out.println("a : "+a);
			System.out.println("b : "+b);
			System.out.println("c : "+c);
			System.out.println("d : "+d);
		}
		
		void innerMethod() {
			outerMethod();
		}
	}
	
	void outerMethod() {
		System.out.println("나는 outerClass의 메서드");
	}
	
	
	
	public static void main(String[] args) {
		// 1. outerClass 인스턴스 생성
		InnerClass ic = new InnerClass(); 
		
		// 2. innerClass 인스턴스 생성 => 
		// outerClass.innerClass 참조변수명 = outClass의 인스턴스.new innerClass생성자
		InnerClass.num icn = ic.new num(); 
		
		
		
		icn.print();
		/*
		 * ## 결과 ##
		 * a : 10 
		 * b : 20 
		 * c : 30 
		 * d : 40
		 */
		
		icn.innerMethod();
		/*
		 * ## 결과 ##
		 * 나는 outerClass의 메서드
		*/
	}
}

 

2) static InnerClass

static 으로 선언된 innerClass 는 복잡한 인스턴스 생성과정없이 바로! 인스턴스 생성이 가능하다 -> static 이니까!!

다만 innerClass 를 static 으로 선언하는 경우 다음과 같은 주의점이 있다.

  • innerClass 는 static 이지만 outerClass 에서 선언된 필드 - 멤버 - 변수나 method 가 static 이 아닌 경우 static 으로 선언된 InnerClass 에서는 사용할 수 없다.
    • 이는 사실 당연한 것인데, innerClass 의 경우 static 으로 설정되면 static area 에 올라가서 바로 사용이 가능하지만 outerClass 에 선언된 멤버 변수나 메서드의 경우 static 이 붙지 않았다면 먼저 인스턴스를 생성해서 메모리에 올려주어야하기 때문에 그 전에는 사용할 수 없게 된다.
    • 이 때문에 아직 인스턴스가 생성되지 않은 InnerClass 의 안에서는 해당 변수나 메서드를 사용할 수 없는 것이다.
  • 물론 static 으로 설정된 필드 변수와 메서드의 경우 InnerClass 에서도 사용할 수 있다.
public class OuterClass {
	int a = 10;
	static int b = 20;
	private int c = 30;
	private static int d = 40;
	
	static class StaticInnerClass{
		
		// 안쪽 클래스가 static 인 경우 outerClass 의 static 변수, static 메서드만 사용가능하다
		// 이는 outerClass 의 인스턴스가 아직 생성 전이기 때문에...!!
		void print() {
//				System.out.println("a : "+a); 에러 발생
				System.out.println("b : "+b);
//				System.out.println("c : "+c); 에러 발생
				System.out.println("d : "+d);
			}
			
			void innerMethod() {
				System.out.println("나는 static InnerClass 메서드");
//				outerMethod();
				staticOuterMethod();
			}
		}
		
	void outerMethod() { // 에러 발생
		System.out.println("나는 outerClass의 메서드");
	}
	
	static void staticOuterMethod() {
		System.out.println("나는 outerClass의 static 메서드");
	}
	
	
	public static void main(String[] args) {
		StaticInnerClass sic = new StaticInnerClass();
		sic.print();
		
		sic.innerMethod();
	}
}

 

3) Local Inner Class

  • local innerClass 는 메서드 안에서 선언된 클래스이다.
  • 메서드 안에서 선언되고 사용되기 때문에 일종의 메서드 안에서만 사용되는 지역변수와 비슷하다고 생각할 수 있다.
    • 따라서 메서드가 종료되면 해당 클래스와 관련된 내용들도 같이 종료된다.
  • 보통 OuterClass 에서 메서드를 하나 선언 후 그 메서드 내부에서 클래스를 선언하고, 인스턴스를 만들어서 해당 innerClass 의 메서드를 실행하는 코드를 넣어준다. 이후 외부에서는 outerClass 의 인스턴스를 생성 후 메서드를 실행하여 innerClass 의 내용을 실행하는 방법을 사용한다 => 외부에서는 사실상 접근 불가능하다
public class OuterClass2 {

	static final int b = 40;
	
	void inner() {
		class LocalInnerClass{
			// static 으로 선언된 변수도 메서드 밖에서는 사용 불가 => 
			//LocalInnerClass 의 변수는 메서드 안에서 선언되는 지역변수처럼 취급되기에
			// 메서드가 종료되면 관련 내용도 같이 없어지기 때문
			int a = 10;
			static final int b = 30;
			
			void print() {
				System.out.println("a : "+a);
				System.out.println("b : "+b);
			}	
		}
		
		// 이렇게 LocalInnerClass 의 인스턴스 생성이 없다면 외부에서는 접근 불가...!!
		LocalInnerClass lic = new LocalInnerClass();
		lic.print(); // a 와 b 값 출력
	}
	

	
	void change() {
		// 여기서 불러오는 b 는 outerClass 에서 선언된 b 의 값 = 40
		System.out.println(b);

	}
	
	public static void main(String[] args) {
		OuterClass2 oc = new OuterClass2();
		
		oc.change(); // 40
		oc.inner(); // a 와 b 값 출력 => 
		// 참조변수 oc로 inner() 메서드 접근 -> inner() 메서드 안에 선언된 lic.print() 실행
		
//		oc.inner().lic.print(); 이런거는 불가능!!
		
		
	}
}

 

4) 익명 innerClass

  • 익명 innerClass 는 익명 클래스라고도 불리며, 생성자 안에 클래스를 껴 넣는 것을 말한다.
  • 클래스의 선언과 객체의 생성을 동시에!! 하는 이름없는 클래스로 일회용 클래스로서 사용된다.
    • 생성자 매개변수에 클래스를 끼워넣음으로써 본래 매개변수로 와야하는 클래스의 생성자로서 동작한다.
    • 만약 본래 와야하는 매개변수가 인터페이스나 추상 클래스의 구현 클래스라면 이렇게 익명 클래스를 만들어서 끼워넣는 식으로 해당 인터페이스나 추상 클래스를 상속, 구현한 클래스로서 취급한다.
  • 대표적으로 아래의 자바의 Frame 에서 이벤트와 관련해서 사용하는 예시에서 찾아볼 수 있다.
    • addWindowListener 의 생성자의 매개변수로는 WindowListener 인터페이스를 구현한 구현 클래스 혹은 추상 클래스인 WindowAdapter 를 상속받아 구현한 클래스가 와야한다.
    • 하지만 매번 이렇게 상속받아서 사용하거나 구현하기 힘들기 때문에 보다 간결하게 코드를 짜기위해 익명클래스가 사용된다.
    • 즉 addWindowListener 의 생성자 매개변수로 new WindowAdapter() 를 구현한 클래스 생성자가 오는데 이때 익명클래스를 사용한다. 익명클래스로 선언되는 { ~~~} 안쪽은 암시적으로 WindowAdapter를 상속받아 구현한 클래스가 되는 것이다.
MyWin(){
		this.setVisible(true);
		this.setBounds(200, 200, 800, 600);
		
		this.addWindowListener(new WindowAdapter() 
        	(여기서부터 클래스가 된다 => 이름없는 익명 클래스){
			@Override
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
	}

2. 제네릭 자료형

  • 제네릭 자료형은 클래스 내부에서 사용될 자료형을 본인이 지정하는 것이다.
  • 쉽게 이야기해서 클래스 내부에서 사용될 자료형을 임시 상태로 두고, 클래스 인스턴스를 만든 후 사용할 때마다 내가 원하는 자료형을 넣어서 클래스를 사용하게 되는 것이다.
  • 모든 자료형에 대해서 처리가 가능하며, 아직 자료형ㅇ르 결정하지 않은 미완성 자료형으로 선언된다.
    • 이 자료형 안에는 클래스도 올 수 있다.
  • 선언은 [클래스명]<타입명> 으로 한다. 대표적으로 아래처럼 사용한다.
    • Generic<T> : 이때 T 는 Type 을 의미하며 제네릭형으로 사용한다는 암시적 표현정도? 사실 뭐가 와도 상관없단다
  • 제네릭 타입의 가장 큰 장점은 자료형을 미리 결정하지 않는 다는 점!! 즉 인스턴스 생성해서 사용할 때 자료형을 결정하고 사용하기 때문에 코드 재사용과 편의성이 높아진다.
  • 제네릭 타입의 대표적인 것은 ArrayList. ArrayList 안에는 Integer, String 혹은 어떤 클래스의 인스턴스가 올 수 도 있다. 이 모든것은 ArrayList 가 제네릭이기 때문에 가능하다!!
  • 아래 예시처럼 내가 Integer 타입일 때, String 타입일 때 등 내가 원할 때 마다 바꿔서 해당 클래스를 사용할 수 있다.
// 제네릭 클래스 선언
public class Generic<T> {
	
	T[] array; // T 라는 타입으로 선언된 array
	
	public void setArray(T[] array) {
		this.array = array; // array 초기화
	}

	T[] getArray() {
		return array; // array 반환
	}
	
	void print(T[] array) {
		for(T a : array) {
			System.out.print(a+"  ");
		}
		System.out.println();
	}
	
	
	public static void main(String[] args) {
		// 1. Integer 타입을 선언
		Generic<Integer> gi_int = new Generic<Integer>();
		Integer[] intArr = { 10, 20, 30, 40, 50};
		
		gi_int.setArray(intArr); 
		System.out.println(gi_int.getArray()); // [Ljava.lang.Integer;@27808f31
		gi_int.print(intArr); // 10  20  30  40  50  
		
		System.out.println("--------------------------------");
		// 2. STring 타입을 선언
		Generic<String> gi_str = new Generic<String>();
		String[] strArr = {"저그","테란","프로토스","스타"};
		
		gi_str.setArray(strArr); 
		System.out.println(gi_str.getArray()); // [Ljava.lang.String;@436e852b
		gi_str.print(strArr); // 저그  테란  프로토스  스타  

	}
}

- 참고

JAVA - 익명클래스(Anonymous class)란?

 

JAVA - 익명클래스(Anonymous class)란?

오늘은 자바의 익명클래스(Anonymous Class) 혹 무명클래스 에 관해 포스팅을 할 것이다. 익명클래스는 말 그대로 익명의 성질을 가진 클래스라는 뜻이다. 즉 이름이 없는 클래스라고 불리우는데, '

mommoo.tistory.com

 

https://preamtree.tistory.com/138

 

JAVA 제네릭(Generic) 문법 정리

 클래스 설계할 때마다 제네릭 문법을 항상 까먹는다. 제네릭을 직접 사용할 일은 많지 않을 수 있다. 하지만 각종 라이브러리들에서 자주 쓰이므로 꼭 알아두는 것이 좋다. 그래서 정리해보는

preamtree.tistory.com

 

댓글