상속과 생성자

muz·2021년 4월 12일
0

Java

목록 보기
9/21
post-thumbnail

💎 상속

상속은 객체지향의 재활용성을 극대화시킨 기법이자, 객체지향을 복잡하게 하는 주원인이다. 상속(Inheritance)는 어떤 객체가 있을 때 그 객체의 필드(변수)와 메소드를 다른 객체가 물려받을 수 있는 기능을 말한다.

다음의 Calculator 코드에 substract 기능을 추가하고 싶다고 가정해보자.

class Calculator{
    int left, right;
      
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    }
      
    public void sum(){
        System.out.println(this.left+this.right);
    }
      
    public void avg(){
        System.out.println((this.left+this.right)/2);
    }
}
  
public class CalculatorDemo4 {
      
    public static void main(String[] args) {
          
        Calculator c1 = new Calculator();
        c1.setOprands(10, 20);
        c1.sum();       
        c1.avg();       
          
        Calculator c2 = new Calculator();
        c2.setOprands(20, 40);
        c2.sum();       
        c2.avg();
    }
  
}

코드에 직접 기능 추가하기

위의 코드에 substract()를 추가하고 싶다면, substract 메소드를 추가하여 호출하면 된다. 그러나 다음의 2가지 상황에 속한다면 객체에 메소드를 추가하는 것이 어렵다.
1. 자신이 객체를 만들지 않음
→ 소스 변경 불가 / 변경 가능해도 원 소스 업데이트 시 substarct 메소드가 사라짐
2. 메소드를 추가하면 다른 곳에서는 불필요한 기능이 포함될 수 있음
→ 몰라도 되는 것 까지 알아야 함

객체를 그대로 유지하면서 기능을 추가하는 방법

기존 객체를 수정하지 않으면서 새로운 객체가 기존의 객체를 기반으로 생성되게끔 하는 것이 상속이다. 기존 객체가 기능을 물려주기에 '부모 객체'가 되고, 새로운 객체는 기존 객체의 기능을 물려받으므로 '자식 객체'가 된다.

  • 부모 클래스 - 자식 클래스
  • 상위(super) 클래스 - 하위(sub) 클래스
  • 기초(base) 클래스 - 유도(derived) 클래스
class calculator2 {
    int left, right;
 
    public void setOprands(int left, int right) {
        this.left = left;
        this.right = right;
    }
 
    public void sum() { // 합계를 구하는 메소드
        System.out.println(this.left + this.right);
    }
 
    public void avg() { // 평균을 구하는 메소드
        System.out.println((this.left + this.right) / 2);
    }
}
 
class SubstractionableCalculator extends calculator2 {
    public void substract() {
        System.out.println(this.left - this.right);
    }
}
 
public class Inheritance2 {
 
    public static void main(String[] args) {
 
        SubstractionableCalculator c1 = new SubstractionableCalculator();
        c1.setOprands(10, 20);
        c1.sum();
        c1.avg();
        c1.substract();
    }
 
}

위의 코드가 전체 코드이다. 분해해서 생각해보자.

class SubstractionableCalculator extends calculator2 {
    public void substract() {
        System.out.println(this.left - this.right);
    }
}

새로운 SubstractionableCalculator 클래스를 정의했다. 이 클래스에는 substract 메소드만 존재한다. 그렇다면 어떻게 c1.substract()로 호출이 가능한 것일까? 바로 extends calculator2 덕분이다. 이는 클래스 calculator2를 상속 받는다는 의미로, SubstractableCalculator는 Calculator에서 정의한 setOprands, sub, avg를 사용할 수 있게 된다.

  • 상속의 이점
  1. 코드의 중복을 제거할 수 있게해줌
  2. 부모 클래스를 개선하면 상속받는 모든 자식 클래스에게도 적용됨
    ⇒ 즉, 유지보수가 쉬워짐

곱셈 기능 추가하기

위의 코드에 곱셈 기능까지 더해주고 싶다면 어떻게 하면 될까? SubstractableCalculator처럼 클래스를 정의할 때 extends를 이용하고, main()에서 객체 생성 시 생성한 MultiplecationCalculator 클래스를 이용하면 된다.

class MultiplecationCalculator extends Calculator {
	public void multiplecation() {
    	System.out.println(this.left*this.right);
    }
}

상속한 클래스 다시 상속하기?

상속한 클래스를 다시 상속하는 것도 가능하다. 위에서 생성한 MultiplecationCalculator 클래스를 상속받는 클래스를 만들어보자.
1. MultiplecationCalculator를 상속받는 DivisionCalculator클래스 생성하기

class DivisionCalculator extends MultiplecationCalculator {
	public void division() {
    	System.out.println(this.left / this.right);
    }
}
  1. main()에서 DivisionCalculator 클래스를 이용하여 객체 생성 및 메소드 호출하기
public static void main(String[] args) {
	DivisionCalculator c1 = new DivisionCalculator();
    c1.setOprands(10,20);
    c1.sum();
    c1.avg();
    c1.multiplication();
    c1.division();
}

💎 상속과 생성자

생성자가 상속을 만나면서 발생하는 복잡성

public class Constructor {
	public Constructor(int p) {
    	public static void main(String[] args) {
        	Constructor c = new Constructor();
        }
    }
}

위의 코드를 실행하면 에러가 발생한다. 상위 클래스에 매개변수가 있는 생성자가 있다면, 자바는 상위 클래스의 기본 생성자를 만들어주지 않는다. 때문에 기본 생성자를 직접 추가해주어야한다.

public class Constructor {
	public Constructor() {} // 기본 생성자 추가 
	public Constructor(int p) {
    	public static void main(String[] args) {
        	Constructor c = new Constructor();
        }
    }
}

생성자로 매개변수 값 설정하기

SubstractableCalculator의 생성자로 left와 right 값을 받아서 초기화시키고, setOprands가 아닌 생성자를 통해 left와 right값을 설정해보자.


class Calculator3 {
    int left, right;
     
    public Calculator3() {} // 상위 클래스에 기본 생성자 추가하기

    public Calculator3(int left, int right){
        this.left = left;
        this.right = right;
    }
     
    public void setOprands(int left, int right) {
        this.left = left;
        this.right = right;
    }
 
    public void sum() {
        System.out.println(this.left + this.right);
    }
 
    public void avg() {
        System.out.println((this.left + this.right) / 2);
    }
}
 
class SubstractionableCalculator2 extends Calculator3 {
    public SubstractionableCalculator2(int left, int right) {
        this.left = left;
        this.right = right;
    }
 
    public void substract() {
        System.out.println(this.left - this.right);
    }
}
 
public class Inheritance3 {
    public static void main(String[] args) {
        SubstractionableCalculator2 c1 = new SubstractionableCalculator2(10, 20);
        c1.sum();
        c1.avg();
        c1.substract();
    }
}

상위 클래스인 Calculator3에는 left와 right 값을 초기화할 수 있는 생성자가 이미 존재한다. 상위 클래스의 생성자를 호출해보자.

super : 상위 클래스를 가리키는 키워드

super를 사용하여 상위 클래스의 생성자를 호출하는 방법은 간단하다.

class SubstractionableCalculator2 extends Calculator3 {
    public SubstractionableCalculator2(int left, int right) {
        super(left, right); // 상위 클래스의 생성자 호출하기
    }
 
    public void substract() {
        System.out.println(this.left - this.right);
    }
}

이 Calculator3을 부모 클래스로 갖는 SubstractionableCalculator2클래스 내에서, this.left = left; this.right = right;super(left, right);로 바꾸워주면 된다.

super 키워드는 부모 클래스를 뜻한다. super()는 부모 클래스의 생성자를 의미한다. 이렇게 하면 부모 클래스의 기본 생성자 public Calculator3() {} 가 없어져도 오류가 발생하지 않는다.

단, 하위 클래스의 생성자에서 super 사용 시, super가 가장 먼저 나타나야 함.


Reference

profile
Life is what i make up it 💨

0개의 댓글