상속은 부모 클래스의 멤버(필드, 메서드, 이너 클래스)를 내려받아 자식 클래스 내부에 포함시키는 자바 문법요소이다.
객체지향 프로그래밍의 핵심 요소 중 하나로, 기존 클래스의 필드와 메서드를 새로운 클래스에서 재사용하게 해준다. 기존 클래스의 속성과 기능을 그대로 물려받는 것이다.
상속을 사용하려면 extends 키워드를 사용하면 된다. 대상은 하나만 선택할 수 있다. 다중상속이 불가능!
전기차가 가솔린차가 Car를 상속 받은 덕분에 electricCar.move(), gasCar.move()를 사용할 수 있다. 상속은 부모의 기능을 자식이 물려 받는 것이다. 자식이 부모의 기능을 물려 받아서 사용할 수 있다 반대로 부모 클래스는 자식 클래스에 접근할 수 없다. 자식 클래스는 부모 클래스의 기능을 물려받기 때문에 접근할 수 있다. 자식 코드는 extends Parent를 통해서 부모를 알고 있다.
자바는 다중 상속을 지원하지 않는다. extend대상은 하나만 선택할 수 있다. 부모를 하나만 선택할 수 있다는 듯이다. 부모가 또 다른 부모를 하나 가지는 것은 괜찮다.
ElectricCar 뿐만 아니라 상속 관계에 있는 Car까지 포함해서 인스턴스를 생성한다.
Car, ElectricCar라는 두가지 클래스 정보가 공존하는 것이다.
상속이라고 해서 단순하게 부모의 필드와 메서드만 물려 받는것이 아니다.
상속 관계를 사용하면 부모 클래스도 함께 포함해서 생성된다. 자식과 부모가 둘 다 생성이 된다.
public class HydrogenCar extends Car{
public void fillHydrogen() {
System.out.println("수소를 충전합니다.");
}
}
HydrogenCar hydrogenCar = new HydrogenCar();
hydrogenCar.move();
hydrogenCar.fillHydrogen();
hydrogenCar.openDoor();
extends를 통해 Car를 상속받는다.
hydrogenCar의 기능을 추가한다.
업캐스팅(자동 변환)
A ac = (A) new C(); // C->A로 업캐스팅(자동 변환)을 한다.
B bc = (B) new C(); // C->B로 업캐스팅(자동 변환)을 한다.
다운캐스팅 : 캐스팅할 수 없을 때(실행할 때 예외 발생)
A aa=new A();
다운캐스팅 : 캐스팅이 가능할 때
A ab=new B();
B b=(B) ab; // A->B다운캐스팅(수동 변환)
실제 객체를 어떤 생성자로 만들었는지 클래스 사이의 상속 관계를 알아야 한다.
@Override를 작성한다.
부모 클래스에게 상속받은 메서드와 동일한 이름의 메서드를 재정의 하는 것이다. @이 붙은 부분을 어노테이션이라 한다. 주석과 비슷한 역할을 하고, 프로그램이 읽을 수 있는 특별한 주석이라고 생각하면 된다. 어노테이션이 없어도 상관없다. 메서드가 정확하게 오버라이드 되어 있어야 한다 아니면 오류를 나타낸다고 알려준다.
메서드 이름이 같고 매개변수(파라미터)가 다른 메서드를 여러개 정의하는 것
같은 이름의 메서드를 여러 개 정의했다고 이해하면 된다. 부모 메서드와 같은 메서드를 오버라이딩 할 수 있다.
하위클래스에서 상위 클래스의 메서드를 재정의하는 과정을 의미한다.
상속 관계에서 사용한다. 부모의 기능을 자식이 정의하는 것이다. 자식의 새로운 기능이 부모의 기능을 넘어서 덮어버린다.
(+) : public
(#) : protected
(~) : default
(-) : private
- 접근 제어자의 종류
private : 모든 외부 호출을 막는다.
default (package-private) : 같은 패키지안에서 호출은 허용한다.
protected : 같은 패키지안에서 호출은 허용한다. 패키지가 달라져도 상속 관계의 호출은 허용한다.
public : 모든 외부 호출을 허용한다.
private -> default -> protected -> public이 가장 많이 허용한다.
publicValue : 부모의 public 필드에 접근이 가능하다.
protectedValue = 1 : 부모의 protected가 상속 관계이므로 접근할 수 있다.
defaultValue = 1 : 부모의 default 필드에 접근하면 자식과 부모가 다른 패키지이므로 접근할 수 없다.
privateValue = 1 : 부모의 private 필드에 접근한다. 모든 외부 접근을 막으므로 자식이라도 호출할 수 없다.
- 접근 제어와 메모리 구조
본인 타입에 없다면 부모 타입에서 기능을 찾는데, 접근 제어자가 영향을 준다. 객체 내부에서는 자식과 부모가 구분되어 있기 때문이다. 자식 타입에서 부모 타입의 기능을 호출할 때, 부모 입장에서 보면 외부에서 호출한 것과 같다.
부모와 자식의 필드명이 같거나 메서드가 오버라이딩 되어 있으면, 자식에서 부모의 필드나 메서드를 호출할 수 없다. super 키워드를 사용하면 부모를 참조할 수 있다. super은 이름 그대로 부모 클래스에 대한 참조를 나타낸다.
public class Child extends Parent{
public String value = "child";
@Override
public void hello() {
System.out.println("Child.hello");
}
public void call() {
System.out.println("this value = " + value);
System.out.println("super value = " + super.value);
this.hello(); // this 생략 가능 -> 자기 자신에서 찾고, 부모요소에서 찾는다.
super.hello();
}
}
this는 자기 자신의 참조를 뜻한다. 생략이 가능하다.
super는 부모 클래스에 대한 참조를 뜻한다.
필드 이름과 메서드 이름이 같지만 super를 사용해서 부모 클래스에 있는 기능을 사용할 수 있다.
상속 관계를 사용하면 자식 클래스의 생성자에서 부모 클래스의 생성자를 반드시 호출해야 한다.
super가 있을 경우 super부터 실행이 된다.
public class ClassC extends ClassB{
public ClassC() {
super(10, 20); // 부모가 기본 생성자가 없다면 내가 직접 정의해야 한다.
System.out.println("ClassC 생성자");
}
}
cf. 부모 클래스의 생성자가 기본 생성자(파라미터가 없는 생성자)인 경우에는 super()를 생략할 수 있다. 상속 관계에서 첫줄에 super()를 생략하면 자바는 부모의 기본 생성자를 호출하는 super()를 자동으로 만들어 준다.