Programming/Java

자바 Java 상속, 부모 클래스와 자식 클래스(extends, super)

Jan92 2021. 12. 11. 01:50

상속(Inheritance)

'Java 상속 (Inheritance)'

Inheritance is the process by which a new class is created from another class.

 

[자식 클래스] extends [부모 클래스]
[하위 클래스] extends [상위 클래스]
[파생된 클래스] extends [기반 클래스]
[새로 작성하고자 하는 클래스] extends [상속 받고자하는 클래스]

 

자바에서 상속(Inheritance)이라는 개념은 부모 클래스(상위 클래스)와 자식 클래스(하위 클래스)의 관계에서 발생하며, '자식 클래스가 부모 클래스를 상속받는다.'라고 표현합니다.

 

상속을 받은 자식 클래스는 부모 클래스에 선언되어 있는 private 접근 제한을 갖는 필드와 메소드를메서드를 제외한 public, protected, default로 선언되어 있는 모든 변수와 메서드를 물려받아 사용할 수 있습니다.

만약 부모와 자식 클래스가 서로 다른 패키지에 있다면, 부모의 default 접근 제한을 갖는 필드 및 메소드도 자식이 물려받을 수 없습니다.

(상속하는 부모 클래스는 자신이 만든 클래스가 될 수도 있고,  JDK가 지원하는 클래스가 될 수도 있습니다.)

 

* default : 아무런 접근 제한자를 명시하지 않으면 default 값이 되며, 동일한 패키지 내에서만 접근 가능합니다.

 

부모 클래스가 변경되었을 때 자식 클래스는 자동으로 영향을 받게 되고, 반대로 자식 클래스의 변경은 부모에게 영향을 주지 않습니다.

자식 클래스의 멤버 개수는 부모 클래스보다 항상 같거나 많다는 점도 당연하지만 중요한 내용입니다.

 

 

[하위 클래스] extends [상위 클래스1], [상위 클래스2]
// 불가능

 

(또한 Java에서는 다중 상속이 불가능하며, 단일 상속만 가능합니다.)

 

 

이어서 아래 코드를 통해 상속이 무엇인지 직접 보도록 하겠습니다.

 

 


 

 

public class Parent {
	public Parent() {
		System.out.println("Default Constructor of Parent class");
	}
	
	public void parentMethod() {
		System.out.println("parentMethod() execute");
	}
}

- 부모 클래스

 

public class Child extends Parent{
	public Child() {
		System.out.println("Default Constructor of Child class");
	}
	
	public void childMethod() {
		System.out.println("childMethod() execute");
	}
}

- 자식 클래스

(이때 부모, 자식 클래스는 같은 패키지에 존재합니다.)

 

 

public class InheritanceExample {
	public static void main(String[] ar) {
		Child child = new Child();
		child.parentMethod();
		child.childMethod();
	}
}

//	    Default Constructor of Parent class
//          Default Constructor of Child class
//	    parentMethod() execute
//          childMethod() execute

 

먼저 자식 클래스인 Child 객체를 생성합니다. 이때 부모 클래스인 Parent 클래스의 기본 생성자도 실행됩니다. 그리고 Child는 Parent를 상속받았기 때문에 Parent 클래스에 구현된 parentMethod()를 사용할 수 있습니다. 그리고 자신의 메서드인 childMethod() 역시 사용할 수 있습니다.

 

예를 들어 Car라는 부모 클래스가 있습니다. Car 클래스는 속도를 측정하는 getSpeed() 메서드를 가지고 있습니다. 그리고 Car를 상속받는 자식 클래스인 Truck, RacingCar, Van, CompactCar 등이 있습니다. 자식 클래스들은 모두 부모 클래스에 구현된 공통 기능인 getSpeed() 메서드를 사용할 수 있으며, getSpeed() 메서드에 수정이 필요한 경우 부모 클래스인 Car에서만 수정하게 되면 자식 클래스들은 자동으로 수정된 메서드가 적용됩니다.

 

***

이처럼 상속을 사용하게 되면 이미 만들어진 클래스를 재사용할 수 있기 때문에 효율적이며, 중복된 코드가 줄어들게 됩니다.

또한 예시의 getSpeed() 메서드처럼 공통된 기능의 경우, 수정이 필요할 때 한 곳에서만 수정하면 되기 때문에 기능의 추가 및 유지 보수가 편리합니다.

 

 


 

 

public class ParentArg {
	public ParentArg(String name) {
		System.out.println("Constructor(" + name + ") of ParentArg class");
	}
}

- 부모 클래스

 

public class ChildArg extends ParentArg {
	public ChildArg() {
		super("Jan");
		System.out.println("Default Constructor of ChildArg class");
	}
}

- 자식 클래스

(마찬가지로 부모, 자식 클래스는 같은 패키지에 존재합니다.)

 

 

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

//     Constructor Jan of ParentArg class
//     Default Constructor of ChildArg class

 

이어서 부모 클래스의 생성자에 매개 변수가 필요한 경우입니다.

앞의 예제에서 자식 클래스 객체가 생성될 때 부모 클래스의 생성자도 실행되는 것을 봤는데요. 해당 경우처럼 부모 클래스의 생성자가 매개 변수를 받는다면 이때는 super() 메서드를 사용해서 부모 클래스의 생성자에 매개변수를 넘겨주면 됩니다.

 

이때 주의할 점은 자식 클래스의 생성자에 super() 메소드가 없다면 Java에서는 부모 클래스(상위 클래스)의 기본 생성자를 찾습니다. 하지만 지금의 경우와 같이 매개 변수를 받는 생성자만 있고, 기본 생성자가 없는 경우 오류가 발생하게 됩니다.

 

 


 

 

'오버 라이딩(Overriding)' 

 

앞의 Car class 예시처럼 부모 클래스에 정의되어 자식 클래스들이 공통으로 사용할 수 있는 getSpeed() 메서드가 있습니다.

이 메서드는 출력하는 속도의 단위가 km/h인데 수출을 위한 MediumCar(Car를 상속받은 자식 클래스)에서는 getSpeed()에서 출력하는 속도의 단위가 mile/h가 되어야 합니다. 

이처럼 상속을 하다보면 자식 클래스에서 부모 클래스에 정의된 메서드를 사용할 때 수정이 필요한 경우가 생길 수 있습니다. 이럴 때 사용하는 것이 바로 Overriding입니다.

 

 

public class Car {
	public Car() {
		System.out.println("Default Constructor of Car Class");
	}
	
	public void getSpeed() {
		System.out.println("speed :: km/h");
	}
}

- 부모 클래스

 

public class mediumCar extends Car {
	public mediumCar() {
		System.out.println("Default Constructor of mediumCar Class");
	}
	
	public void getSpeed() {
		System.out.println("speed :: mile/h");
	}
}

- 자식 클래스

 

오버 라이딩(Overriding)에서 중요한 것은 '동일한 시그니처(signature)를 가져야 한다'는 것입니다. 동일한 시그니처란, 메서드 이름과 매개 변수, 반환 타입이 모두 같아야 하고 중괄호 { } 안의 내용만 바뀔 수 있습니다. 

 

주의할 점은 자식 클래스에서 오버 라이딩하는 메서드는 부모 클래스의 메서드보다 좁은 범위의 접근 제어자를 사용할 수 없다는 것인데요.

만약 위 예시처럼 부모 클래스인 Car의 getSpeed() 메서드가 public인 경우 MediumCar에서 오버 라이딩하는 getSpeed() 메서드는 public 보다 좁은 범위의 접근 제어자인 protected, default, private를 가질 수 없다는 이야기입니다.

(public > protected > default > private)

 

 

 

추가로 정리하고 싶은 부분인 '업캐스팅'과 '다운 캐스팅'은 따로 포스팅하여 링크하도록 하겠습니다.

(자식 클래스가 부모 클래스 타입으로 캐스팅되는 업캐스팅, 부모 클래스가 자식 클래스 타입으로 캐스팅되는 다운 캐스팅)