상속
이유빈 |
상속의 형식
class ElectricCar extends Car {
...
public void charge(int amount) {
...
}
}
- 상속을 정의하려면 자식 클래스 이름 뒤에 extends를 쓰고 부모 클래스 이름을 적으면 됨
- extends는 확장(또는 파생)한다는 의미이다
- 즉, 부모 클래스를 확장하여서 자식 클래스를 작성한다는 의미
왜 상속을 사용하는가
- 원하는 코드를 가진 클래스가 이미 존재한다면 이 클래스를 상속받아 이미 존재하는 클래스의 필드와 메소드를 재사용할 수 있음
- 상속을 사용하면 중복되는 코드를 줄일 수 있음
자바 상속의 특징
- 다중 상속(여러 개의 클래스로부터 상속 받는 것)을 지원하지 않는다
- 상속의 횟수에는 제한이 없다 (부모 → 자식 → 자식의 자식 → …)
- 상속 계층 구조의 최상위에는 java.lang.Object 클래스가 있다
상속과 접근 지정자
- 자식 클래스는 부모 클래스의 public 멤버, protected 멤버, 디폴트 멤버(같은 패키지 일때)를 상속 받음
- private 멤버는 상속되지 않음
- protected는 자식 클래스만 접근이 가능
상속과 생성자
- 자식 클래스의 객체가 생성될 때, 자식 클래스와 부모 클래스의 생성자 모두 호출됨
- 부모 클래스의 생성자 → 자식 클래스의 생성자 순으로 호출됨
- 명시적인 생성자 호출
- 자식 클래스의 생성자에 super(); 작성
- 묵시적인 생성자 호출
- 컴파일러는 부모 클래스의 기본 생성자가 자동으로 호출되도록 함
- 묵시적인 부모 클래스 생성자 호출을 사용하려면 부모 클래스에 기본 생성자(매개 변수가 없는 생성자가)가 반드시 정의되어 있어야 함
- 인수의 형태에 따라 적절한 생성자가 선택됨
- super(x, y) ⇒ 부모 클래스의 x, y 인자가 있는 생성자 호출
메소드 오버라이딩
- 자식 클래스가 부모 클래스의 메소드를 자신의 필요에 맞추어서 재정의 하는 것
-
메소드의 이름이나 매개 변수, 반환형은 동일해야 함
class A { int test() { return 1; } } class B extends A { int test(int a) { return 2; } } public class TestClass { public static void main(String[] args) { B test = new B(); System.out.println(test.test()); //A의 test()실행됨 } } //1 출력- 철자를 잘못 쓰는 경우, 새로운 메소드 이름으로 인식하여 메소드 오버라이드가 일어나지 않음
- 방지를 위해 @Override 어노테이션을 붙이는 것이 좋음
- 부모 클래스에 그런 이름의 메소드가 없다면 컴파일러에 오류 발생
- 철자를 잘못 쓰는 경우, 새로운 메소드 이름으로 인식하여 메소드 오버라이드가 일어나지 않음
- 자식 클래스가 부모 클래스의 정적 메소드와 동일한 정적 메소드를 정의하는 경우 어떤 참조 변수를 통하여 호출되는지에 따라 실행되는 메소드가 달리짐
- 하나는 정적 메소드이고, 다른 하나가 일반 메소드일 경우 오류 발생
키워드 super를 사용하여 부모 클래스 멤버 접근
- super는 상속 관계에서 부모 클래스의 메소드나 필드를 명시적으로 참조하기 위해 사용됨
- 만약 부모 클래스의 메소드를 오버라이딩한 경우에 super를 사용하면 부모 클래스의 메소드를 호출할 수 있음
- 예) super.draw → 부모 클래스의 draw() 호출
오버라이딩과 오버로딩
- 오버로딩이란 같은 메소드명을 가진 여러 개의 메소드를 작성하는 것
- 오버라이딩은 부모 클래스의 메소드를 자식 클래스가 다시 정의 하는 것
- 다형성
- 이들은 모두 다형성과 관련이 있음
- 이름을 재사용한다는 것이 같음
- 오버로딩은 컴파일 시간에서의 다형성을 지원함
- 메소드 오버라이딩은 실행 시간에서의 다형성을 지원함
다형성
- 객체들의 타입이 다르면 똑같은 메시지가 전달되더라도 서로 다른 동작을 하는 것
- 예) 다양한 타입의 도형 객체들이 있을 때 draw 메시지를 보내면, 각 도형의 모습을 화면에 그려냄. 즉, 도형의 타입을 고려하지 않고 draw 메시지를 보내면 됨
업캐스팅
- 부모 클래스 변수로 자식 클래스 객체를 참조할 수 있음
- 이것을 업캐스팅이라고 함

Shape s1, s2;
s1 = new Shape(); //가능
s2 = new Rectangle(); //가능. 업캐스팅
/* s2.width
s2.height 접근 불가능 */
자식 클래스와 부모 클래스의 포함 관계

업캐스팅과 다운캐스팅
public class PhoneTest {
public static void main(String[] args) {
Phone[] phones = {
new Phone("황진이"),
new AnsweringPhone("길동이", "내일"), //업캐스팅
new SmartPhone("민국이", "저녁때", "갤러그") //업캐스팅
};
phones[0].talk();
((AnsweringPhone)phones[1]).autoAnswering(); //다운캐스팅
((SmartPhone)phones[2]).playGame(); //다운캐스팅
}
}
- 업캐스팅: 자식 객체를 부모 참조 변수로 참조하는 것
- 업캐스팅은 묵시적으로 수행될 수 있음
- 부모 클래스의 멤버 접근 가능
- 자식 클래스의 멤버 접근 불가능
- 다운캐스팅: 부모 객체를 자식 참조 변수로 참조하는 것
- 묵시적으로 수행 불가
- 명시적으로만 가능
동적 바인딩
- 여러 가지 타입의 객체를 하나의 자료 구조 안에 모아서 처리하려는 경우에 필요
- 업캐스팅의 활용
- 메소드의 매개 변수를 부모 타입으로 선언하면 훨씬 넓은 범위의 객체를 받을 수 있음
public static void printLocation(Shape s) { System.out.println("x=" + s.x + "y=" + s.y); }
instanceof 연산자
- 변수가 가리키는 객체의 실제 타입을 알고 싶으면 instanceof 연산자를 사용하면 됨
if (obj instanceof Rectangle)
System.out.println("실제 타입은 Rectangle");
종단 클래스와 종단 메소드
- 종단 클래스
- 상속을 시킬 수 없는 클래스
- 보안상의 이유로 사용
- 종단 메소드
- 서브 클래스에서 재정의할 수 없는 메소드
- 부모 클래스의 final 메소드는 불러올 수 있음
class A { final int test() { return 1; } } class B extends A { } public class TestClass { public static void main(String[] args) { B test = new B(); System.out.println(test.test()); } } //1출력
상속과 구성

- 구성은 “클래스가 다른 클래스의 인스턴스”를 클래스의 필드로 가지는 디자인 기법
- 상속은 한 객체가 클래스를 상속받아서 부모 객체릐 속성과 동작을 획득할 수 있는 기법
- 상속과 구성은 모두 클래스를 연결하여 코드 재사용성을 제공함
- 상속과 구성은 객체 지향 개발자가 사용하는 두 가지의 중요한 프로그래밍 기술
- 상속은 한 클래스를 다른 클래스에서 파생시키는 반면, 구성은 하나의 클래스를 다른 클래스의 합으로 정의함
is-a 관계 “A는 B의 일종이다”

has-a관계 “~은 ~을 가지고 있다”
- 구성 관계 또는 집합 관계를 나타냄

- 구성관계
- House에는 하나 이상의 Room이 있다. 이것은 구성이다. Room은 House 없이는 존재하지 않으므로 Room의 수명은 House에 의해 제어된다.
- 집합관계
- Block을 모아서 ToyHouse를 만들 수 있다. 이것은 집합이다. ToyHouse가 분해되더라도 Block들은 남는다.
public class A { private B b; public A(B b) { this.b = b } }
댓글