우선 abstract에 대해 먼저 설명하겠습니다

public abstract class Player {
	String playerName;
	int hp;
	int recovery;
	int blending;
	boolean isLive = true;
	int damage;
	int saveskill;
	
	// abstract 클래스를 만든다고 
	public String toString() {
		System.out.print(" HP: " + this.hp);
		return "";
	}
	
	public abstract int attack();
	
	public abstract void checkHP();
	
	public abstract void recoveryHP();
	
	// abstract 메소드를 사용할 경우 반드시 그 클래스 역시 abstract 클래스로 만들어줘야한다.
	public abstract int skill();
	
	// abstract클래스라고 일반 메소드를 아예 못 쓰는게 아니다.
//	public int skill() {
//		return 0;		
//	}

}

 

import java.util.Random;
import java.util.Scanner;

public class Sorceress extends Player {
	Random r = new Random();
	Scanner s = new Scanner(System.in);
	
	public Sorceress() {
		this.playerName = "소서러스";
		this.hp = 6500;
		this.recovery = 50;
		this.saveskill = 0;
	}
	
	public String toString() {
		System.out.print(this.playerName);
		return super.toString();
	}
	
	@Override
	public int attack() {
		this.damage = 100 + r.nextInt(200);
		return this.damage;
	}
	
	@Override
	public void checkHP() {
		if (this.hp < 1) {
			System.out.println(this.playerName + "이(가) 죽었습니다.");
			this.isLive = false;
			this.hp = 0;	// 사망시 HP 마이너스 된 값 0으로 보정.
		}
	}
	
	@Override
	public void recoveryHP() {
		if (this.hp < 6500) {
			this.hp = this.hp + this.recovery;
		}
		if (this.hp > 6500) {	// 회복은 하지만 자신의 최대 HP를 넘어갈 수 없도록 보정.
			this.hp = 6500;
		}
	}
	
	@Override
	public int skill() {
		for (int i = 0; i < 99; i++) {
			try {
				System.out.println("소서리스의 스킬을 사용하려면 '1', 모아두려면 '2'를 입력하세요. (현재 모아놓은 스킬은 " + this.saveskill + ")");
				int yourDecision = s.nextInt();
				s.nextLine();	// nextInt() return 소거용. 이거는 nextInt()에 정수가 들어왔을 경우에만 소거가 작동하지 nextInt()에 문자가 들어올 경우에는 아예 윗 라인에서 에러가 나고 바로 catch로 빠지기 때문에 입력이 들어오지 않는다.
				if (yourDecision == 1) {
					System.out.println(this.playerName + "가 메테오를 사용합니다.");
					this.damage = ((1000 + r.nextInt(4000)) * (this.saveskill + 1));
					this.saveskill = 0;
					break;	// 정상 입력이 들어왔을 경우 for문을 나가기 위한 제어.
				} else if (yourDecision == 2) {
					System.out.println("스킬을 모아둡니다.");
					this.saveskill = this.saveskill + 1;
					this.damage = 0; // 최초 턴 이후 스킬을 모았을 때 이전에 턴에 저장된 일반 공격력 값에 의해 플레이어의 공격이 실행되지 않도록 초기화.
					break;	// 정상 입력이 들어왔을 경우 for문을 나가기 위한 제어.
				} else {
					System.out.println("정수 '1' 또는 '2'만 입력해주세요.");
//					this.skill();	// for문을 돌기 때문에 얘가 있으면 안 된다. 얘가 있으면 for문 반복에 재귀 호출이 되어 버려서 오류 3번 입력시 정상 입력을 3번 해서 break를 3번 제어 받아야 루프를 완전히 나갈 수 있다.
			}
			System.out.println("");
			} catch (Exception e) {
				System.out.println("정수 '1' 또는 '2'만 입력해주세요.");
				s.nextLine();	// nextInt()에 문자열이 들어와서 오류를 내고 바로 빠졌을 경우 여기서 System.in에 들어있는 문자열을 소거시킨다. 얘가 없으면 무한루프를 타게 된다.
//				this.skill();	// for문을 돌기 때문에 얘가 있으면 안 된다. 얘가 있으면 for문 반복에 재귀 호출이 되어 버려서 오류 3번 입력시 정상 입력을 3번 해서 break를 3번 제어 받아야 루프를 완전히 나갈 수 있다.
			}
		}
			
		return this.damage;
		
	}
		
}

 

abstract란?

네... 그렇습니다. 추상화는 보면 대충 뭐구나 하지만 자세한 것은 알 수가 없죠?

자바에서도 동일합니다. Super 클래스가 '난 ABC라는 메소드가 있어. 하지만 선언만 하고 구현은 안 할거야! 그건 Sub 클래스가 할거야!' 하는 것이다. 그리고 여기서 @Override는 Super 클래스의 메소드를 재정의했다는 의미다.(@Override에 대한 자세한 내용은 하단 참조.)

그렇다면 Sub 클래스에선 어떻게 해줘야 할까요?

Sub 클래스는 '반드시 ABC메소드를 구현'해줘야 합니다. 만약 여러개의 Sub 클래스가 있고, 어떤 Sub 클래스에서는 사용하지 않는다 해도 반드시 빈 깡통 메소드라도 구현은 해야합니다.

---------------------- Super 클래스 ----------------------

public abstract class Player {
String playerName;

abstract 메소드를 사용할 경우 반드시 그 클래스 역시 abstract 클래스로 만들어줘야한다.
public abstract int skill();

}

---------------------- Sub 클래스 ----------------------


public class Sorceress extends Player {
	
	@Override
	public int skill() {
			
		return 0;
	}
}

이렇게라도 말이죠...

 

정리해보면

1. abstract 메소드를 사용하려면 반드시 abstract 클래스로 만들어줘야한다.

2. abstract 메소드를 만들면 Sub 클래스는 반드시 해당 메소드를 깡통이라도 구현해야한다.
   (abstract 클래스로 만든 후에 Sub 클래스를 만들면 알아서 @Override가 붙은 채로 자동완성 되어 있음.)

3. abstract 클래스라고 반드시 abstract 클래스만 사용해야 하는 것은 아니다. 일반 메소드도 사용할 수 있다.

 

 

그렇다면 @Override 역시 abstract와 같이 써야할까? No!!!

@Override는 abstract가 없어도 사용할 수 있다.

public class People {	// 얘는 기본적으로 java.lang.Object의 Sub class다. 즉, 'public class People extends java.lang.Object'와 같다. 
	protected String name;
	int age;
	double height;
	
//	public String toString() {
//		return this.name;
//	}
	
	public String toString() {	// 따라서 toString()은 Object 클래스에 있는 메소드이기 때문에 없어도 사용할 수 있다.
								// 다만, 위 main 메소드에서 s1.toString()는 Student@36aa7bc2 를 반환해 'Student@36aa7bc2의 점수 100, 50' 을 출력할거다.
								// 이유는 재정의 하지 않았을 경우 Object 클래스가 toString()으로 반환하는 것은 s1이라는 방의 메모리 주소 자체를 반환하기 때문이다.
		System.out.println("명단");
		return this.name;
	}
}
public class Teacher extends People {
	String studyClass;
	
	@Override	// 나는 부모클래스에 있던 메소드를 재정의해서 쓰고 있는거 맞아! 하는것이다.
	public String toString() {	// @Override에 의해 public String toString2()로 이름을 바꾸면 에러가 발생한다. (부모 클래스에 public String toString2()라는 메소드가 없기 때문.)
								// 마찬가지로 public void toString()로 메소드 타입을 바꾸면 에러가 발생한다. (부모 클래스에 public void String toString()라는 메소드가 없기 때문.)
		System.out.println(this.name + "의 나이 " + this.age + " 가르치는 반 : " + this.studyClass);
		super.toString();	// super : 부모한테 가라.
		return "";
	}
	
	// 따라서 부모 클래스가 변경되었거나 내가 실수로 메소드 이름을 잘못 적을 경우 부모 클래스에 일치하는 메소드가 확인되지 않기 때문에,
	// 너 부모 클래스 재정의해서 쓰고 있는거 맞아?? 확인해봐! 하고 <디버깅을 도와준다!>
	// 만약 부모 클래스의 메소드명이 변경되었다면, 부모 클래스에 맞게 고쳐주거나, 부모 클래스와 다르게 새롭게 메소드를 만들려는거면
	// @Override를 지우고 메소드의 이름을 부모 클래스와 다르게 바꿔 상속되지 않는 Teacher 클래스만의 메소드로 새로운 메소드를 만들어 사용한다.
	
}

위 두 코드블록을 보면 People 클래스는 Teacher 클래스의 Super 클래스다. (= Teacher 클래스는 People 클래스의 Sub 클래스다.)

@ : annotation(주석)처리
@Override : Override라는 주석처리를 했다는 것이다. 의미는 '나 Super 클래스에 있는거 정의해 사용하는거 맞아!' 하는 것이다.

왜 사용할까? 어떤 효과가 있을까? 코드블럭에도 적어놨듯이...

메소드의 이름이나 타입(인풋, 아웃풋 파라미터 정보)를 바꾸면 에러가 난다. -> 왜? 그런 메소드가 Super 클래스에 없으니까! 
-> 디버깅을 도와준다!!

+ Recent posts