본문 바로가기
Programming/Java

Inheritance(상속)

Inheritance(상속): 이미 잘 개발된 클래스를 재사용해서 중복 코드 줄이기.

부모클래스 = 상위클래스

자식클래스 = 하위클래스

 

 

 

 

Car 클래스를 상속해서 SportsCar 클래스를 설계하려면?

class SportsCar extends Car {
}

[규칙]

단 상속은 딱 1개만 받을 수 있다. (여러 개 불가.)

private로 선언된 것은 상속 불가

서로 다른 패키지에 존재할 경우, default로 선언 된 메소드와 필드도 제한된다.

  -> protected의 경우 패키지가 달라도 상속을 허용한다.

 

객체 생성은 부모 먼저, 이후에 자식 객체

 

 

 

 

 

 

메소드 재정의 (overriding): 부모클래스 메소드가 자식 클래스에서 사용하기 부적합할 때, 자식클래스에 재정의해서 사용.

 

Super: 재정의한 메소드에 대해 다시 원본(부모) 메소드를 사용하고싶을 때 사용

super.method();

이런식으로 호출한다.

 

 

 

 

추가 예제

Airplane을 상속한 SupersonicAirplane class 예제

 

final이 선언된 클래스는 상속이 되지않으며 final이 선언된 메소드는 상속이되더라도 재정의할 수 없다.

public final class 클래스이름 { ... } 
public final 리턴타입 메소드( [  . ... ] ) { ... }

 

 

다형성: 객체 사용법은 동일하지만 실행결과가 다양하게 나오는 성질.

 

 

클래스 자동타입변환(promotion): 자식 클래스가 부모와 동일하게 취급되는 상태 

부모타입 변수 = 자식타입;

 

 

이렇게 변환된 경우, 변수로 접근 가능한 멤버는 부모 클래스의 것으로 한정된다. 단, 재정의(Override)된 메소드가 자식타입에 있다면 그것은 자식클래스의 것으로 받는다. 밑의 예제가 대표적이다.

Parent-method1()

Child-method2()

자동타입변환으로 method1과 method2만 취급하는 상태가 되었다.

그러나 method2는 재정의된 자식타입의 것을 사용한다. "Child-method2()" 라는 메세지가 출력된다.

그러나 method3은 부모타입에 없는 존재이므로 호출이 되지 않는다. (= method3은 손실된다.)

 

 

 

이러한 특성은 OOP가 지닌 필드의 다형성(다양한 자식객체들을 저장하여 다양한 결과 출력)을 실현한다.

ex) 어느 한 자동차 제품에 지닌 일반 타이어 값에 대해 개선된 금호, 한국 타이어개념이 알아서 적용되는 개념

 

 

 

 

 

심화예제 Tire 부모클래스

package sec02.exam03;

public class Tire {
	//필드
	public int maxRotation;     			//최대 회전수(최대 수명)
	public int accumulatedRotation;		//누적 회전수
	public String location;       			//타이어의 위치

	//생성자
	public Tire(String location, int maxRotation) {
		this.location = location;   // 타이어 위치 초기화
		this.maxRotation = maxRotation;  // 타이어 최대 회전수 초기화
	}

	//메소드
	public boolean roll() {
		++accumulatedRotation;		// 누적 회전수 1씩 증가
		if(accumulatedRotation<maxRotation) {
			System.out.println(location + " Tire 수명: " + (maxRotation-accumulatedRotation) + "회");
			return true;  // 정상회전(누적<최대)일 경우에 실행
		} else {
			System.out.println("*** " + location + " Tire 펑크 ***");
			return false; // 펑크 (누적=최대)일 때 실행
		}
	}
}

 

그리고 Tire 클래스를 이용하는 Car 클래스

package sec02.exam03;

public class Car {
	//필드: 자동차는 4개의 타이어를 가짐
	Tire frontLeftTire = new Tire("앞왼쪽", 6);
	Tire frontRightTire = new Tire("앞오른쪽", 2);
	Tire backLeftTire = new Tire("뒤왼쪽", 3);
	Tire backRightTire = new Tire("뒤오른쪽", 4);
	
	//생성자
	
	//메소드: 각 Tire 객체에 roll() 메소드 호출. False 리턴시 stop() 호출
	int run() {
		System.out.println("[자동차가 달립니다.]");
		if(frontLeftTire.roll()==false) { stop(); return 1; };
		if(frontRightTire.roll()==false) { stop(); return 2; };
		if(backLeftTire.roll()==false) { stop(); return 3; };
		if(backRightTire.roll()==false) { stop(); return 4; };
		return 0;
	}
	
	void stop() { // 펑크났을 때 실행
		System.out.println("[자동차가 멈춥니다.]");
	}
}

 

 

여기에 대해 자식 클래스 금호타이어와 한국타이어

package sec02.exam03;

public class KumhoTire extends Tire {
	//필드
	//생성자
	public KumhoTire(String location, int maxRotation) {
		super(location, maxRotation);
	}	
	//메소드 재정의
	@Override
	public boolean roll() {
		++accumulatedRotation;		
		if(accumulatedRotation<maxRotation) {
			System.out.println(location + " KumhoTire 수명: " + (maxRotation-accumulatedRotation) + "회");
			return true;
		} else {
			System.out.println("*** " + location + " KumhoTire 펑크 ***");
			return false;
		}
	}
}
package sec02.exam03;

public class HankookTire extends Tire {
	//필드
	//생성자
	public HankookTire(String location, int maxRotation) {
		super(location, maxRotation);
	}	
	//메소드 재정의
	@Override
	public boolean roll() {
		++accumulatedRotation;		
		if(accumulatedRotation<maxRotation) {
			System.out.println(location + " HankookTire 수명: " + (maxRotation-accumulatedRotation) + "회");
			return true;
		} else {
			System.out.println("*** " + location + " HankookTire 펑크 ***");
			return false;
		}
	}
}

 

 

 

 

그리고 위의 모든 것을 이용하는 실행예제

package sec02.exam03;

public class CarExample {
	public static void main(String[] args) {
		Car car = new Car();  // Car 객체 생성
		
		for(int i=1; i<=5; i++) {   // Car 객체의 run() 메소드 5번 반복 실행
			int problemLocation = car.run();
			switch(problemLocation) {  // 문제 발생시 교체
				case 1:
					System.out.println("앞왼쪽 HankookTire로 교체");
					car.frontLeftTire = new HankookTire("앞왼쪽", 15);
					break;
				case 2:
					System.out.println("앞오른쪽 KumhoTire로 교체");
					car.frontRightTire = new KumhoTire("앞오른쪽", 13);	
					break;
				case 3:
					System.out.println("뒤왼쪽 HankookTire로 교체");
					car.backLeftTire = new HankookTire("뒤왼쪽", 14);	
					break;
				case 4:
					System.out.println("뒤오른쪽 KumhoTire로 교체");
					car.backRightTire = new KumhoTire("뒤오른쪽", 17);		
					break;
			}
			System.out.println("----------------------------------------");
		}
	}
}

[자동차가 달립니다.]

앞왼쪽 Tire 수명: 5회

앞오른쪽 Tire 수명: 1회

뒤왼쪽 Tire 수명: 2회

뒤오른쪽 Tire 수명: 3회

----------------------------------------

[자동차가 달립니다.]

앞왼쪽 Tire 수명: 4회

*** 앞오른쪽 Tire 펑크 ***

[자동차가 멈춥니다.]

앞오른쪽 KumhoTire로 교체    <- 펑크나서 바로 교체됨

----------------------------------------

[자동차가 달립니다.]

앞왼쪽 Tire 수명: 3회

앞오른쪽 KumhoTire 수명: 12회

뒤왼쪽 Tire 수명: 1회

뒤오른쪽 Tire 수명: 2회

----------------------------------------

[자동차가 달립니다.]

앞왼쪽 Tire 수명: 2회

앞오른쪽 KumhoTire 수명: 11회

*** 뒤왼쪽 Tire 펑크 ***

[자동차가 멈춥니다.]

뒤왼쪽 HankookTire로 교체   <- 펑크나서 바로 교체됨

----------------------------------------

[자동차가 달립니다.]

앞왼쪽 Tire 수명: 1회

앞오른쪽 KumhoTire 수명: 10회

뒤왼쪽 HankookTire 수명: 13회

뒤오른쪽 Tire 수명: 1회

----------------------------------------

 

 

 

casting(강제타입변환): 자식타입이 부모타입으로 자동변환 된 상황에서 다시 자식타입으로 변환하고자 할때 사용한다.

package sec02.exam05;

public class ChildExample {
	public static void main(String[] args) {
		Parent parent = new Child(); // 자동 타입 변환
		parent.field1 = "data1";
		parent.method1();
		parent.method2();
		/*
		parent.field2 = "data2";  //(불가능)  
		parent.method3();         //(불가능)   <-자식에만 있는 것을 사용하고싶다면 강제변환필요!
		*/
		
		Child child = (Child) parent;  // 강제 타입 변환(다시 자식으로)
		child.field2 = "yyy";  //(사용 可)
		child.method3();     //(사용 可)
	}
}

 

강제타입변환은 자식이 부모로 바뀐 경우에서 다시 되돌릴 때만 사용 가능하다.

즉, 원래부터 부모클래스인 경우는 자식클래스로 바꿀 수 없다.

 

 

처음부터 method3을 쓸 예정이였다면 자동타입변환을 사용하지 않는다.

그러나 예측 못한 일로 인한 이유로 사용하게 된 경우에 이렇게 보수(?) 하는 의미로 쓰게된다.

 

 

 

 

instanceof: 어느 객체가 어느 클래스의 인스턴스인지 확인 (강제타입변환때 오류가 생길 수 있으므로 확인용)

parent void method(Parent parent) {
  if(parent instanceof Child) {  //Parent 매개변수가 참조하는 객체가 Child인가?
     Child child = (Child) parent;
  }
}

즉, instanceof는 강제타입변환의 대상 클래스가 기존의 자식클래스로 부모클래스를 상속받은 것인지를 판별한다.

 

 

 

 

 

 

 

Abstract Class(추상 클래스): 여러 클래스의 공통된 특성(필드, 메소드)를 추출해서 선언한 것

추상클래스는 규격만 줄 뿐, 객체생성 클래스가 아님. 부모로 상속받기만 함. abstract class로 규격을 만든다.

실체 클래스에 반드시 존재해야할 필드와 메소드 선언 후 나머지는 하청맡기는 느낌  (설계 규격만)

 

추상 메소드: 선언만 통일하고 실행 내용은 클래스마다 달라야하는 경우 (반드시 자식클래스에서 재정의되야 함)

                     즉, 하위클래스가 실행내용을 구체적으로 채우도록 강제하고 싶은 메소드

 

package sec03.exam02;

public abstract class Animal {  // 추상 클래스 선언
	public String kind;
	
	public void breathe() {  // 추상 메소드 아님. 만들어두었으니 쓸려면 쓰세요 느낌
		System.out.println("숨을 쉽니다.");
	}

	public abstract void sound();   // 추상 메소드 선언
}

이렇게 양식을 주면 이 양식을 상속받아서 자식클래스에서 알아서 다음과 같이 코드를 작성해야한다. (dog, cat)

 

이러한 클래스들은 밑의 예제에서 3가지 방법으로 실행이 가능하다.

package sec03.exam02;

public class AnimalExample {
	public static void main(String[] args) {    //일반적인 변수 지정으로 호출하기 [1]
		Dog dog = new Dog();  
		Cat cat = new Cat();
		dog.sound();
		cat.sound();
		System.out.println("-----");
		
		//Animal 변수로 자동 타입 변환하여 sound()호출하기 [2]
		Animal animal = null; 
        // 객체생성아님. Animal이라는 class type에 animal이라 이름붙이고 null 지정.
		animal = new Dog();  // 그 animal에 new Dog()으로 구체화된 상속클래스 객체 형성
		animal.sound();  // 추상클래스 조건상 sound가 반드시 들어가야함
		animal = new Cat(); //그 animal에 new Cat()으로 구체화된 상속클래스 객체 형성
		animal.sound();   // 추상클래스 조건상 sound가 반드시 들어가야함
		System.out.println("-----");
		
		//매개변수의 자동 타입 변환으로 호출하기 [3]
		animalSound(new Dog());  // 지금까지 나온 모든 new Dog() 또는 new Cat()은 다른 객체를 지닌다!
		animalSound(new Cat());
	}
// 자바에서는 animalSound가 뒤에서 정의되더라도 컴파일링 원리 때문에 상관없이 작동한다.	
	public static void animalSound(Animal animal) { // Animal type의 animal이라는 변수 사용
		animal.sound(); // Animal은 추상클래스. 그럼 그 상속클래스는? 라는 식으로 구체화되어 실행된다.
	}
}

멍멍

야옹

-----

멍멍

야옹

-----

멍멍

야옹

 

 

 

 

위의 예제에서 breathe()를 추가한 실행버전은 다음과 같다.

package sec03.exam02;

public class AnimalExample {
	public static void main(String[] args) {    //일반적인 변수 지정으로 호출하기 [1]
		Dog dog = new Dog();  
		Cat cat = new Cat();
		dog.sound();
		cat.sound();
		cat.breathe(); // 아무때나 호출 가능한 부모 클래스의 메소드
		System.out.println("-----");
		
		//Animal 변수로 자동 타입 변환하여 sound()호출하기 [2]
		Animal animal = null; 
		animal = new Dog();  
		animal.sound();
		animal = new Cat();
		animal.sound();  
		animal.breathe(); // 아무때나 호출 가능한 부모 클래스의 메소드
		System.out.println("-----");
		
		//매개변수의 자동 타입 변환으로 호출하기 [3]
		animalSound(new Dog()); 
		animalSound(new Cat());
	}
// 자바에서는 animalSound가 뒤에서 정의되더라도 컴파일링 원리 때문에 상관없이 작동한다.	
	public static void animalSound(Animal animal) { // Animal type의 animal이라는 변수 사용
		animal.sound();
		animal.breathe(); // 부모 클래스에 있는 메소드 호출!
	}
}

멍멍

야옹

숨을 쉽니다.

-----

멍멍

야옹

숨을 쉽니다.

-----

멍멍

숨을 쉽니다.

야옹

숨을 쉽니다.

 

728x90
반응형