자바/기본

자바 - 상속

backend dev 2024. 3. 11.

상속

부모 클래스(상위 클래스)와 자식 클래스(하위 클래스)가 있고

자식 클래스는 부모 클래스를 선택해서, 그 부모의 멤버를 상속받아 쓸 수 있는것이 상속이다.

 

상속을 하는 이유는 이미 만들어진 클래스를 재사용해서 새로운 클래스를 만들기 위함이다.

효율적이고, 개발시간을 줄여준다.

 

 

상속받은 모든 필드,메소드를 자식클래스에서 사용할 수 있는것은 아니다.

- 부모클래스의 private 접근제한자를 가지는 필드 및 메소드는 자식이 물려받을수없다.

- 부모와 자식클래스가 서로 다른 패키지에 있다면 부모의 default 접근제한자를 가지는 필드와 메소드에 접근할 수 없다.

- 그 이외의 접근제한자를 가지는 필드와 메소드는 모두 상속의 대상이 된다.

 

 

부모 생성자의 호출 - super(...);

자바에서 자식 객체를 생성하면 , 부모 객체를 먼저 생성한 후 자식 객체가 생성됩니다.

public class Parent {
    int pVar;

    Parent() {
        System.out.println("부모클래스 생성자 실행");
    }
}

class Child extends Parent{

    Child() {
        System.out.println("자식클래스 생성자 실행");
    }

}

class Test {

    public static void main(String[] args) {
        Child child = new Child();
    }

}

자식클래스 객체 생성시 부모클래스의 생성자가 실행되는것을 확인할 수 있다.

 

 

자바는 클래스의 생성자가 생략되어있을경우 컴파일러는 알아서 기본 생성자를 만든다.
자식클래스 생성자에 super()가 생략되었을경우 컴파일러는 알아서 첫줄에 super()를 생성해준다.

class Child extends Parent{

    Child() {
        //super()가 생략되어있으면 컴파일러가 실행시 생성해줌.
        System.out.println("자식클래스 생성자 실행");
    }

}

 

 

부모에게 기본 생성자는 없고, 매개 변수가 있는 명시적 생성자만 있다면 컴파일러는 기본생성자를 생성해주지않는다.

자식클래스의 생성자에서 반드시 첫줄에서 super(전달인자,전달인자.....)로 부모 생성자를 처리해줘야한다.

컴파일러는 super()로 기본생성자만 처리가능하기 때문이다.

public class Parent {
    int pVar;

    Parent(int pVar) {
        this.pVar = pVar;
        System.out.println("부모클래스 생성자 실행");
    }
}

class Child extends Parent{

    Child() {
        super(1000);// 부모클래스의 매개변수가 존재하는 생성자가 존재한다면 해당 생성자에 맞게 super(..)를 처리해줘야한다. 
        System.out.println("자식클래스 생성자 실행");
    }

}

 

다형성

하나의 객체가 여러가지 타입의 객체를 가질 수 있는것이 다형성이다.

public class Parent {
    int pVar;
}

class Child extends Parent{
    int childVar;
    void childMet() {
        System.out.println("자식 메서드");
    }
}

class Test {

    public static void main(String[] args) {
        Parent parent = new Child();
    }

}

Parent 참조변수가 Child 인스턴스를 받을 수 있는것이 다형성이다.

 

유의할점

 

    public static void main(String[] args) {
        Parent parent = new Child();
//        parent.childMet();
//        parent.childVar = 10;
    }

하지만 Parent 참조변수는 자신의 클래스에 있는 필드와 메소드만 사용할 수 있으며

자식클래스의 필드와 메소드는 사용 불가능하다. [Child 인스턴스가 자동으로 Parent로 형변환 되기 때문이다.]

 

하지만 오버라이드 된 메서드는 사용가능하다.

public class Cpu {
    Cpu cpu; // 필드의 다형성

    void overrideMe() {
        System.out.println("오버라이드 전");
    }

}

class Intel extends Cpu {
    @Override
    void overrideMe() {
        System.out.println("오버라이드 후");
    }
}


class test33 {

    public static void main(String[] args) {
        Cpu cpu = new Intel();
        cpu.overrideMe();
    }

}

오버라이드 된 메소드는 실행가능한 모습

강제 형변환(casting)

public static void main(String[] args) {
    Parent parent = new Child();

    Child ptoC = (Child) parent; //자식클래스의 필드나 메소드를 사용하고 싶다면 자식클래스로 강제형변환을 한다.

    ptoC.childVar = 100;
    ptoC.childMet();
}
자식클래스 참조변수 = (자식클래스) 부모클래스 인스턴스;

자식 타입이 부모 타입으로 자동 변환하면, 

부모 타입에 선언된 필드와 메소드만 사용 가능하다는 제약사항이 따르게 되는데 

 

만약 자식 타입에 선언된 필드와 메소드를 꼭 사용해야 한다면 강제 타입 변환을 사용하면 됩니다.

 

다형성 사용 - 필드,매개변수의 다형성

public class Cpu {
    Cpu cpu; // 필드의 다형성

    Cpu(Cpu cpu) { // 매개변수의 다형성
        this.cpu = cpu;
    }

    void setCpu(Cpu cpu) { // 매개변수의 다형성
        this.cpu = cpu;
    }
}

class Intel extends Cpu {
    Intel() {
        super(new Intel());
    }
}

class Amd extends Cpu {

    Amd() {
        super(new Amd());
    }
}

class test33 {

    public static void main(String[] args) {
        Cpu cpu = new Cpu(new Intel());
        cpu.setCpu(new Amd());
    }

}

필드,매개변수의 타입을 부모타입으로 설정하여 모든 자식클래스의 타입들을 받을 수 있게 설계하였다.

코드의 수정이 필요하다면 자식클래스의 객체만 바꿔주면되서 코드의 수정이 간결해진다.

 

 

댓글