Inheritance(상속): 이미 잘 개발된 클래스를 재사용해서 중복 코드 줄이기.
부모클래스 = 상위클래스
자식클래스 = 하위클래스
Car 클래스를 상속해서 SportsCar 클래스를 설계하려면?
class SportsCar extends Car {
}
[규칙]
단 상속은 딱 1개만 받을 수 있다. (여러 개 불가.)
private로 선언된 것은 상속 불가
서로 다른 패키지에 존재할 경우, default로 선언 된 메소드와 필드도 제한된다.
-> protected의 경우 패키지가 달라도 상속을 허용한다.
객체 생성은 부모 먼저, 이후에 자식 객체
메소드 재정의 (overriding): 부모클래스 메소드가 자식 클래스에서 사용하기 부적합할 때, 자식클래스에 재정의해서 사용.
Super: 재정의한 메소드에 대해 다시 원본(부모) 메소드를 사용하고싶을 때 사용
super.method();
이런식으로 호출한다.
추가 예제
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(); // 부모 클래스에 있는 메소드 호출!
}
}
멍멍
야옹
숨을 쉽니다.
-----
멍멍
야옹
숨을 쉽니다.
-----
멍멍
숨을 쉽니다.
야옹
숨을 쉽니다.
'Programming > Java' 카테고리의 다른 글
[Java web] Hello JSP (2) | 2023.10.17 |
---|---|
Interface (인터페이스) (0) | 2023.10.17 |
instance, static, package, access modifier (0) | 2023.10.16 |
OOP(Object Oriented Programming) 객체지향 프로그래밍 (0) | 2023.10.13 |
참조타입, 배열, 열거타입 (0) | 2023.10.12 |