티스토리 뷰

Spring/Java

[Java] 상속 / 상속과 생성자

DevJunku 2021. 12. 3. 18:54

⩤ 상속

객체 지향을 통해 달성하고자 하는 목표 중에서 가장 중요한 것은 당연히 기능을 재활용하는 것이다. 상속은 객체 지향의 재활용성을 극대화시킨 프로그래밍 기법이라고 할 수 있다. 또한 동시에 객체 지향을 복잡하게 하는 주요 원인이라고 할 수 있다.

상속: 물려준다.

어떤 객체가 있을 때 해당 객체가 갖고 있는 필드와 메서드를 다른 객체가 물려받을 수 있도록 한 것이 바로 상속이라고 한다.

기존의 객체를 유지하면서, 기능을 추가할 수 있는 방법이 바로 상속임

class C {
    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 substractableCalculator extends C {
    public void substract() {
        System.out.println(this.left - this.right);
    }
}

public class ScopeDemo {
    public static void main(String[] args) {
        substractableCalculator s = new substractableCalculator();
        s.setOprands(10, 20);
        s.sum();
        s.avg();
        s.substract();
    }
}

스크린샷 2021-12-03 오후 3.11.10.png

클래스 다이어그램으로 이야기 한다고 한다.

s → 상속을 의미

c → 클래스를 의미

substractableCalculator라는 자식 클래스를 만든 것인데, 여기서 중요한 것은 extends를 사용한 뒤 상속할 클래스를 명시해야 한다. 그리고 만약에 위 코드를 코틀린으로 바꾸면 다음과 같다.

fun main(args: Array<String>) {
    val c = substractableCalculator()
    c.setOprands(10, 20)
    c.sum()
    c.avg()
    c.substract()
}

open class C { // 코틀린에서는 상속을 하기 위한 class에 open을 붙여줘야함.
    var left: Int = 0
    var right: Int = 0

    fun setOprands(left: Int, right:Int) {
        this.left = left;
        this.right = right;
    }

    fun sum() {
        println(this.left + this.right)
    }

    fun avg() {
        println((this.left + this.right) / 2)
    }
}

class substractableCalculator: C() { // 자식 클래스가 상속받기 위해서는 ':'로 구분함. 
    fun substract() {
        println(this.left - this.right)
    }
}

보면 알겠지만, 문법은 매우 많이 동일하다. 차이점이 있다면, 굳이 메인 클래스를 만들지 않아도 main함수만 있다면 알아서 실행시킬 수 있다. 하나더 상속해보자.

class MultiplicationableCalculator extends C {
    public void multiplication() {
        System.out.println(this.left * this.right);
    }
}

public class ScopeDemo {
    public static void main(String[] args) {
        MultiplicationableCalculator m = new MultiplicationableCalculator();
        m.setOprands(10, 20);
        m.multiplication();
    }
}
class DivisionableCalculator extends MultiplicationableCalculator {
    public void division() {
        System.out.println(this.left / this.right);
    }
}

public class ScopeDemo {
    public static void main(String[] args) {
        DivisionableCalculator d = new DivisionableCalculator();
        d.setOprands(10, 20);
                d.multiplication();
        d.division();
    }
}

위 2개의 코드를 보면 간접적으로든 직접적으로든 class C의 기능을 모두 상속받기 때문에, 상속한 뒤에 편리하게 추가하고 싶은 기능을 추가할 수 있다.

즉, 부모가 만든 기능을 자식이 또 만들 필요는 없는 것이다.

⩤ 상속과 생성자

생성자는 객체를 의미한다. 그 과정에서 해야 할 일들을 생성자 메소드에 지정해서 초기화 작업을 진행할 수 있다. 이번 수업을 이해하기 위해서는 기본 생성자의 성질에 대한 이해가 선행되어야 한다. (무지 중요하다는 의미)

다음 코드를 보면 좀 이상하다는 생각을 할 수 있다.

// 1
public class ContructorDemo {
    public static void main(String[] args) {
        ContructorDemo c = new ContructorDemo();
    }
}

그렇다 자기 자신을 사용하고 있는 것이다. 근데 에러는 나지 않는다. 아무런 문제가 없다.

// 2
public class ContructorDemo {
        public ContructorDemo(int params) {}
    public static void main(String[] args) {
        ContructorDemo c = new ContructorDemo();
    }
}

다음 코드는 에러를 발생시킨다.

ContructorDemo클래스는 params라는 인자를 받으려고 한다. 이때 에러가 발생한다. 1번 코드의 경우 ContructorDemo는 자바가 기본생성자로 만들어주기 때문에 ContructorDemo c = new ContructorDemo();를 작성해도 아무런 문제가 발생하지 않는다. 하지만 2번 코드는 그렇지 않다. 자바에서는 ContructorDemo를로 다시 클래스를 할당했기 때문에 기본 생성자로 만들어주지 않는다. 따라서 2번 코드는 에러를 발생시킨다. 이 문제는 처음에 기본 생성자를 생성하지 않아서 생기는 문제이므로 생성자를 생성하면 문제는 해결된다. 다음과 같은 식으로 코드를 작성하면 에러는 사라진다.

스크린샷 2021-12-03 오후 4.15.56.png

결국에는 자바는 new ContructorDemo()public ContructorDemo(int params) {}가 아닌 public ContructorDemo() {}를 보고 생성자를 생성한다는 의미이다.

그렇다면, 생성자를 활용하여 초기화를 따로 시켜주지 않으려면, 어떻게 해야할까? 다음과 같이 코드를 작성하면 된다.


class SubstractableCalculator extends C {
    public SubstractableCalculator(int left, int right) { // 이코드를 통해 생성자 초기화를 안할 수 있다.
        this.left = left;
        this.right = right;
    }

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

public class ContructorDemo {
    public static void main(String[] args) {
        SubstractableCalculator s = new SubstractableCalculator(10, 20); // 다음과 같은 코드를 작성하면된다.
        s.sum();
        s.avg();
        s.substract();
    }
}

이때 부모 클래스에 기본 생성자가 생성되지 않았다면, 오류가 발생한다. 따라서 다음과 같이 코드를 작성하여 오류를 방지할 수 있다

class C {
    int left, right;

    public C(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 SubstractableCalculator extends C {
    public SubstractableCalculator(int left, int right) {
        super(left, right); // 얘가 받는다.
    }

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

public class ContructorDemo {
    public static void main(String[] args) {
        SubstractableCalculator s = new SubstractableCalculator(10, 20);
        s.sum();
        s.avg();
        s.substract();
    }
}

super class를 잊지말자. 상위 클래스 초기화 하기 전에 뭘할건지 알려주는 것이다. 그리고 또 하나 중요한 것은 super 이후에 초기화 작업을 진행해야 한다.

'Spring > Java' 카테고리의 다른 글

[Java] Overriding vs Overloading  (0) 2021.12.06
[Java] 초기화와 생성자  (0) 2021.12.02
[Java] 유효범위  (0) 2021.12.02
[Java] 클래스 맴버와 인스턴스 맴버  (0) 2021.12.02
[Java] 클래스와 인스턴스 그리고 객체  (0) 2021.12.02