상속은 객체지향의 재활용성을 극대화시킨 기법이자, 객체지향을 복잡하게 하는 주원인이다. 상속(Inheritance)는 어떤 객체가 있을 때 그 객체의 필드(변수)와 메소드를 다른 객체가 물려받을 수 있는 기능을 말한다.
다음의 Calculator 코드
에 substract 기능을 추가하고 싶다고 가정해보자.
class Calculator{
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);
}
}
public class CalculatorDemo4 {
public static void main(String[] args) {
Calculator c1 = new Calculator();
c1.setOprands(10, 20);
c1.sum();
c1.avg();
Calculator c2 = new Calculator();
c2.setOprands(20, 40);
c2.sum();
c2.avg();
}
}
위의 코드에 substract()를 추가하고 싶다면, substract 메소드를 추가하여 호출하면 된다. 그러나 다음의 2가지 상황에 속한다면 객체에 메소드를 추가하는 것이 어렵다.
1. 자신이 객체를 만들지 않음
→ 소스 변경 불가 / 변경 가능해도 원 소스 업데이트 시 substarct 메소드가 사라짐
2. 메소드를 추가하면 다른 곳에서는 불필요한 기능이 포함될 수 있음
→ 몰라도 되는 것 까지 알아야 함
기존 객체를 수정하지 않으면서 새로운 객체가 기존의 객체를 기반으로 생성되게끔 하는 것이 상속이다. 기존 객체가 기능을 물려주기에 '부모 객체'가 되고, 새로운 객체는 기존 객체의 기능을 물려받으므로 '자식 객체'가 된다.
- 부모 클래스 - 자식 클래스
- 상위(super) 클래스 - 하위(sub) 클래스
- 기초(base) 클래스 - 유도(derived) 클래스
class calculator2 {
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 SubstractionableCalculator extends calculator2 {
public void substract() {
System.out.println(this.left - this.right);
}
}
public class Inheritance2 {
public static void main(String[] args) {
SubstractionableCalculator c1 = new SubstractionableCalculator();
c1.setOprands(10, 20);
c1.sum();
c1.avg();
c1.substract();
}
}
위의 코드가 전체 코드이다. 분해해서 생각해보자.
class SubstractionableCalculator extends calculator2 {
public void substract() {
System.out.println(this.left - this.right);
}
}
새로운 SubstractionableCalculator 클래스를 정의했다. 이 클래스에는 substract 메소드만 존재한다. 그렇다면 어떻게 c1.substract()
로 호출이 가능한 것일까? 바로 extends calculator2
덕분이다. 이는 클래스 calculator2를 상속 받는다는 의미로, SubstractableCalculator는 Calculator에서 정의한 setOprands, sub, avg를 사용할 수 있게 된다.
위의 코드에 곱셈 기능까지 더해주고 싶다면 어떻게 하면 될까? SubstractableCalculator처럼 클래스를 정의할 때 extends
를 이용하고, main()에서 객체 생성 시 생성한 MultiplecationCalculator 클래스를 이용하면 된다.
class MultiplecationCalculator extends Calculator {
public void multiplecation() {
System.out.println(this.left*this.right);
}
}
상속한 클래스를 다시 상속하는 것도 가능하다. 위에서 생성한 MultiplecationCalculator 클래스를 상속받는 클래스를 만들어보자.
1. MultiplecationCalculator를 상속받는 DivisionCalculator클래스 생성하기
class DivisionCalculator extends MultiplecationCalculator {
public void division() {
System.out.println(this.left / this.right);
}
}
public static void main(String[] args) {
DivisionCalculator c1 = new DivisionCalculator();
c1.setOprands(10,20);
c1.sum();
c1.avg();
c1.multiplication();
c1.division();
}
public class Constructor {
public Constructor(int p) {
public static void main(String[] args) {
Constructor c = new Constructor();
}
}
}
위의 코드를 실행하면 에러가 발생한다. 상위 클래스에 매개변수가 있는 생성자가 있다면, 자바는 상위 클래스의 기본 생성자를 만들어주지 않는다. 때문에 기본 생성자를 직접 추가해주어야한다.
public class Constructor {
public Constructor() {} // 기본 생성자 추가
public Constructor(int p) {
public static void main(String[] args) {
Constructor c = new Constructor();
}
}
}
SubstractableCalculator의 생성자로 left와 right 값을 받아서 초기화시키고, setOprands가 아닌 생성자를 통해 left와 right값을 설정해보자.
class Calculator3 {
int left, right;
public Calculator3() {} // 상위 클래스에 기본 생성자 추가하기
public Calculator3(int left, int right){
this.left = left;
this.right = 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 SubstractionableCalculator2 extends Calculator3 {
public SubstractionableCalculator2(int left, int right) {
this.left = left;
this.right = right;
}
public void substract() {
System.out.println(this.left - this.right);
}
}
public class Inheritance3 {
public static void main(String[] args) {
SubstractionableCalculator2 c1 = new SubstractionableCalculator2(10, 20);
c1.sum();
c1.avg();
c1.substract();
}
}
상위 클래스인 Calculator3에는 left와 right 값을 초기화할 수 있는 생성자가 이미 존재한다. 상위 클래스의 생성자를 호출해보자.
super를 사용하여 상위 클래스의 생성자를 호출하는 방법은 간단하다.
class SubstractionableCalculator2 extends Calculator3 {
public SubstractionableCalculator2(int left, int right) {
super(left, right); // 상위 클래스의 생성자 호출하기
}
public void substract() {
System.out.println(this.left - this.right);
}
}
이 Calculator3을 부모 클래스로 갖는 SubstractionableCalculator2클래스 내에서, this.left = left; this.right = right;
를 super(left, right);
로 바꾸워주면 된다.
super
키워드는 부모 클래스를 뜻한다. super()
는 부모 클래스의 생성자를 의미한다. 이렇게 하면 부모 클래스의 기본 생성자 public Calculator3() {}
가 없어져도 오류가 발생하지 않는다.
단, 하위 클래스의 생성자에서 super 사용 시, super가 가장 먼저 나타나야 함.
Reference