Java에서 클래스간의 중복되는 것을 막아서 코드가 길어지는 것을 방지해서
유지보수를 쉽게 하는 다형성에 해당하는 기능이다
기본적으로 상속은 어떻게 이루어 지는지 그 문법을 알아보자
class Pos2D { // 슈퍼클래스 (부모클래스, 상위클래스)
private int x, y;
public Pos2D(int x, int y) {
this.x = x;
this.y = y;
}
public void show() {
System.out.println("x : " + x + ", y : " + y);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
class Pos3D extends Pos2D { // Pos2D의 내용을 상속받아서, 추가속성과 기능을 작성한 새로운 클래스
// 서브 클래스 (자식클래스, 하위클래스)
private int z;
public Pos3D(int x, int y, int z) { // 서브클래스는 슈퍼클래스의 생성자를 반드시 호출해야만 한다
// this.x = x;
// this.y = y;
super(x, y); // Pos2D(int x, int y) // 생성자의 첫번째 줄에서 호출
this.z = z;
}
@Override // 물려받은 기능(함수)의 형식은 유지하면서, 내용을 새로 작성한다
public void show() {
System.out.printf("x : %d, y : %d, z : %d\n", getX(), getY(), z);
}
public int getZ() {
return z;
}
}
public class Ex01 {
public static void main(String[] args) {
// extends : 기존의 클래스의 내용을 물려받아서(inheritance)
// 추가적인 속성과 기능을 만들어서 개념을 확장한 새로운 클래스를 작성한다
Pos2D ob1 = new Pos2D(3, 4);
ob1.show();
Pos3D ob2 = new Pos3D(2, 3, 5);
// Pos3D 호출 -> Pos2D(x, y) -> Pos3D(z) -> 객체 생성 완료
ob2.show();
}
}
위와 같이 하위 클래스의 생성자는 상위 클래스의 생성자를 나타내는 super(); 를 통해서
나타낼 수 있고, Pos2D의 내용과 Pos3D의 내용은 그 결과의 방향성이 거의 흡사하므로 상속관계
로 묶을 수 있었다
여기서 정말 중요한 내용은 상속을 하기위한 특징이라면!
ex) 1) 의사는 사람이다 (O)
2) 타이어는 자동차다 (X)
여기서 서브클래스의 객체는 슈퍼클래스의 다른 서브클래스로 대체될 수 있어야 상속관계가 성립한다고 할 수 있다.
위의 예시에서 의사는 사람에 해당하는 다른 클래스의 인스턴스로 대체될 수 있어야 한다
옆집 의사선생님 -> 옆집 학생
우리집 자동차를 타고 이동한다 -> 우리집 타이어를 타고 이동한다 (?)
의사도 사람이고, 학생도 사람이므로, 의사 객체와 학생 객체는 사람 클래스로 처리할 수 있다
아래의 코드를 보자
class Human { // 사람
private String name;
private int age;
public Human(String name, int age) {
this.name = name;
this.age = age;
// 0) 서브클래스의 생성자는 슈퍼클래스의 생성자를 반드시 호출해야 한다
// 1) 생성자를 직접 작성하지 않으면, 기본생성자를 만들어준다
// 2) 매개변수를 받는 생성자를 작성하면 기본생성자가 사라진다
// 3) 슈퍼클래스의 생성자 모양에 맞게 서브클래스에서 직접 호출하도록 설정해야 한다
}
public void show() {
System.out.printf("이름은 %s이고, 나이는 %d살입니다\n", name, age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Doctor extends Human { // 의사
private String major;
public Doctor(String name, int age, String major) {
super(name, age);
this.major = major;
}
public void show() {
System.out.printf("%s 의사 %s이고, 나이는 %d살입니다\n", major, getName(), getAge());
}
public void heal(Human target) {
System.out.println(target.getName() + " 환자를 치료합니다");
}
}
class Student extends Human { // 학생
private int score;
public Student(String name, int age, int score) {
super(name, age);
this.score = score;
}
public void show() {
System.out.printf("학생 %s이고, 나이는 %d살, 점수는 %d점입니다\n", getName(), getAge(), score);
}
}
public class Ex02 {
public static void main(String[] args) {
Doctor ob1 = new Doctor("김사부", 42, "흉부외과");
ob1.show(); // 슈퍼클래스의 메서드를 상속받은 후, 형식을 유지하면서 내용을 재정의한 오버라이딩 메서드
Student ob2 = new Student("해리포터", 24, 100);
ob2.show(); // 물려받은 내용을 덮어쓴 오버라이딩 메서드
ob1.heal(ob2);
// 서브클래스 객체는 슈퍼클래스 타입으로 참조할 수 있다
Human ref1 = ob1; // 의사는 사람이다
Human ref2 = ob2; // 학생은 사람이다
// ref1.heal(); // 의사 행세를 하고 있을때만 치료행위를 할 수 있다
ref1.show(); // 사람은 자기소개가 가능하고, 의사가 평소 하던 식 대로 자기소개를 한다
ref2.show();
}
}
실행결과
서브클래스의 객체를 슈퍼클래스타입으로 형변환 할 수 있다
슈퍼클래스의 변수로 서브클래스의 객체를 저장할 수 있다
상속받은 메서드를 재정의 하는것을 말한다
상속을 받았다? --> 슈퍼클래스에 이미 정의가 되어 있다는 말
따라서, 업캐스팅 되어도 호출 할 수 있다!
다만, 호출되었을 때 실행되는 내용은 오버라이딩 된 내용이 호출 된다
비행기에 사람을 제한해서 태우는 메서드 부터, 운행중에 아픈사람이 랜덤으로 생겨서 의사를 찾아서 치료하는 메서드를 실행하는 내용!!!
import java.util.Random;
class Airplane { // 비행기
// 사람을 태울 수 있는 좌석 5개가 있습니다
Human[] seat = new Human[5];
public boolean entrance(Human ob) { // 메인에서 어떤 타입이든, Human의 서브클래스라면 Human으로 취급한다
// 의사이든, 학생이든 모두 일반적인 '사람'으로 취급한다
for(int i = 0; i < seat.length; i++) {
if(seat[i] == null) {
seat[i] = ob;
System.out.println(ob.getName() + " 탑승했습니다 !!");
return true;
}
}
System.out.println(ob.getName() + " : 자리가 없는데 어떻게 타요 !!");
return false;
}
public void emergency() {
Random ran = new Random();
int idx = ran.nextInt(seat.length);
Human target = seat[idx];
System.out.println(target.getName() + " : 배가 아파요");
// Doctor doctor = seat[0]; // 모든 사람이 의사는 아니잖아요
// 업캐스팅(서브클래스를 슈퍼클래스로 형변환)된 상태에서는 Human으로 취급하고
// Human에는 heal() 메서드가 없다. heal()은 Doctor의 고유기능(메서드)이다
// 탑승객 중의 각 사람이 의사인지 아닌지 판별하는 과정이 추가로 필요하다
Doctor doctor = null;
for(int i = 0; i < seat.length; i++) {
Human cursor = seat[i];
if(cursor instanceof Doctor) {// 현재 바라보는 객체가 Doctor의 인스턴스인가?
doctor = (Doctor)cursor; // 다운캐스팅(강제 형변환)
break;
}
}
System.out.println("의사의 이름은 " + doctor.getName() + "입니다");
doctor.heal(target);
}
// 비행기 탑승객 끼리 자기소개를 합니다
// 오버라이딩 메서드는 다운캐스팅을 하지 않아도 호출할 수 있다
// 오버라이딩 메서드는 덮어씌워진 형태로 호출된다
public void introduce() {
for(int i = 0; i < seat.length; i++) {
Human cursor = seat[i];
cursor.show();
}
}
}
Human cursor = seat[i];
if(cursor instanceof Doctor)
이 부분에서 instanceof (하위클래스명)을 입력하면
업캐스팅된 객체가 하위클래스 소속이면 true를 반환하는 boolean타입이다
public class Ex03 {
public static void main(String[] args) {
// jdk 8 api 문서
Doctor p1 = new Doctor("정경호", 42, "흉부외과");
Student p2 = new Student("우수한", 18, 92);
Student p3 = new Student("박영재", 19, 100);
Human p4 = new Human("원종래", 20);
Human p5 = new Human("톰크루즈", 60);
Human p6 = new Human("조세호", 37);
Airplane airplane = new Airplane();
airplane.entrance(p2); // 함수를 호출하면서 매개변수의 타입이 Student -> Human으로 바뀐다
airplane.entrance(p3);
airplane.entrance(p4);
airplane.entrance(p1); // 함수를 호출하면서 매개변수의 타입이 Doctor -> Human으로 바뀐다
airplane.entrance(p5);
airplane.entrance(p6);
System.out.println();
airplane.emergency();
System.out.println();
airplane.introduce();
}
}
실행된 화면
class Animal {
String name;
public Animal(String name) {
this.name = name;
}
void bark() {
System.out.printf("%s : ...\n", name);
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
// Cannot reduce the visibility of the inherited method from Animal
@Override // 오버라이딩 할때, 접근제한자의 범위를 넓힐 수 있다 (좁힐 수는 없다)
public void bark() {
System.out.printf("%s : 야옹\n", name);
}
// 고양이의 고유 기능 (상속받은 메서드가 아니다 !!)
public void grooming() {
System.out.printf("%s가 털을 핥는다\n", name);
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override // 오버라이딩 메서드임을 명시하기 위해서, 형식을 맞추지 않으면 경고를 알리기 위해서
public void bark() {
System.out.printf("%s : 멍멍\n", name);
}
public void tailing() {
System.out.printf("%s가 꼬리를 흔든다\n", name);
}
}
public class Ex04 {
public static void main(String[] args) {
Cat cat = new Cat("나비");
Dog dog = new Dog("형욱이");
cat.bark();
cat.grooming();
dog.bark();
dog.tailing();
System.out.println();
Animal[] animalHospital = new Animal[2];
animalHospital[0] = cat; // 업캐스팅
animalHospital[1] = dog; // 서로 다른 타입의 객체를 묶어서 관리하고 싶을 때
for(int i = 0; i < animalHospital.length; i++) {
Animal a = animalHospital[i];
a.bark(); // animal 클래스에도 bark() 가 정의되어 있으므로 호출할 수 있다
// 단, 실행되는 내용은 서브클래스에서 덮어씌운 형태로 실행된다
}
Animal a1 = animalHospital[0]; // cat
Animal a2 = animalHospital[1]; // dog
a1.bark();
// a1.grooming(); // grooming() 는 Animal 클래스에 정의되어 있지 않아서 호출할 수 없다
a2.bark();
// a2.tailing(); // tailing() 는 Animal 클래스에 정의되어 있지 않아서 호출할 수 없다
for(int i = 0; i < animalHospital.length; i++) {
if(animalHospital[i] instanceof Cat) {
Cat c1 = (Cat)a1;
c1.grooming();
}
if(animalHospital[i] instanceof Dog) {
Dog d1 = (Dog)a2;
d1.tailing();
}
}
// 다형성 (polymorphism)은 하나의 객체가 슈퍼클래스 타입 혹은 서브클래스 타입으로 참조될 수 있다는 성격
// 하나의 객체가 다양한 형태로 취급될 수 있는 성격
}
}
실행결과