[JAVA] 상속

Jaemin Lee·2023년 12월 11일

JAVA

목록 보기
7/7

이 글은 Inflearn 강의 중 김영한님의 김영한의 실전 자바 - 기본편을 감상하며 작성하였습니다.


상속을 사용해야하는 이유

중복 코드 제거

  전기차의 기능은 주행과 충전이 있고, 가솔린차의 기능은 주행과 주유가 있다고 가정하자. 두 의 공통점은 주행이다. 그리고 전기차와 가솔린차 모두 차(Car)라는 범주 안에 있다. 그럼 이 두 차(객체)는 차(Car)라는 객체에 객체에 주행기능을 넣어 상속받게하여 공통된 기능을 사용할 수 있다.

  또, 수소차를 추가로 생산하려고 한다. 수소차의 기능은 주행과 수소충전이다. 상속을 사용하지않고 수소차를 생산하게되면 주행이라는 기능을 또 개발하여 생산해야 한다. 하지만 차(Car)라는 객체를 상속받아 차의 기능을 가져다 쓰기만 하면 된다.

코드로 확인해 보자.

먼저, 전기차와 가솔린차를 개발해보면

public class ElectricCar {
    public void move() {
        System.out.println("이동");
    }
    public void fillUp() {
        System.out.println("전기 충전");
    }
}
public class GasCar {
    public void move() {
        System.out.println("이동");
    }
    public void fillUp() {
        System.out.println("주유");
    }
}

두 개의 클래스가 생성되었다.
보다시피 두 클래스모두 move() 라는 메서드(기능)이 추가되어있다.
여기에서 수소차를 추가해보자.

public class HydrogenCar {
    public void move() {
        System.out.println("이동");
    }
    public void fillUp() {
        System.out.println("수소 충전");
    }
}

move() 라는 메서드를 개발하였다.
예제 코드에서는 짧은 코드이지만 로직이 복잡한 메서드를 또 중복하여 개발하는 것은 너무 비효율적이다.

이제 상속을 활용하여 깔꼼하게 개발해보자.

//부모클래스
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 HydrogenCar  extends Car{
    public void fillHydrogen() {
        System.out.println("수소 충전");
    }
}

차(Car) 객체를 성성하고 extends 키워드를 활용하여 각 클래스에 상속시켰다.
여기에서 상속시킨 Car 클래스를 부모클래스라고 하고 상속받은 각 클래스를 자식클래스라고 한다.

이제 이 클래스들을 인스턴스화하고 공통메서드인 move()를 실행시켜보자.

public class CarMain {
    public static void main(String[] args) {
		//인스턴스 생성
        ElectricCar electricCar = new ElectricCar();
        GasCar gasCar = new GasCar();
        HydrogenCar hydrogenCar = new HydrogenCar();
        
        //move() 메서드 사용
        electricCar.move();
        gasCar.move();
		hydrogenCar.move();
    }
}

각 클래스에 move() 라는 메서드가 없는데 각 클래스의 참조변수를 통해 move()를 사용할 수 있게 됐다.

기능 추가

 이제 공통기능인 openDoor() 라는 메서드를 추가해보자.
상속을 사용하지않고 해당 메서드를 추가하기 위해서는 각 클래스마다 openDoor() 메서드를 추가해줘야한다. 하지만 상속을 사용하면 부모클래스에만 추가하면된다.

//부모클래스
public class Car {
	public void move() {
        System.out.println("이동");
    }
    public void openDoor() {
    	System.out.println("문열기");
    }
}

이제 Car 를 상속받은 클래스의 인스턴스에서 openDoor() 라는 기능을 사용할 수 있다.

	//openDoor() 메서드 사용
    electricCar.openDoor();
    gasCar.openDoor();
	hydrogenCar.openDoor();

메모리관점에서 본 상속

 ElectricCar 클래스가 Car 클래스를 상속받고 Car 클래스에있는 move() 메서드에 접근하는 것을 메모리관점에서 도식화 해보자.

heap 메모리영역에는 ElectricCar 클래스만 인스턴스화 되는것이아니라 상속받은 Car 인스턴스도 생성된다는 것을 기억하자. 처음에는 참조변수의 타입에 해당하는 인스턴스에 접근하여 move() 메서드를 찾고 없으면 부모클래스인 Car 인스턴스에서 찾는다.

인스턴스화 한 세 클래스를 모두 확인해 보면 Car 인스턴스가 포함되어있고 추가한 openDoor()메서드도 각각 생성된 것을 볼 수 있다.


메서드 오버라이딩

사용방법

 각 자동차마다 이동속도를 다르게 해야할 경우가 생길 수 있다. 예를 들어, 전기차는 두 차 보다 조금 더 빠르게 이동해야 할 경우 move()메서드를 재정의 하여 사용할 수 있다.

그 때 쓰이는 어노테이션이 @override 어노테이션 이다.

public class ElectricCar extends Car{
	@Override
    public void move() {
    	System.out.println("빠르게 이동");
    }
    public void charge() {
        System.out.println("전기 충전");
    }
}

이제 ElectricCar 인스턴스의 move()를 사용하면 "빠르게 이동" 이라는 텍스트가 출력될 것이다. 이것은 메모리관점에서 확인해 보면 쉽게 이해할 수 있다.

move() 메서드를 부모클래스를 상속받아 바로 사용한 것과 다르게 ElectricCar 인스턴스에 move() 메서드가 존재하여 해당 메서드를 실행시킨다.

사용 조건

  • 이름
    • 메서드이름이 부모클래스의 메서드이름과 동일해야한다.
  • 파라미터
    • 파라미터 타입, 순서, 갯수가 같아야 한다.
  • 반환타입
    • 반환타입이 같아야한다. 단, 하위클래스 타입일 수 있다.
  • 접근제어자
    • 오버라이딩 메서드의 접근제어자는 부모클래스 메서드의 접근제어자보다 더 제한적 일 수 없다.
  • static final private 키워가 사용된 메서드는 오버라이딩이 불가능하다.
    • static static 영역에 생성되므로 오버라이딩할 수 없다.
    • final final 키워드가 생성된 메서드는 재정의할 수 없다.
    • private private 접근제어자는 해당 클래스에서만 접근 가능하다.
profile
Developer

0개의 댓글