자바의 상속에 대해 학습하세요.
학습할 것
자바 상속의 특징
super 키워드
메소드 오버라이딩
다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
추상 클래스
final 키워드
Object 클래스
상속은 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다.
상속을 통해서 클래스를 작성하면 적은 양의 코드로 새로운 클래스를 작성할 수 있고 코
드를 공통적으로 관리할 수 있기 때문에 코드의 추가 및 변경이 매우 용이하다.
class Child extends Parent {
// ...
}
//조상 클래스 : 부모(parent)클래스, 상위(super)클래스, 기반(base)클래스
//자손 클래스 : 자식(child)클래스, 하위(sub)클래스, 파생된(derived)클래
객체지향에서의 상속은 inheritance(상속)가 아닌 extends (재사용과 확장)로 이해하는 것이 좋다.
VS
자바에는 자식 클래스가 부모 클래스의 기능을 그대로 물려받을 수 있는 상속(Inheritance) 기능이 있다.
자바는 다중 상속을 지원하지 않는다. (extends) 뒤에는 하나의 부모 클래스만 올 수 있다.
상속의 횟수 제한이 없다.
자바에서의 최상위 계층 부모 클래스는 java.lang.Object이다. 즉 Object 클래스는 super class를 가지지 않는다
상속을 할때 상위 클래스는 물려줄 특성이 풍부하면 좋고 반대로 인터페이스는 구현을 강제할 메서드의 개수가 적을 수록 좋다.
객체지향에서 상속은 상위 클래스의 특성을 하위 클래스에서 상속하고 필요한것을 부가하여 확장해서 사용할 수 있다고 볼 수 있다.
자신이 상속받은 부모를 가리키는 참조 변수 super
자식 클래스는 부모클래스를 상속받았기 때문에 자유롭게 부모의 모든 property를 사용할 수 있다. 하지만 그럼에도 자식과 부모사이의 구분이 있어야한다. 자식클래스가 부모클래스의 property 와 동일한 이름을 갖고 있다면 그것을 부모로부터 구분해 낼 수 있어야한다는 것.
// 예제 1
class Object{
int a;
}
class A extends Object{
int a;
}
public static void main(String args[]){
A ins = new A();
ins.a=2 // 여기서 a는 A의 a. 즉, 자식의 변수이다
// 만약 자식에게 a라는 변수가 없었다면 부모의 a를 가리켰을 것이다.
// 여기서 자식 a가 아닌 부모의 a로 접근하고 싶다면?
ins.super.a = 20; // 이렇게 super라는 참조 변수를 사용해서 부모의 a에 접근할 수 있다
}
// 위와 같은 이유로 자바에서는 다중 상속이 불가능하다 (상속의 모호성)
자바에서 객체를 생성하면, 부모 객체를 먼저 생성한 후, 자식 객체가 생성된다.
객체는 생성자를 호출해야만 생성이 되는데 만약에 생성자 선언이 없다면 컴파일러는 알아서 기본 생성자를 생성하여 호출한다.
이때 부모 클래스의 생성자 선언은, 자식 클래스의 생성자 선언 내부에 맨 첫줄에 super() 라고 생성된다.
super() 는 부모 클래스의 기본 생성자를 컴파일러가 호출하느 것 이라고 볼 수 있다.
super 키워드는 부모 클래스(객체) 를 나타내는 것을 기억하자
super는 자식 클래스가 부모클래스로부터 상속받은 멤버를 참조할 때 사용한 참조 변수 이다.
하지만 부모에게 기본 생성자는 없고, 매개 변수가 있는 명시적 생성자만 있다면
자식 클래스에서는 반드시 생성자 내부 첫줄에 super( 매개값 , 매개값, …); 과 같이 선언해 주어야 한다.
public class ParentBook{
String name; //필드
int price;
public ParentBook (String name, int price){ // 부모의 생성자가 있는 경우
this.name = name;
this.price = price;
}
public void Print(){ // 메소드
System.out.println("책의 이름과 가격 : "+name+" "+price);
}
public class ChildBook extends ParentBook{
ChildBook (){ // 자식 생성자
super("나의 라임오렌지 나무", 10000); // 반드시 자식 생성자 첫줄에 선언
}
public static void main (String[] args){
ChildBook Child = new ChildBook();
System.out.print("[구현 결과 2] : ");
Child.Print();
}
//책의 이름과 가격 : 나의 라임오렌지 나무 10000
자바는 상속된 메소드 중 일부를 자식 클래스에서 다시 수정하여 사용할 수 있게 해준다.
이 기능을 메소드 오버라이딩 이라고 한다.
하지만 메소드 오버라이딩을 하기 위해서는 몇 가지 규칙이 있다.
재정의할 부모의 메소드와 동일한 메소드명, 리턴타입, 매개변수 리스트로 작성 해야함.
메소드의 접근 제한자는 부모의 것보다 더 제한이 강한 것을 사용할 수 없음.
(부모 public 자식 private x)
새로운 Exception 을 thorws x
public Class Parent {
int CalCul(int num){ // 부모의 메소드
return num * 3;
}
}
public Class Child extends Parent {
@Override
int CalCul(int num) { // 메소드 재정의
System.out.print("결과는 "+num+" 입니다.");
}
public static void main(String[] args) {
Child ch = new Child();
ch.CalCul(5); //오버라이딩된 자식 객체의 메소드 CalCul이 호출됨
}
}
// 5
@Override 어노테이션은 컴파일러에게 메소드가 정확하게 오버라이딩 되었는지 확인을 요청하는 것으로, 생략되도 상관은 없으나 개발자의 실수를 체크해 주므로 프로그램 안정성에 도움을 준다.
어떤 메서드를 호출할지 결정하여 실제로 실행시키는 과정
컴파일 시점에선 어떤 메소드를 호출하는지 모른다.
추상 타입의 메소드를 호출하는것만 알고 있다.
런타임 시점에 할당된 객체의 타입을 보고 메소드를 실행함.
런타임 시점에서 컴파일러가 알게 된다
public class Dispatch{
static abstract class Service{
abstract void run();
}
//자식 클래스는 재 구현을 강요받음.
static class ServiceTest extends Service{
@Override
void run() {
System.out.println("ServiceTest run");
}
}
public static void main(String[] args) {
Service svc = new ServiceTest();
svc.run();
}
}
컴파일 시점에서, 컴파일러가 특정 메소드를 호출할 것이라고 명확하게 알고 있는 경우
class SuperClass{
void printImp(){
System.out.println("SuperClass printImp호출");
}
}
class Main{
public static void main(String[] args){
new SuperClass.printImp();
}
}
메소드 오버로딩의 경우 static dispatch를 사용한다.)
클래스가 설계도라고 하면 추상 클래스는 미완성 설계도 라고 볼 수 있다.
미완성이란 뜻은 단지 미완성메서드(추상메서드) 를 포함하고 있다는 의미.
미완성 설계도로 제품을 만들 수 없듯이 추상클래스로 인스턴스는 생성할 수 없다.
추상클래스는 상속을 통해서 자손클래스에 의해서만 완성될 수 있다.
추상클래스 자체로는 클래스로서의 역할을 못하지만, 새로운 클래스를 작성하는데 바탕이 되는 조상클래스로서 중요한 의미를 가진다.
추상클래스는 키워드 ‘abstract’를 붙이기만 하면 됨
abstract class 클래스이름 {
...
}
클래스 선언부의 abstract을 보고 이 클래스에는 추상메서드가 있으니 상속을 통해 구현해야 한다는 것을 쉽게 알 수 있다.
추상클래스는 추상메서드를 포함하고 있다는 것을 제외하고 일반 클래스와 동일하므로, 생성자가 있고, 멤버변수와 메서드도 가질 수 있다.
만일 추상메서드로 정의되지 않고 빈 몸통만 가지고 있다면 상속받는 자손클래스에서는 이 메서드가 온전히 구현된 것으로 인식하고 오버라이딩을 하지 않을수도 있다.
그렇기 때문에 추상메서드로 선언하여 자손클래스에게 내용을 구현해주어야 한다는 것을 알려주는 것이다.
클래스의 메서드를 추상메서드로 하는 대신, 아무 내용도 없는 메서드로 작성할 수도 있다.
자손클래스에서 오버라이딩하여 자신의 클래스에 맞게 구현하기 때문에 굳이 추상메서드를 사용할 필요가 없다고 생각할 수도 있다.
만일 추상메서드로 정의되지 않고 빈 몸통만 가지고 있다면 상속받는 자손클래스에서는 이 메서드가 온전히 구현된 것으로 인식하고 오버라이딩을 하지 않을수도 있다.
그렇기 때문에 추상메서드로 선언하여 자손클래스에게 내용을 구현해주어야 한다는 것을 알려주는 것이다.
https://velog.io/@shkim1199/JAVA-final
final은 ‘마지막의’ 또는 ‘변경될 수 없는’의 의미를 가지고 있으며 거의 모든 대상에 사용될 수 있다.
변수에 사용되면 값을 변경할 수 없는 상수가 되며, 메서드에 사용되면 오버라이딩을 할 수 없게 되고 클래스에 사용되면 자신을 확장하는 자손클래스를 정의하지 못하게 된다.
final class FinalTest { // 조상이 될 수 없는 클래스
final int MAX_SIZE = 10; // 값을 변경할 수 없는 멤버변수(상수)
final void getMaxSize() { // 오버라이딩할 수 없는 메서드(변경불가)
final int LV = MAX_SIZE; // 값을 변경할 수 없는 지역변수(상수)
return MAX_SIZE;
}
}
모든 클래스의 조상
Object클래스는 모든 클래스 상속계층도의 최상위에 있는 조상클래스이다. 다른 클래스로부터 상속 받지 않는 모든 클래스들은 자동적으로 Object클래스로부터 상속받게 함으로써 이것을 가능하게 한다.
https://yadon079.github.io/2020/java%20study%20halle/week-06