[객체지향] 상속(Heritance) (다형성, 추상클래스, 인터페이스)
[객체지향] 상속(Heritance) (다형성, 추상클래스, 인터페이스)
1) 상속 (Heritance)
: (상속 받을 수 있는) 필드와 메서드를 넘겨받는 개념.
// 상위 클래스(슈퍼 클래스, 부모 클래스, 기본 클래스)
public class KoreanPeople {
public String name = "이사람";
public int years;
public String location = "대한민국";
}
// 하위 클래스(서브 클래스, 자식 클래스, 파생 클래스)
public class Students extends KoreanPeople {
public String name = "김학생";
public String school;
public void printInfo() {
System.out.println("name: " + name);
System.out.println("years: " + years);
System.out.println("Student location: " + location);
// 부모클래스 변수 출력방법
System.out.println("People location" + super.location);
}
}
// 제 3의 클래스
public class test1 {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "홍길동";
s1.years = 30;
s1.location = "서울";
// 맴버 변수를 선언하지 않아도 부모 클래스에서 변수를 상속받아 사용 가능
s1.printInfo();
// 메서드도 사용 가능
}
}
// 제 4의 클래스
public class test2 {
public static void main(String[] args) {
Student s2 = new Student();
s2.years = 20;
s2.location = "성남";
s2.school = "성남대학교";
s2.printInfo();
// name = 김학생, years = 20,
// People location = 대한민국, Student location = 성남.
// printInfo() 메서드는 Student 클래스 메서드이기 때문.
}
}
-
단일 상속
: 클래스는 한 곳에서만 상속 받을 수 있음.
-
상속과 생성자
: 부모 클래스의 생성자를 먼저 호출하고, 자식 클래스의 생성자를 그 이후에 호출한다.
// 부모 클래스 public class KoreanPeople { public String name; public int years; public String location = "대한민국"; public Korean() { System.out.println("한국사람 생성"); // 없으면 에러남. // 자식의 기본생성자에서 부모의 기본생성자를 못찾기 때문 // 자식의 기본생성자 안에 부모의 매개변수 생성자를 호출하면 괜찮음. } public Korean(int years, int location) { this.years = years; this.location = location; } } // 자식 클래스 public class Student extends KoreanPeople { public String name = "김학생"; public String school; public student() { System.out.println("학생 생성"); } public Student(int years, String location, String school) { super(years, location); // 부모 클래스 생성자로 넘기기 this.school; // 자식 클래스에서 처리하기 } } // 제 3의 클래스 public class test { public static void main(String args[]) { Student s3 = new Student(30, "부산", "부산대학교"); // "한국사람 생성" // "학생 생성" // 30과 부산은 부모 클래스, 부산대학교는 자식 클래스 생성자로 감 } }
-
접근 제어자 복습
-
2) 다형성 (Polymorphism)
: 상속관계에 따라 다른 클래스 형태로 표현하는 것.
- 업캐스팅과 다운캐스팅
: 하위 객체가 상위 클래스형 변수에 대입될 때. 즉, 상위로의 자료형 변환이 되었을 때 업캐스팅 되었다고 함.
업캐스팅된 객체는 하위 객체의 멤버를 참조할 수 없음.
다운캐스팅은 반대로 상위 클래스형을 하위 클래스 형으로 변환했을 때를 말하며, 명시적인 형변환 연산자가 필요함.
// 부모 클래스
public class KoreanPeople {
public String name;
public int years;
public String location = "대한민국";
public Korean() {
}
public Korean(int years, int location) {
this.years = years;
this.location = location;
}
}
// 자식 클래스
public class Student extends KoreanPeople {
public String name = "김학생";
public String school;
public student() {
}
public Student(int years, String location, String school) {
super(years, location);
this.school;
}
}
// 제 3의 클래스
public class test {
public static void main(String args[]) {
// 객체 생성
Student s3 = new Student(30, "부산", "부산대학교");
// 업캐스팅
KoreanPeople k3 = s3;
// 업캐스팅 시 Student의 객체라 하더라도 캐스팅 된 클래스의 변수와 메서드만 사용 가능해짐.
System.out.println(k3.location); // "대한민국"
System.out.println(k3.school); // 불가능
// 다운캐스팅
Student s4 = (Student)k3;
// k3의 본체는 Student였음.
// 업캐스팅을 해서 필요한 작업을 하고 나면 다운캐스팅해서 다시 하위 클래스의 객체에 넣어줄 수 있음.
System.out.println(s4.school); // "부산대학교"
}
}
-
A instanceof B
A(객체)가 B(클래스)의 부모클래스 객체가 맞는지 확인. 즉, 형변환이 가능한지(A가 B로 형변환이 가능한가?). 맞으면 A는 B로 다운캐스팅이 가능해짐.
=> if문에 넣어서 다운캐스팅 오류를 제한함.
if(k3 instanceof Student) {
Student s4 = (Student)k3;
} else {
System.out.println("k3는 Student 객체가 아닙니다.");
}
-
오버라이드(override)
: 메서드를 수정, 보완, 재정의 하는 것을 말함.
메서드의 1) 이름, 2) 매개변수, 3) 반환값 이 같아야 오버라이드 개념이 성립함.
=> 상속받은 하위 클래스에서 생성자 및 메서드를 오버라이드 하는 경우가 많으며, 오버라이드 된 메서드는 가장 하위 객체의 메서드를 실행하게 된다.
*메서드가 final, private인 경우는 오버라이딩 불가능
*오버로딩(overload)은 1) 이름, 2) 반환값 이 같으며 3) 매개변수의 개수나 매개변수의 자료형이 다르면 오버로딩 개념이 성립함.
3) 추상 클래스 (abstract) 와 인터페이스 (interface)
- 추상클래스
: 하위 클래스들을 대표하는 클래스 개념. 다만, ‘구체적’이지 않음.
=> 직접 객체화(instantiation) 될 수 없다. 상속해서 쓰라는 의미.
- 인터페이스
: 하위 클래스가 수행해야 하는 메서드와 필요한 상수만을 미리 추상적으로 정의해놓은 특별한 클래스. 정의만 있고 구현체는 없음.
=> 다중상속이 안되는 java의 특성을 보완. 인터페이스는 다중상속 지원.
=> 같은 인터페이스를 implement 한다면, 같은 메서드가 있다는 것을 보증할 수 있다!!
실전예제
// 부모 클래스
public (abstract) class 유저 {
String 직업명;
int 체력;
int 마나;
private int level = 1;
public void 마을에서다시태어나기() {
체력 = full;
마나 = full;
}
public void 공격() {
System.out.println("각 직업에 맞는 공격 설정");
}
// 추상클래스로 구현 시 아래처럼.
public abstract void 공격();
}
// 직업 인터페이스
public interface 캐릭터 {
public void 레벨업();
public void 전직();
}
// 자식 클래스 1
public class 전사 extends 유저 implements 캐릭터 {
public 전사() {
직업명 = "전사";
체력 = 100;
마나 = 20;
}
@override from class
public void 공격() {
System.out.println("몽둥이로 공격");
}
@override from interface
public void 레벨업() {
level++;
System.out.println("전사 LV." + level + "이 되었습니다.");
}
public void 전직() {
System.out.println("광전사 또는 갑옷전사로 전직이 가능합니다.");
}
}
// 자식 클래스 2
public class 마법사 extends 유저 implements 캐릭터 {
public 마법사() {
직업명 = "마법사";
체력 = 50;
마나 = 80;
}
@override from class
public void 공격() {
System.out.println("파이어볼 공격");
}
@override from interface
public void 레벨업() {
level++;
System.out.println("마법사 LV." + level + "이 되었습니다.");
}
public void 전직() {
System.out.println("불마법사 또는 물마법사로 전직이 가능합니다.");
}
}
// 자식 클래스 3
public class 용병 implements 캐릭터 {
@override from interface
public void 레벨업() {
System.out.println("레벨이 1 올랐습니다.");
}
public void 전직() {
System.out.println("용병은 전직이 불가능합니다.");
}
}
// 실행 클래스
public class Game {
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
System.out.print("캐릭터를 고르세요. 1=전사, 2=마법사 (1/2?)");
int input = sc.nextInt();
유저 user;
if (input==1) {
user = new 전사();
} else if (input==2) {
user = new 마법사();
} else {
System.out.println("캐릭터를 선택하지 않았습니다.");
}
user.공격(); // 전사면 몽둥이 공격, 마법사면 파이어볼 공격
user.마을에서다시태어나기(); // 오버라이드 메서드가 없으니 둘다 체력, 마나 가득 회복
}
}
마지막 수정일시: 2022-06-06 17:53
댓글남기기