상속(Inheritance)

김용민·2023년 3월 27일
0

상속

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();
		
	}
}

실행결과

서브클래스의 객체를 슈퍼클래스타입으로 형변환 할 수 있다
슈퍼클래스의 변수로 서브클래스의 객체를 저장할 수 있다

만약에 타입을 슈퍼클래스로 참조하고 있더라도(업캐스팅)

오버라이딩된 메서드는 서브 클래스의 오버라이딩 메서드가 호출된다

ヾ(•ω•`)o 오버라이딩(Overriding) 이란?

상속받은 메서드를 재정의 하는것을 말한다
상속을 받았다? --> 슈퍼클래스에 이미 정의가 되어 있다는 말
따라서, 업캐스팅 되어도 호출 할 수 있다!
다만, 호출되었을 때 실행되는 내용은 오버라이딩 된 내용이 호출 된다

ヾ(•ω•`)o 대표적인 예시 코드(비행기 코드)

비행기에 사람을 제한해서 태우는 메서드 부터, 운행중에 아픈사람이 랜덤으로 생겨서 의사를 찾아서 치료하는 메서드를 실행하는 내용!!!

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();
		
	}
}

실행된 화면

ヾ(•ω•`)o 대표적인 예시 코드 (개와 고양이)

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)은 하나의 객체가 슈퍼클래스 타입 혹은 서브클래스 타입으로 참조될 수 있다는 성격
		// 하나의 객체가 다양한 형태로 취급될 수 있는 성격
		
		
		
		
	}
}

실행결과

profile
안녕하세요

0개의 댓글