상속

고동현·2024년 6월 12일
0

JAVA

목록 보기
9/22
post-thumbnail

상속관계

상속을 하려면 extends를 사용한다.

  • 부모 클래스(슈퍼 클래스): 상속을 통해 자신의 필드와 메서드를 다른 클래스에 제공하는 클래스
  • 자식 클래스(서브 클래스): 부모 클래스로 부터 필드와 메서드를 상속받는 클래스
public class Car {
    public void move(){
        System.out.println("차를 이동합니다.");
    }
}
public class ElectricCar extends Car{
    public void charge(){
        System.out.println("충전합니다.");
    }
}
public class GasCar extends Car{
    public void fillUp(){
        System.out.println("기름을 주유합니다.");
    }
}
public class CarMain {
    public static void main(String[] args) {
        ElectricCar electricCar = new ElectricCar();
        electricCar.move();
        electricCar.charge();
        GasCar gasCar = new GasCar();
        gasCar.move();
        gasCar.fillUp();
    }
}

자식클래스는 부모의 필드와 메서드를 모두 물려받으므로 부모의 필드와 메서드를 사용할 수 있다.

자바는 다중상속을 지원하지 않는다. 하나의 클래스는 하나의 부모만 가질수있다. 물론, 그 부모가 또 부모를 가지는건 가능하다.

상속과 메모리 구조

  1. 인스턴스 생성시 자식 인스턴스가 생성되지만, 내부에 부모와 자식 인스턴스가 구분되어 둘다 생성된다.
  2. 메서드 호출시 부모와 자식이 둘다 있으므로, 호출하는 변수의 타입(클래스)부터 시작한다.
  3. 만약 해당 타입에 메서드가 존재하지 않는다면, 부모타입으로 올라가서 확인한다.
  4. 만약 거기에도 없으면 더 상위 부모로 올라가서 확인한다.

상속과 메서드 오버라이딩

오버라이딩: 부모로 부터 상속받은 기능을 자식이 재정의 하는것.

public class ElectricCar extends Car {
    public void charge(){
        System.out.println("충전합니다.");
    }

    @Override
    public void move(){
        System.out.println("전기차가 더 빠르게 움직입니다.");
    }
}

@Override
오버라이딩한 메서드 위에 이 애노테이션을 붙여야한다.
사실, 붙이지 않아도 오버라이딩이 되긴하는데, 오버라이딩 조건을 만족시키지 않으면 컴파일 에러가 발생하므로, 실수로 오버라이딩을 못하는 경우를 방지해준다.


굳이 @Override를 붙이지 않아도 본인타입에서 move()메서드를 찾기 때문에 오버라이딩한 move가 호출된다.
그러나, 만약 movee라고 내가 잘못 메서드를 정의해놓으면 부모타입으로 올라가서 move를 찾을 것이다. -> 오버라이딩 안됨
고로 @Override로 붙여서 명확성을 가지는게 좋다.

참고

  • Overriding: 하위클래스에서 상위 클래스의 메서드를 재정의하는것
  • Overloading: 메서드 이름이 같고 파라미터가 다른 메서드를 여러개 정의하는것.

오버라이딩 조건

  • 메서드이름이 같아야한다.
  • 메서드 파라미터타입,순서,갯수가 같아야한다.
  • 반환타입이 같아야한다.
  • 오버라이딩 메서드의 접근제어자는 상위클래스의 메서드보다 제한적일 수없다.
    if 상위가 protected라면 하위는 public protected가능 private불가
  • 오버라이딩 메서드는 상위클래스의 메서드보다 더 많은 체크 예외를 throw할수없다.
  • static,final,private키워드가 붙은 메서드는 오버라이딩 될 수 없다.
  • 생성자는 오버라이딩 할 수없다.

상속과 접근제어

현재 Parent와 Child의 패키지가 다르다.


public class Parent {
    public int publicValue;
    protected int protectedValue;
    int defaultValue;
    private int privateValue;
    public void publicMethod() {
        System.out.println("Parent.publicMethod");
    }
    protected void protectedMethod() {
        System.out.println("Parent.protectedMethod");
    }
    void defaultMethod() {
        System.out.println("Parent.defaultMethod");
    }
    private void privateMethod() {
        System.out.println("Parent.privateMethod");
    }
    public void printParent() {
        System.out.println("==Parent 메서드 안==");
        System.out.println("publicValue = " + publicValue);
        System.out.println("protectedValue = " + protectedValue);
        System.out.println("defaultValue = " + defaultValue); //부모 메서드 안에서 접근 가능
        System.out.println("privateValue = " + privateValue); //부모 메서드 안에서 접근 가능
        //부모 메서드 안에서 모두 접근 가능
        defaultMethod();
        privateMethod();
    }
}

public class Child extends Parent {
    public void call(){
        publicValue = 1;
        protectedValue = 1;
        //defaultValue = 1 //다른패키지 접근 불가
        //privateValue = 1; // private은 클래스 내부에서만 사용가능

        publicMethod();
        protectedMethod();
        //defaultMethod(); //protected는 다른패키지 접근불가
        //privateMethod(); //private는 클래스 내부에서만 사용가능

        printParent(); //public 메서드 접근가능, 해당 메서드 내부에서 private protected메서드 부를 수 있음, 자기 클래스 내부라서
    }
}
  • Public: 어디에서나 접근가능
  • protected(상속관계, 같은패키지 접근가능): 현재 패키지가 다르나, 상속관계라서 접근가능
  • Default(같은패키지안에서 호출허용): 현재 패키지가 다르므로 호출 불가
  • private: 클래스 내부에서만 접근 허용

printParent()메서드는 public메서드라 접근가능, 해당 메서드 내부에서 private,protected메서드를 호출하는데, 이건 클래스 내부에서 호출하는거라 접근가능

super

super를 사용하는 이유가 뭘까?
super를 사용하면 부모의 필드와 메서드에 접근이가능하다.

public class Parent {
    public String value = "parent";

    public void hello(){
        System.out.println("Parent.hello");
    }
}
public class Child extends Parent{
    public String value = "child";

    @Override
    public void hello(){
        System.out.println("Child.hello");
    }

    public void call(){
        System.out.println("this value = "+ this.value);
        System.out.println("super value = "+ super.value);

        this.hello();
        super.hello();
    }
}

항상 호출한 클래스의 타입부터 시작한다고 했으므로, 부모의 hello를 호출하고 싶어도.
child.hello()를 하면 Child.hello가 나올것이다.
이때 super.hello()를 통해 부모클래스에 있는 기능을 사용할 수 있다.

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

super 생성자.

생각해보면, 자식인스턴스를 생성할때 부모의 인스턴스도 생성된다고 하였는데,
우리는 부모의 생성자를 호출 해준적이 없다.
자바가 부모 클래스의 생성자가 기본생성자인 경우에는 super()를 자동으로 호출해준다.

public class ClassA {
    public ClassA(){
        System.out.println("ClassA 생성자");
    }
}

ClassA -> 파라미터가 없는 기본생성자임

public class ClassB extends ClassA{
    public ClassB(int a){
        //super();
        System.out.println("ClassB 생성자 a="+a);
    }
}

ClassB생성자는 반드시 부모의 생성자를 호출해야한다. 그러나 supr()를 호출하지 않아도, ClassA의 생성자는 기본생성자이기 때문에 자동으로 자바가 호출해준다.

public class ClassC extends ClassB{
    public ClassC(){
        //super(); 기본생성자 호출 불가 -> B에서 이미 생성자를 지정해놨기때문에 기본생성자를 Java가 만들지 않음
        super(10);
        System.out.println("classC 생성자");
    }
}

ClassC에서는 기본생성자를 반드시 호출해야한다.

왜냐하면, B에서 이미 생성자를 선언했으므로 자바가 기본생성자를 만들지 않는다. (생성자를 하나도 만들지 않은경우에 자바가 기본생성자 자동으로 만듬),

고로 ClassC생성자는 반드시 부모클래스의 생성자를 호출해줘야한다.

부모에서 기본생성자가 아닌 생성자를 정의했다 -> 자식 생성자에서 반드시 super호출

profile
항상 Why?[왜썻는지] What?[이를 통해 무엇을 얻었는지 생각하겠습니다.]

0개의 댓글