본문 바로가기
뒷북 정리 (국비 교육)/java

[Java] step10. Abstract Class

by 규글 2022. 4. 19.

step10. Abstract Class

- 01

/*
 *  [ 추상클래스 (Abstract Class) ]
 *  
 *  - class 예약어 앞에 abstract 를 명시해서 클래스를 정의한다.
 *  - 형태만 정의되고 실제 구현은 되지 않은 메소드가 존재할수 
 *    있다.
 *  - 형태만 정의된 메소드를 만들때는 abstract 예약어를 
 *    붙어서 메소드를 정의한다.
 *  - 생성자는 존재하지만 단독으로 객체 생성은 불가하다
 *  - 추상클래스 Type 의 id 가 필요하다면 추상클래스를 상속받은 
 *    자식클래스를 정의해서 객체 생성한다.
 *  - 추상클래스를 상속받은 자식클래스는 부모의 추상메소드를 
 *    모두 오버라이드(재정의) 해야한다. 
 */
/*
 *  미완성된 추상 메소드를 맴버로 가지고 있는 클래스는
 *  abstract 예약어를 붙여서 정의해야한다.  
 */
public abstract class Weapon {
	// 무기 작동을 준비하는 메소드
	public void prepare() {
		System.out.println("무기 작동을 준비 합니다.");
	}
	// 미완성된 추상메소드를 만들때는 abstract 예약어를 붙여서 메소드를 정의한다. 
	public abstract void attack();
}

 필자에게 abstract는 '개요, 초록' 이라는 의미가 익숙하지만, 여기에서는 '추상적인' 의 의미로 사용된다. 그래서 abstract class를 '추상 클래스'라고 한다. 뭔가 method가 미완성이라면 해당 method에 abstract를 붙이고, 그 method를 포함하고 있는 class에도 abstract를 붙여서 정의해야 한다. 미완성된 method라는 것이 이해할 수는 없지만 형태만 정의되고 실제로 그 기능이 구현되지 않을 수 있다고 하는데, 와닿지는 않는다. 사실은 이렇게 만들고 설계할 일은 거의 없을 것이라고는 했다. 하지만 남의 것을 import 했을 때 사용할 일이 있을 수는 있다고 했다.

 

 뭔가 전체 application(like web server) 중에서 일부만을 만들게 될 일이 있을텐데, 이런 경우 abstract class로 제공이 되어서 application의 목적에 맞게 프로그래밍 할 수 있게끔 되어있을 것이라고 했다.

 

public class MyWeapon extends Weapon{
	// 상속받은 Weapon 클래스의 추상 메소드를 강제로 오버라이드 해야한다.
	@Override
	public void attack() {
		System.out.println("내가 알아서 공격해요!");
	}
}

 이렇게 만들어진 abstract class는 생성자가 존재하기는 하나 new를 이용해서 단독으로 객체를 생성하는 것은 불가능하다고 한다. 만약 이 abstract class type 객체의 id가 필요하다면 이 class를 확장시킨 자식 class를 정의해서 객체로 만들어야 한다. 그리고 이 자식 class를 정의할 때에는 미완성된 abstract method를 강제로 override 해야한다. 그래도 class 안의 모양은 내용이 없을 뿐이지 완벽하며, type으로써의 역할로는 충분하다고 했다.

 

- 02

	public static void main(String[] args) {
		//여러분이 직접 클래스를 만들고 객체 생성을 해서 
		//아래의 useWeapon() 메소드를 호출해 보세요.
		Weapon w = new HeavyMachinegun();
		
		MainClass02.useWeapon(w);
	}
	
	public static void useWeapon(Weapon w) {
		w.prepare();
		w.attack();
	}

 Weapon class를 확장시켜 만든 자식 class를 가지고 한 예시이다.

 

- 03

// 동물원 클래스 
public class Zoo {
	// 메소드
	public Monkey getMonkey() { // 내부 클래스로 객체를 생성해서 리턴해주는 메소드 
		return new Monkey();
	}
	public Tiger getTiger() {
		return new Tiger();
	}
	
	// 클래스 안에 클래스 (내부 클래스)
	public class Monkey{
		public void say() {
			System.out.println("안녕! 나는 원숭이야");
		}
	}
	// 내부 클래스 
	public class Tiger{
		public void say() {
			System.out.println("안녕! 나는 호랑이야");
		}
	}
}
		// Zoo 클래스에 있는 getMonkey() 메소드를 호출해서
		// 리턴되는 값을 m1 이라는 지역 변수에 담아 보세요.
		Zoo z=new Zoo();
		// inner class type Monkey
		Monkey m1=z.getMonkey();
		//메소드 호출하기
		m1.say();
		
		// Zoo 클래스에 있는 getTiger() 메소드를 호출해서
		// 리턴되는 값을 t1 이라는 지역 변수에 담아 보세요.
		Tiger t1=z.getTiger();
		t1.say();

 class 안에 class를 새롭게 정의할 수도 있는데, 이렇게 class 안에 있는 class를 inner class (내부 클래스) 라고 한다. 이로써 class 안에는 field, method, constructor에 이어 class까지 총 4가지가 존재할 수 있다는 것을 알게 되었다.

 

- 04

/*
 *  클래스 안에 만든 클래스 : inner class
 *  메소드 안에 만든 클래스 : local inner class
 */
public class MainClass04 {
	public static void main(String[] args) {
		// 헉! 메소드 안에서도 클래스 정의 가능?
		class Gura{
			public void say() {
				System.out.println("안녕 나는 구라야!");
			}
		}
		// 메소드 안에 정의한 클래스를 이용해서 객체 생성하고 참조값을 지역 변수에 담기
		Gura g1=new Gura();
		g1.say();
	}
}

 class 안에 class를 만들 수도 있지만, method 안에 class를 만들 수도 있다. 이렇게 method 안에 만들어진 class를 local inner class라고 한다. 이 local inner class는 해당 method의 안에서만 사용할 수 있다.

 

- 05

public class MainClass05 {
	//필드 
	public String myName="김구라";
	public static String yourName="원숭이";
	
	public static void main(String[] args) {
		//static 메소드 안에서 this 는 의미도 없고 사용불가
		//System.out.println(this.myName);
		
		//myName 이라는 필드는 non static 이므로 클래스명. 으로 접근 불가
		//System.out.println(MainClass05.myName);
		
		//myName 은 non static 필드임으로 참조값으로 접근 가능  
		System.out.println(new MainClass05().myName);
		
		// yourName 이라는 필드는 static 이므로 클래스명. 으로 접근 가능
		System.out.println(MainClass05.yourName);
		
		// yourName 과 main() 메소드는 동일 클래스 내부에 있기 때문에 클래스명 생략 가능
		System.out.println(yourName);
		
		//static 메소드 안에서 this 는 의미도 없고 사용불가 
		//System.out.println(this.yourName);
	}
}

 main class에 만들어진 static method 안에서 field를 사용할 수 있을까? 이전에 class 안의 method에서 해당 class로 만든 객체의 field를 사용하기 위해서 this를 사용했다. 하지만 static method 안에서 this는 의미도 없고 소용도 없다. 다음의 이미지를 보도록 하자.

 

 this는 heap 영역에서 자기 자신이 포함된 객체의 참조값을 의미하는 것이다. 하지만 static 영역에 있는 친구들은 참조값의 개념이 아니라고 했다. 때문에 static 영역에서의 this는 말이 안된다.

 

 다시 예시로 돌아오면 해당 static method 내에서 field를 사용하기 위해서는 this를 사용하는 것이 아니라 해당 객체를 직접 생성하거나, static field인 경우 class name에 점을 찍어 직접 참조하는 방식이어야 한다.

 

- 06

public class MainClass06 {
	// static inner class 로 Weapon 추상 클래스 상속 받기 
	public static class YourWeapon extends Weapon{
		@Override
		public void attack() {
			System.out.println("아무나 공격해요");
		}	
	}
	
	public static void main(String[] args) {
		/*
		 *  useWeapon() 메소드를 호출하는게 목적이다.
		 *  호출하려고 보니 Weapon 추상 클래스 type 객체의 참조값이 필요하다
		 *  따라서 Weapon  추상 클래스를 상속받은 클래스를 파일로 만들어야 된다.
		 *  근데 불금이다보니 만사가 구찮다. 클래스를 파일로 만들기 싫다
		 *  내부클래스(inner class) 로 만들고 싶다. 		 
		 */
		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();
	}
}

 특별히 inner class에 한에서 static을 붙이는 것이 가능하다. 여기에서 왜 inner class에 static을 붙였을까? 그 이유는 static을 붙이지 않는다면 static method인 main method에서 class를 참조할 수 없기 때문이다.

 

- 07

	// run 했을때 실행순서가 시작되는 특별한 main 메소드 
	public static void main(String[] args) {
		/*
		 *  Weapon 추상 클래스를 상속받은 익명의 local inner class 
		 *  의 생성자를 호출해서 Weapon type 의 참조값 얻어내서 지역변수 w1 에 담기 
		 */
		Weapon w1=new Weapon() {
			@Override
			public void attack() {
				System.out.println("아무나 공격해요!");
			}
		};
		
		Weapon w2=new Weapon() {
			@Override
			public void attack() {
				System.out.println("공중 공격을 해요!");
			}
		};
		
		useWeapon(w1);
		useWeapon(w2);
		useWeapon(new Weapon() {
			@Override
			public void attack() {
				System.out.println("지상 공격을 해요!");
			}
		});
	}
	
	public static void useWeapon(Weapon w) {
		w.prepare();
		w.attack();
	}

 이 부분이 중요하다. 예시를 보면 new Weapon( )이 미완성인 abstract class임에도 불구하고 만들어진다. 그리고 뒤에 중괄호 { } 가 있다. 어떻게 된 일일까?

 (세뇌식으로 수업) 우선 여기에서의 중괄호 { }는 class이다. 그런데 그 class name이 없이 익명이다. 그렇다면 무엇일까? 일단은 main method 안에 있는 class이기 때문에 local inner class인 것이다. 이름이 없는 익명의 local inner class. 그 바로 앞에 있는 Weapon을 extends 하여 만들어지는 객체인 것이다. 그리고 new Weapon( ) 을 통해 호출되는 생성자는 Weapon class의 것이 아니라 이 익명 class의 것이다. 이렇게 만들어진 객체가 Weapon type이 되는 것은 확실하다.

 결과적으로 이 자리는 약식으로 즉석에서 완성하고, 그 완성한 class로 만들어진 객체의 참조값이 되는 것이다. 이를 anonymous inner class라고 한다. 그리고 그 참조값을 field에 넣고 싶을 경우가 다음의 예시가 된다.

 

- 08

public class MainClass08 {
	// 클래스의 필드에 Weapon type 의 참조값 넣어두기
	static Weapon w1=new Weapon() {	// anonymous inner class 
		@Override
		public void attack() {
			System.out.println("수중 공격을 해요");
		}
	};
	
	public static void main(String[] args) {
		//필드에 저장된 Weapon type 의 참조값을 이용해서 useWeapon() 메소드 호출하기
		useWeapon(w1);
	}
	public static void useWeapon(Weapon w) {
		w.prepare();
		w.attack();
	}
}

'뒷북 정리 (국비 교육) > java' 카테고리의 다른 글

[Java] step12. Generic Class  (0) 2022.04.22
[Java] step11. Interface  (0) 2022.04.19
[Java] step09. Extends  (0) 2022.04.19
[Java] step08. Array  (0) 2022.04.17
[Java] step07. Wrapper Class  (0) 2022.04.17

댓글