<상속>
1. 상속(Inheritance) 이란?
- 현실 세계:
* 부모가 자식에게 물려주는 행위
* 부모가 자식을 선택해서 물려줌
- 객체 지향 프로그램:
* 자식(하위, 파생) 클래스가 부모(상위) 클래스의 멤버를 물려받는 것
* 자식이 부모를 선택해 물려받음
* 상속 대상 : 부모의 필드와 메소드
2. 상속 개념의 활용
- 상속의 효과
* 부모 클래스 재사용해 자식 클래스 빨리 개발 가능
* 반복된 코드 중복 줄임
* 유지 보수 편리성 제공
* 객체 다형성 구현 가능
- 상속 대상 제한
* 부모 클래스의 private 접근 갖는 필드와 메소드 제외
* 부모 클래스가 다른 패키지에 있을 경우, default 접근 갖는 필드와 메소드도 제외
3. extends 키워드
- 자식 클래스가 상속할 부모 클래스를 지정하는 키워드
- 자바의 단일 상속 - 부모 클래스 나열(다중 상속) 불가
class 자식클래스 extends 부모클래스 { ... } => (O)
class 자식클래스 extends 부모클래스1, 부모클래스2 { ... } => (X)
<부모 생성자 호출 (super(...))>
1. 자식 객체 생성하면 부모 객체도 생성되는가?
- 부모 없는 자식은 없음
* 자식 객체 생성할 때는 부모 객체로부터 생성 후 자식 객체 생성
* 부모 생성자 호출 완료 후 자식 생성자 호출 완료
public CellPhone() { ... }
public DmbCellPhone {
super();
...
}
DmbCellPhone dmbCellPhone = new DmbCellPhone();
2. 명시적인 부모 생성자 호출
- 부모 객체 생성할 때, 부모 생성자 선택해 호출
자식클래스( 매개변수선언, ... ) {
super( 매개값, ... );
...
}
- super(매개값, ...)
* 매개값과 동일한 타입, 개수, 순서 맞는 부모 생성자 호출
* 부모 생성자 없다면 컴파일 오류 발생
* 반드시 자식 생성자의 첫 줄에 위치
* 부모 클래스에 기본(매개변수 없는) 생성자가 없다면 필수 작성
3. 부모 클래스 : People.java
public class People {
public String name;
public String ssn;
public People(String name, String ssn) {
this.name = name;
this.ssn = ssn;
}
}
4. 자식 클래스 : Student.java
public class Student extends People {
public int studentNo;
public Student(String name, String ssn, int studentNo) {
super(name, ssn);
this.studentNo = studentNo;
}
}
5. 자식 클래스 사용 : StudentExample.java
public class StudentExample {
public static void main(String[] args){
Student student = new Student("홍길동", "123456-1234567", 1);
System.out.println("name : " + student.name);
System.out.println("ssn : " + student.ssn);
System.out.println("studentNo : " + student.studentNo);
}
}
<메소드 재정의>
1. 메소드 재정의(@Override)
- 부모 클래스의 상속 메소드 수정해 자식클래스에서 재정의하는 것
2. 메소드 재정의 조건
- 부모 클래스의 메소드와 동일한 시그니처 가져야
- 접근 제한을 더 강하게 오버라이딩 불가
* public을 default나 private로 수정 불가
* 반대로 default는 public으로 수정 가능
- 새로운 예외(Exception) throws 불가 (예외처리는 10장 참조)
3. 부모 클래스: Calculator.java
public class Calculator {
double areaCircle(double r) {
System.out.println("Calculator 객체의 areaCircle() 실행");
return 3.14159 * r * r;
}
}
4. 자식 클래스: Computer.java
public class Computer extends Calculator {
@Override
double areaCircle(double r) {
System.out.println("Computer 객체의 areaCircle() 실행");
return Math.PI * r * r;
}
}
5. 자식 클래스 사용 : ComputerExample.java
public class ComputerExample {
public static void maiin(String[] args) {
int r = 10;
Calculator calculator = new Calcultator();
System.out.println("원면적 : " + calculator.areaCircle(r));
System.out.println();
Computer computer = new Computer();
//재정의된 메서드 호출
System.out.println("원면적 : " + computer.areaCircle(r));
}
}
6. @Override 어노테이션
- 컴파일러에게 부모 클래스의 메소드 선언부와 동일한지 검사 지시
- 정확한 메소드 재정의 위해 붙여주면 OK
7. 메소드 재정의 효과
- 부모 메소드는 숨겨지는 효과 발생
* 재정의된 자식 메소드 실행
8. 부모 메소드 사용(super)
- 메소드 재정의는 부모 메소드 숨기는 효과!!
* 자식 클래스에서는 재정의된 메소드만 호출
- 자식 클래스에서 수정되기 전 부모 메소드 호출 - super 호출
* super는 부모 객체 참조(참고 : this는 자신 객체 참조)
9. super 변수 : Airplane.java
public class Airplane {
public void land() {
System.out.println("착륙합니다.");
}
public void fly() {
System.out.println("일반비행합니다.");
}
public void takeOff() {
System.out.println("이륙합니다.");
}
}
10. super 변수 : SupersonicAirplane.java
public class SupersonicAirplane extends Airplane {
public static final int NORMAL = 1;
public static final int SUPERSONIC = 2;
public int flyMode = NORMAL;
@Override
public void fly() {
if(flyMode == SUPERSONIC) {
System.out.println("초음속비행합니다.");
} else {
//Airplane 객체의 fly() 메소드 호출
super.fly();
}
}
}
11. super 변수 : SupersonicAirplaneExample.java
public class SuperSonicAirplaneExample {
public static void main(String[] args) {
SupersonicAirplane sa = new SupersonicAirplane();
sa.takeOff();
sa.fly();
sa.flyMode = SupersonicAirplane.SUPERSONIC;
sa.fly();
sa.flyMode = SupersonicAirplane.NORMAL;
sa.fly();
sa.land();
}
}
<final 클래스와 final 메소드>
1. final 키워드의 용도
- final 필드 : 수정 불가 필드
- final 클래스 : 부모로 사용 불가한 클래스
- final 메소드 : 자식이 재정의할 수 없는 메소드
2. 상속할 수 없는 final 클래스
- 자식 클래스 만들지 못하도록 final 클래스로 생성
public final class 클래스 { ... }
public final class String { ... }
public class NewString extends String { ... }
3. 오버라이딩 불가한 final 메소드
- 자식 클래스가 재졍의 못하도록 부모 클래스의 메소드를 final로 생성
<protected 접근 제한자>
1. protected 접근 제한자
- 상속과 관련된 접근 제한자
- 같은 패키지 : default와 동일
- 다른 패키지 : 자식 클래스만 접근 허용
접근 제한 | 적용할 내용 | 접근할 수 없는 클래스 |
public | 클래스, 필드, 생성자, 메소드 | 없음 |
protected | 필드, 생성자, 메소드 | 자식 클래스가 아닌 다른 패키지에 소속된 클래스 |
default | 클래스, 필드, 생성자, 메소드 | 다른 패키지에 소속된 클래스 |
private | 필드, 생성자, 메소드 | 모든 외부 클래스 |
<타입변환과 다형성>
1. 다형성(Polymorphism)
- 같은 타입이지만 실행 결과가 다양한 객체 대입(이용) 가능한 성질
* 부모 타입에는 모든 자식 객체가 대입 가능
** 자식 타입은 부모 타입으로 자동 타입 변환
* 효과 : 객체 부품화 가능
2. 자동 타입 변환(Promotion)
- 프로그램 실행 도중에 자동 타입 변환이 일어나는 것
* Animal : 부모클래스
* Cat : 자식클래스
Cat cat = new Cat();
Animal animal = cat;
Animal animal = new Cat; //가능
cat == animal; // true
- 바로 위의 부모가 아니더라도 상속계층의 상위면 자동 타입 변환 가능
- 변환 후에는 부모 클래스 멤버만 접근 가능
3. 필드의 다형성
- 다형성을 구현하는 기술적 방법
* 부모 타입으로 자동 변환
* 재정의된 메소드(오버라이딩)
4. 하나의 배열로 객체 관리
5. 타이어 클래스 : Tire.java
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() {
++accumlatedRotation; //누적 회전수 1증가
if(accumulatedRotation < maxRotation) {
System.out.println(location + " Tire 수명: " +
(maxRotation - accumulatedRotation) + "회");
return true;
} else {
System.out.println("*** " + location + "Tire 펑크 ***");
return false;
}
}
}
6. Tire를 부품으로 가지는 클래스 : CarExample.java
public class Car {
Tire[] tires = {
new Tire("앞왼쪽", 6),
new Tire("앞오른쪽", 2),
new Tire("뒤왼쪽", 3),
new Tire("뒤오른쪽", 4)
};
int run() {
System.out.println("[자동차가 달립니다.]");
for(int i=0; i<tires.length; i++) {
if(tires[i].roll() == false) {
stop();
return(i+1);
}
}
return 0;
}
void stop() {
System.out.println("[자동차가 멈춥니다.]");
}
}
7. 실행 클래스 : CarExample.java
public class CarExample {
public static void main(String[] args) {
Car car = new Car();
for(int i = 1; i <= 5; i++) {
int problemLocation = car.run();
if(problemLocation != 0) {
System.out.println(car.tires[problemLocation - 1].location +
"HankookTire로 교체");
car.tires[problemLocation - 1] = new HankookTire(
car.tires[problemLocation - 1].location, 15);
}
System.out.println("---------------------------------------");
}
}
}
8. 매개변수의 다형성
- 매개변수가 클래스 타입일 경우
* 해당 클래스의 객체 대입이 원칙이나 자식 객체 대입하는 것도 허용
** 자동 타입 변환
** 매개변수의 다형성
9. 강제 타입 변환(Casting)
- 부모 타입을 자식 타입으로 변환하는 것
자식클래스 변수 = (자식클래스) 부모클래스타입; //강제 타입 변환, 자식 타입이 부모 타입으로 변환된 상태
- 조건
* 자식 타입을 부모 타입으로 자동변환 후, 다시 자식 타입으로 변환할 때
- 강제 타입 변환이 필요한 경우
* 자식 타입이 부모 타입으로 자동 변환
** 부모 타입에 선언된 필드와 메소드만 사용 가능
* 자식 타입에 선언된 필드와 메소드를 다시 사용해야 할 경우
10. 객체 타입 확인(instanceof)
- 부모 타입이면 모두 자식 타입으로 강제 타입 변환할 수 있는 것 아님
- ClassCastException 예외 발생 가능
Parent parent = new Parent();
Child child = (Child) parent; // 강제 타입 변환을 할 수 없다.
- 먼저 자식 타입인지 확인 후 강제 타입 실행해야 함
boolean result = 좌항(객체) instanceof 우항(타입)