우선 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 클래스에 없으니까!
-> 디버깅을 도와준다!!
'개발자 > Java' 카테고리의 다른 글
Java (자바) 이클립스 JDK 실행 버전 변경 (0) | 2020.05.08 |
---|---|
Java (자바) 학생들 클래스 모으기 (서로 다른 클래스를 일반화 시키기) (0) | 2020.05.07 |
Java (자바) 반올림(올림, 내림) 그리고 자릿수 맞추기(포맷팅) (0) | 2020.04.25 |
Java (자바) 플레이어와 몬스터 대전 게임 만들기 (0) | 2020.04.22 |
Java (자바) 상속, 객체데이터를 이용한 문제 - 학생, 선생, 매점 직원 (0) | 2020.04.22 |