본문 바로가기

Java 기본 문법 - 참조 서적 [이것이 자바다 - 한빛미디어]/5. 상속, 다형성

1. Java 자바 - 상속 inheritance

상속(inheritance) : 부모가 자식에게 물려줌, 자식은 상속으로 부모가 물려준 것을 자연스럽게 이용할 수 있다.

 

객체 지향 프로그래밍에서도 부모 클래스의 멤버를 자식 클래스에게 물려줄 수 있다.

 

부모 클래스(상위 클래스), 자식 클래스(하위 클래스, 파생 클래스)

 

상속 관계

 

상속은 이미 잘 개발된 클래스를 재사용해서 새로운 클래스를 만든다. 따라서 코드의 중복을 줄여준다.

 

field1, field2, method1( ), method2( ) 를 가진 클래스가 있다면 모두 처음부터 작성하는 것보다

 

field1, method1( ) 을 가지고 있는 클래스가 있다면, 이 클래스를 상속하고 field2, method2( ) 만 추가하면

효율적이고 개발 시간을 절약시켜준다.

 

 

실제로 B 클래스를 객체 생성해서 아래와 같이 사용 시

마치 B가 field1, method1( ) 을 가지고 있는 것 처럼 보인다.

 

B b = new B();
b.field1 = 10;      // A 로 부터 물려받은 필드
b.method1();        // A 로 부터 물려받은 메소드

b.field2 = "홍길동";    // B 가 추가한 필드
method2( );             // B 가 추가한 메소드

 

상속을 해도 클래스의 모든 필드와 메소드를 물려받는 것은 아니다.

 

부모 클래스에서 private 접근 제한을 갖는 필드와 메소드는 상속의 대상에서 제외된다.

 

또한 부모 클래스와 자식 클래스가 다른 패키지에 존재한다면

default 접근 제한을 갖는 필드와 메소드도 상속 대상에서 제외된다. (서로 위치한 패키지가 다름)

 

그 외의 경우는 모두 상속의 대상이 된다.

 

상속을 이용하면 클래스의 수정을 최소화시킬 수 있다.

 

부모 클래스의 수정으로 모든 자식 클래스들의 수정 효과를 가져오기 때문에 유지 보수 시간을 최소화시켜 준다.

 

 

 


 

1. 클래스 상속

 

현실에서는 부모가 자식을 선택해서 물려주지만, 프로그램에서는 자식이 부모를 선택한다!!

 

자식 클래스를 선언할 때 어떤 부모 클래스를 상속받을 것인지 결정하고,

부모 클래스는 아래와 같이 extends 뒤에 기술된다.

 

class 자식클래스 extends 부모클래스 {
    // 필드
    // 생성자
    // 메소드
}

ex) Car 클래스를 상속해서 SportsCar 클래스를 만들 때

class SportsCar extends Car {
    . . . 
}

 

다른 언어와 달리 자바는 다중 상속을 허용하지 않는다.

여러 개의 부모 클래스를 상속할 수 없다.

 

extends 뒤에는 단 하나의 부모 클래스만 와야 한다.

 

class 자식클래스 extends 부모클래스1, 부모클래스2 {  // 에러!!
}

 

ex) CellPhone 클래스(부모)를 상속, DmbCellPhone 클래스(자식) 작성

 

CellPhone.java (부모 클래스)

public class CellPhone {
    //필드
    String model;
    String color;
    
    //생성자
    
    //메소드
    void powerOn() { System.out.println("전원을 켭니다."); }
    void powerOff() { System.out.println("전원을 끕니다."); }
    void bell() { System.out.println("벨이 울립니다."); }
    void sendVoice(String message) { System.out.println("자기 : "+ message); }
    void receiveVoice(String message) { System.out.println("상대방 : "+ message); }
    void hangUp() { System.out.println("전화를 끊습니다."); }
}

 

DmbCellPhone.java (자식 클래스)

public class DmbCellPhone extends CellPhone {
    //필드
    int channel;
    
    //생성자
    DmbCellPhone(String model, String color, int channel) {
        this.model = model;   // CellPhone 에게 받은 필드
        this.color = color;   // CellPhone 에게 받은 필드
        this.channel = channel;
    }
    
    //메소드
    void turnOnDmb() {
        System.out.println("채널 "+ channel + "번 DMB 방송 수신을 시작합니다.");
    }
    
    void changeChannelDmb(int channel) {
        this.channel = channel;
        System.out.println("채널 "+ channel +"번으로 바꿉니다.");
    }
    
    void turnOffDmb() {
        System.out.println("DMB 방송 수신을 멈춥니다.");
    }
}

 

DmbCellPhoneExample.java (자식 클래스 사용)

public class DmbCellPhoneExample {
    public static void main(String[] args) {
        //DmbCellPhone 객체 생성
        DmbCellPhone dmbCellPhone = new DmbCellPhone("자바폰", "검정", 10);
        
        //CellPhone 으로부터 상속 받은 필드
        System.out.println("모델 : "+ dmbCellPhone.model);
        System.out.println("색상 : "+ dmbCellPhone.color);
        
        //DmbCellPhone의 필드
        System.out.println("채널 : "+ dmbCellPhone.channel);
        
        //CellPhone으로부터 상속받은 메소드 호출
        dmbCellPhone.powerOn();
        dmbCellPhone.bell();
        dmbCellPhone.sendVoice("여보세요");
        dmbCellPhone.receiveVoice("안녕하세요!");
        dmbCellPhone.sendVoice("반갑습니다.");
        dmbCellPhone.hangUp();
        
        //DmbCellPhone의 메소드 호출
        dmbCellPhone.turnOnDmb();
        dmbCellPhone.changeChannleDmb(12);
        dmbCellPhone.turnOffDmb();
    }
}

 

 


 

2. 부모 생성자 호출

 

자식 객체를 생성하면, 부모 객체가 먼저 생성되고 자식 객체가 그 다음에 생성된다.

 

아래 코드는 내부적으로 부모인 CellPhone 객체가 먼저 생성되고,

DmbCellPhone 객체가 생성된다.

 

DmbCellPhone dmbCellPhone = new DmbCellPhone();

 

메모리 모습

 

 

모든 객체는 클래스의 생성자를 호출해야만 생성된다. (부모 객체도 예외는 아니다.)

 

위의 코드에서 부모 객체가 먼저 생성되는데,

부모 객체를 생성하기 위해 부모 생성자를 어디서 호출한 것일까??

 

자식 생성자에 숨겨져 있다. 부모 생성자는 자식 생성자의 맨 첫 줄에서 호출된다.

 

ex) DmbCellPhone(자식클래스) 의 생성자가 명시적으로 선언되지 않았다면,

컴파일러는 아래와 같은 기본 생성자를 생성한다.

 

public DmbCellPhone() {
    super();
}

 

첫 줄의 super( ); 가 추가된다.

super( ) 는 부모의 기본 생성자를 호출한다.

즉, CellPhone 클래스(부모 클래스)의 생성자를 호출한다.

 

public CellPhone() {
}

 

CellPhone.java 소스 코드 에서도 CellPhone 의 생성자가 선언되어 있지 않았지만,

 

컴파일러에 의해 기본 생성자가 만들어지므로 문제 없이 실행된다.

 

만약, 직접 자식 생성자를 선언하고, 명시적으로 부모 생성자를 호출하고 싶을 때는 아래와 같이 작성한다.

 

자식클래스( 매개변수선언, . . . ) {
    super( 매개값, . . . );
    . . . 
}

 

super(매개값, . . . ) 는 매개값의 타입과 일치하는 부모 생성자를 호출한다.

 

만약 매개값의 타입과 일치하는 부모 생성자가 없을 경우 컴파일 오류가 발생한다.

 

super(매개값, . . . ) 가 생략되면, 컴파일러에 의해 super( ) 가 자동으로 추가되기 때문에

부모의 기본 생성자가 존재해야 한다.

 

부모 클래스에 기본 생성자가 없고 매개 변수가 있는 생성자만 있다면,

자식 생성자에서 반드시 부모 생성자 호출을 위해 super(매개값, . . . ) 를 명시적으로 호출해야 한다.

 

super(매개값, . . . ) 는 반드시 자식 생성자 첫 줄에 위치해야 한다. (그렇지 않으면 컴파일 에러!)

 

ex)

People.java : 부모 클래스

public class People {
    public String name;
    public String ssn;
    
    // 부모 클래스 생성자 명시 (기본 생성자 X)
    public People(String name, String ssn) {
        this.name = name;
        this.ssn = ssn;
    }
}

 

생성자가 명시되어 있기 때문에,

상속하는 Student 클래스는 생성자에서 super(name, ssn) 으로 People 클래스의 생성자를 호출해야 한다.

 

Student.java : 자식 클래스

public clas Student extends People {
    public int studentNo;
    
    public Student(String name, String ssn, int studentNo) {
        super(name, ssn);   // 부모 생성자 호출
        this.studentNo = studentNo;
    }
}

 

Student 클래스 생성자는 name, ssn, studentNo 를 매개값으로 받아서

name, ssn은 부모 생성자를 호출하기 위해 매개값으로 넘겨준다.

 

super(name, ssn) 은 People 클래스(부모)의 생성자인 People(String name, String ssn)을 호출한다.

 

super(name, ssn) 구문이 없다면,

"Implicit super constructor People( ) is undefined. Must explicitly invoke another constructor"

컴파일 오류가 발생한다.

(부모 클래스의 기본 생성자가 없으니, 다른 생성자를 명시적으로 호출해야 한다.)

 

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);
    }
}