국비교육(22-23)

17일차(3)/java(18) : Inner Class, Anonymous Class

서리/Seori 2022. 10. 30. 14:23

InnerClass, Anonymous Class

 

<Zoo> 클래스 생성

package test.mypac;

//동물원 클래스
public class Zoo {
	
	//클래스 안의 클래스(내부 클래스)
	public class Monkey{
		public void say() {
			System.out.println("안녕! 나는 원숭이야");
		}
	}
	
	//내부 클래스
	public class Tiger{
		public void say() {
			System.out.println("안녕! 나는 호랑이야");
		}
	}
	
	//메소드
	public Monkey getMonkey() { //내부클래스로 객체를 생성해서 리턴해주는 메소드
		return new Monkey();
	}
	public Tiger getTiger() {
		return new Tiger();
	}
}

 

<MainClass03>

package test.main;

import test.mypac.Zoo;
import test.mypac.Zoo.Monkey;
import test.mypac.Zoo.Tiger;

public class MainClass03 {
	public static void main(String[] args) {
		//Zoo 클래스에 있는 getMonkey() 메소드를 호출해서
		//리턴되는 값을 m1이라는 지역변수에 담아보세요.
		Zoo z=new Zoo(); 
		Monkey m1=z.getMonkey();
		m1.say();
		
		Tiger t1=z.getTiger();
		t1.say();
		
		//메소드 안에도 클래스를 정의할 수 있다.
		//지역 내부 클래스, Local Inner Class
		class Banana{
			public void say() {
				System.out.println("안녕 나는 바나나야!");
			}
		}
		
		Banana b1=new Banana();
		b1.say();
	}
}

- 클래스 안에 클래스를 정의할 수 있다.

 

- InnerClass : 클래스 안에 정의된 클래스

클래스 안에 필드, 생성자, 메소드 를 정의할 수 있는데, + 여기에 클래스까지 추가하는 것!

 

public Monkey getMonkey() {
      return new Monkey();
}
- 위와 같이 객체를 생성해 내부클래스 타입으로 리턴해주는 메소드를 만들어서 객체를 생성한다.

 

- 클래스 안의 일반메소드(say)는 참조값을 얻어내서 참조한다.
- new Zoo로 객체 생성 후 m1 변수안에 getMonkey로 생성한 객체를 넣는다.

 

import test.mypac.Zoo.Monkey;
import test.mypac.Zoo.Tiger;
- 내부클래스의 경우 위와 같이 import 한다.

- class Banana 와 같이 메소드 안에도 클래스를 정의할 수 있다.
 지역변수를 만드는 곳에 정의되고, 지역 내부 클래스 Local Inner Class 라고 한다.


 

<MainClass04>

package test.main;

import test.mypac.Weapon;

public class MainClass04 {

	//내부 클래스
	static class YourWeapon extends Weapon{
		@Override
		public void attack() {
			System.out.println("공중 공격을 해요!");
		}
		
		public static void main(String[] args) {
			Weapon w1=new YourWeapon();
			useWeapon(w1);
			
			//Local Inner Class
			class OurWeapon extends Weapon{
				@Override
				public void attack() {				
					System.out.println("지겹다 이제 아무나 공격하자!");
				}
			}
			Weapon w2=new OurWeapon();
			useWeapon(w2);				
		}
		
		public static void useWeapon(Weapon w) {
			w.prepare();
			w.attack();
		}
	}
}

- Weapon 을 부모클래스로 받는 YourWeapon이라는 내부 static 클래스를 만들었다.

- 메인메소드 안에는 Weapon 을 부모클래스로 받는 OurWeapon이라는 지역 내부 클래스를 만들었다.

 

 

-  YourWeapon이 static 클래스가 아니면 위와 같이 오류 발생. 내부클래스를 static으로 수정해주어야한다.

main method는 특별하기 때문에 내부클래스도 static만 쓸 수 있다.

 

- static 영역에 메인메소드와 useWeapon이 함께 올라갈 때

 여기서 내부클래스 YourWeapon 도 static 예약어를 붙여주어야 static 영역으로 들어간다.

 

- 이전 게시물 사례처럼 별도의 클래스를 따로 만들지않고 

 같은 클래스 내에서 new를 사용하여 변수에 넣고 이것을 useWeapon에 사용될 매개변수에 대입하는 구조!

- static 메소드 안에서 어떤 클래스를 사용하려면 그 클래스도 static에 올라가 있어야 한다.


- 메소드안에서 내부 클래스를 이용해서 객체 생성가능
 내부클래스를 활용하면 YourWeapon이라는 클래스를 별도의 파일로 만들지 않고 메소드를 실행할 수 있다.
- 단, YourWeapon 내부클래스 여전히 사용되지 않는다. 그냥 객체를 전달하기위한 매개체로 사용될 뿐이다.

 


 

<MainClass05>

package test.main;

import java.util.Scanner;

import test.mypac.Weapon;

public class MainClass05 {
	//필드
	int num=999;
	String name="kim";
	Scanner scan = new Scanner(System.in);
	//필드는 선언만 하면 기본값이 들어간다.
	int weight; //0
	boolean isRun; //false
	String msg; //null
	Scanner scan2; //null
	//static 영역에 올리고싶은 필드는 static 예약을 이용해서 만든다.
	static String greet="안녕";
	
	//Anonymous Inner Class를 이용해서 Weapon type의 참조값 얻어내기
	static Weapon w1=new Weapon() {
		@Override
		public void attack() {
			System.out.println("무엇인지 모르겠지만 아무거나 공격하자!");
		}		
	};
	
	public static void main(String[] args) {
		//메소드 호출하면서 static 필드에 미리 준비된 값을 전달하기
		useWeapon(w1);
		//Anonymous Local Inner Class를 이용해서 Weapon type의 참조값 얻어내기
		Weapon w2=new Weapon() {
			@Override
			public void attack() {
				System.out.println("다시 공격하자!");
			}			
		};
		//메소드 호출하면서 지역변수에 미리 준비된 값을 전달하기
		useWeapon(w2);
		
		//메소드 호출하면서 값을 즉석에서 만들어서 전달하기
		useWeapon(new Weapon() {
			@Override
			public void attack() {
				System.out.println("신기하게 공격하기");
			}
		});
	}
	
	public static void useWeapon(Weapon w) {
		w.prepare();
		w.attack();
	}
}

- MainClass05 에 어떤 필드를 정의한다.

 

- 필드: 공간을 만들고 값을 대입하는 위치. 그 안에 코딩을 하는 건 불가능하다.

- 필드에서 메소드 안에 실행할 기능들을 나열하면 안된다.
- 하지만 필드에서 값을 만들어내는 것은 가능하다. new도 가능하다!

참고) boolean의 기본값은 false이다.

 

static Weapon w1; 

- MainClass05 클래스의 static영역에 Weapon 타입의 w1이라는 필드를 만든다.

- w1안에 현재 null 이 들어있으므로, null 값을 전달한 것이나 마찬가지다. 콘솔창에서도 오류가 발생한다.

 

- 다른 클래스를 import하지 않고 w1에 참조값을 넣는 방법은?

 

- Weapon w1=new Weapon(){}; 하면 Weapon이 미완성 클래스이기 때문에 수정하라는 창이 뜬다.

 클릭해서 메소드를 override하면 된다.
- 메인메소드 안에서 Weapon w2=new Weapon() { } 해도 똑같이 메소드가 override되는 창이 뜬다.

 

- 사용하지 않는 클래스는 굳이 만들지 않고, 메소드를 override하여 참조값을 대입한다.

 

new Weapon() { } 의 {} 부분이 클래스를 의미한다고 생각하면 된다.

class ? extends Weapon{ } 을 의미한다. 이름 없는 어떤 익명의 클래스를 만든 것이다.

 

- 익명 클래스가 Weapon 추상 클래스를 상속받는 것이라고 보면 된다.

 weapon 뒤에 {}를 열어 상속받은 것. 클래스 내부에 정의된 클래스이다.

 

- 익명의 내부 클래스 anonymous inner class 를 사용해서 참조값을 얻어낸 것이다.

 

 

- new Weapon() 은 익명 클래스의 생성자를 호출한 것이다. 호출하면서 강제로 override시킨 것.

- 익명 클래스로 이름없는 객체를 생성한 것

 

- { } { } 부분 안쪽은 모두 클래스이다!

 

Weapon w2=new Weapon(){ };

- {} 안에 정의한 것은 메소드 안에 정의한 익명 내부 클래스(anonymous local inner class) 이다.

 

- 익명클래스도 클래스이기 때문에 Weapon이라는 클래스를 상속하는 데에 override가 강제된다.

- override를 재정의하고나면 new Weapon(){} 을 w2라는 변수에 넣을 수 있다.

 이 지역변수를 useWeapon(w2);에다 전달하는 것

 


 

- 하지만 w2라는 변수는 한번밖에 사용하지 않았는데,

 변수를 굳이 만들어야할까? 이 내용을 useWeapon() 안에 넣기만 하면 되는 거 아닌가?

 

useWeapon( new Weapon() { } );

- 위와 같은 형태로 지역변수를 만들지 않고 useWeapon() 안에서 바로 객체를 만들어서

 아래 메소드의 매개변수로 전달할 수 있다.

 


 

<MainClass06>

package test.main;

import test.mypac.Weapon;

public class MainClass06 {
		
	static Weapon w1=new Weapon() {	 
		@Override
		public void attack() {
			System.out.println("편하게 공격해요");
		}
	};
	
	public static void main(String[] args) {
		Weapon w2=new Weapon() {			
			@Override
			public void attack() {
				System.out.println("아무나 공격!");
			}
		};
		
		useWeapon(new Weapon() {			
			@Override
			public void attack() {
				System.out.println("공격! 공격!");
			}
		});
	}
	
	public static void useWeapon(Weapon w) {
		w.prepare();
		w.attack();
	}
}

 

- 위와 같이 anonymous inner type을 자동완성으로 사용할 수 있다.

 

- Weapon w1=new Weapon 에서 ctrl+space 하면 자동완성되는 모양이 뜬다.
- 메인메소드 안에서도 Weapon w2=new Weapon 상태로 ctrl+space 사용가능.

- useWeapon ( new Weapon() { } ); 구조를 기억하기!