[자바 기초 문법] 상속의 이해와 다형성
상속이란?
상속과 클래스 멤버
- 자식 클래스는 부모 클래스에서 물려받은 멤버를 그대로 사용 혹은 변경 가능하고, 새로운 멤버도 추가가 가능하다.
- 자식 클래스는 대체로 부모 클래스보다 속성이나 동작이 더 많다.
부모-자식 클래스의 관계는 is-a 관계이다!
원은 도형이다.
사과는 과일이다.
has-a 관계는 상속은 아니다!
상속의 선언
- extends 키워드 사용한다.
- 다중 상속은 안된다! 하나의 부모클래스만 가능하다.
현실 세계와 상속 적용
객체 지향의 상속을 적용할 수 있는 현실 세계의 예
부모 클래스 | 자식 클래스 |
Animal | Eagle,Tiger, Goldfish |
Bike | MountainBike, RoadBike, TandemBike |
Circle | Ball, Cone, Juice, Wine |
Drinks | Beer, Coke, Juice, Wine |
Employee | RegularEmployee, ContractEmployee .. |
Animal 클래스와 자식 클래스
메서드 오버라이딩이란?
물려받은 메서드를 자식 클래스에 맞도록 수정하는 것이다. 필요에 따라 상속된 메서드를 다시 정의한다고 볼 수 있다.
오버라이딩 규칙
- 부모 클래스의 메서드와 동일한 시그니처를 사용한다. 반환 타입까지 동일해야 한다.
- 부모 클래스의 메서드보다 접근 범위를 더 좁게 수정할 수 없다.
- 추가적인 예외가 발생할 수 있음을 나타낼 수 없다.
오버라이딩 불가 규칙
- private 메서드: 부모 클래스 전용이므로 자식 클래스에 상속 안된다.
- 정적 메서드: 클래스 소속이므로 자식 클래스에 상속되지 않는다.
- final 메서드: final 메서드는 더이상 수정할 수 없으므로 자식 클래스가 오버라이딩할 수 없다.
어노테이션(Annotation)
@Override //-> 어노테이션이다.
void findArea() {
//부모 클래스에서 상속받은 메서드를 수정한 코드
}
부모 클래스의 멤버 접근
- 자식 클래스가 메서드를 오버라이딩하면 자식 객체는 부모 클래스의 오버라이딩된 메서드를 숨긴다.
- 숨겨진 메서드를 호출하려면 super 키워드를 사용한다.
- super는 현재 객체에서 부모 클래스의 참조를 의미한다!
메서드 오버라이딩 vs 메서드 오버로딩
비교 요소 | 메서드 오버라이딩 | 메서드 오버로딩 |
메서드명 | 동일 | 동일 |
매개변수 | 동일 | 다름 |
반환 타입 | 동일 | 관계 X |
상속 관계 | 필요 | 필요 X |
예외와 접근 범위 | 제약 O | 제약 X |
바인딩 | 동적 바인딩(호출할 메서드 실행 중 결정한다) | 정적 바인딩(호출할 메서드 컴파일할 때 결정한다) |
패키지란?
클래스 파일을 묶어서 관리하기 위한 수단으로 파일 시스템의 폴더를 이용한다.
장점
- 패키지마다 별도의 이름 공간이 생겨서 클래스 이름의 유일성을 보장한다.
- 클래스를 패키지 단위로도 제어할 수 있기 때문에 세밀하게 접근 제어한다.
선언
- 주석문 제외 반드시 첫 라인에 위치한다.
- 패키지 이름은 소문자로 명명하며, 패키지 이름 중복을 피하기 위해 회사의 도메인 이름을 역순으로 사용한다.
package com.hankuk.people;
패키지의 사용
다른 패키지에 있는 공개된 클래스를 사용하려면 패키지 경로를 컴파일러에게 알려줘야 한다.
import문
import 패키지명.클래스;
import 패키지명.*; //*: 모든을 의미
예제
package com.hankuk.people;
import com.usa.people.Lincoln; //컴파일러에 Lincoln 클래스의 경로를 알려준다.
public class ShowWorldPeople{
public static void main(String[] args) {
Lincoln greatman = new Lincoln();
}
}
자식 클래스와 부모 생성자
자식 생성자를 호출하면, 부모 생성자도 자동으로 호출된다.
자식 생성자는 첫 행에 부모 생성자 호출 코드가 있다.
class Box{
public Box(){
...
}
}
class ColoredBox extends Box{
public ColoredBox() {
} //없다면 컴파일러가 super(): 코드를 추가한다.
} //super()에 의해 부모 생성자 Box()를 호출한다.
잘못된 예
class Box{
public Box(String s){
... //생성자가 있으므로 컴파일러는 디폴트 생성자 Box()를 추가하지 않는다.
}
}
class ColoredBox extends Box{
} //생성자가 없으므로 컴파일러가 디폴트 생성자 ColoredBox()를 추가한다. ColoredBox()는 먼저 부모 생성자 super()를 호출한다. 부모 클래스에 Box(String)은 있지만 Box()생성자는 없어 오류가 생긴다.
생성자가 없으면 컴파일러가 디폴트 생성자 Box()를 추가한다.
class Box{
//생성자 없음
}
class ColoredBox extends Box{
public ColoredBox(String s) {
super(s);
}
}
접근 지정자의 접근 범위
접근 지정자 | 클래스 내부 | 동일 패키지 | 하위 클래스 | 다른 패키지 |
public | O | O | O | O |
protected | O | O | O | X |
default | O | O | X | X |
private | O | X | X | X |
주의 사항
- private 멤버는 자식 클래스에 상속되지 않는다.
- 클래스 멤버는 어떤 접근 지정자로도 지정할 수 있지만, 클래스는 protected와 private으로 지정할 수 없다.
- 메서드를 오버라이딩할 때 부모 클래스의 메서드보다 가시성을 더 좁게 할 수는 없다.
Final 클래스
- 더이상 상속될 수 없는 클래스이며, 대표적인 final 클래스로는 String 클래스가 있다.
final 메서드
- final 클래스는 클래스 내부의 모든 메서드를 오버라이딩 할 수 없다.
- 이 때, 특정 메서드만 오버라이딩하지 않도록 하려면 final 메서드로 선언한다.
객체의 타입변환
상속관계일 경우만 타입 변환이 가능하다.
객체 타입 변환을 하는 Person 클래스
public class Person{
String name = "푸린";
void whoami(){
System.out.println("나는 푸린이다.");
}
}
객체 타입 변환을 하는 Student 클래스
public class Student extends Person {
int number = 7;
void work(){
System.out.println("나는 블로그한다.");
}
}
자동 타입 변환
Student s = new Student(); //객체 생성
Person p = s; //자동으로 타입 변환
Person p; //부모클래스 타입의 참조변수로 선언하고
Student s = new Student(); // 값을 초기화한다.
p = s; //자동으로 타입 변환되어 p = (Person)s와 동일하다.
//p.number = 1;
//p.work(); number와 work()는 부모 타입에 없는 멤버여서 부모 타입 변수에서 볼 수 없다.
p.whoami(); //Person 타입 멤버이므로 호출 가능하다.
강제 타입 변환
Person p = new Person();
Student s = (Student) p; //자식클래스로 변환 안됨. (XX)
Student s1 = new Student(); //자식 객체 타입 생성
Person p = s1;
Student s2 = (Student) p; //p는 부모 타입 변수지만 자식 객체를 가리킨다. 강제 타입 변환 가능!(OO)
참조: 어서와 Java는 처음이지!, 인피니티북스, 천인국
쉽게 배우는 자바 프로그래밍 2판, 한빛아카데미, 우종정, 2020.