검수요청.png검수요청.png

상속 (프로그래밍)

위키원
이동: 둘러보기, 검색

            가기.png (다른뜻) 상속에 대해 보기

상속(相續, inheritance)은 객체 지향 프로그래밍(OOP)에서 자손 클래스가 조상 클래스의 기능을 그대로 이어받아서 재사용하는 것을 말한다. 자바에서는 계승, 확장이라는 단어로 사용된다.[1]

개요[편집]

객체 지향 프로그래밍의 3요소는 캡슐화, 상속, 다형성이다. 클래스로 객체가 정의되는 고전 상속에서, 클래스는 기반 클래스, 수퍼클래스, 또는 부모 클래스 등의 기존의 클래스로부터 속성과 동작을 상속받을 수 있다. 그 결과로 생기는 클래스를 파생 클래스, 서브클래스, 또는 자식 클래스라고 한다. 상속을 통한 클래스들의 관계는 계층을 형성한다.

상속 명칭 구분
조상 클래스 자손 클래스
부모 클래스 자식 클래스
상위 클래스 하위 클래스
기반 클래스 파생(derived)된 또는 확장(extended)된 클래스

프로토타입 기반 프로그래밍에서는, 객체가 클래스를 따로 정의할 필요 없이 다른 객체로부터 직접 정의될 수 있다. 이러한 특징을 차등 상속이라고 부른다.[2]

특징[편집]

상속을 통해서 클래스를 작성하면, 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있고 코드를 공통적으로 관리할 수 있기 때문에 코드의 추가 및 변경이 매우 용이하다. 이러한 특징은 코드의 재사용성을 높이고 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 크게 기여한다. 그리고 객체의 다형성을 구현 할 수 있다는 효과를 가지고 있다. 즉, 하위 객체에서 상위객체의 필드메소드를 따로 또 만들어서 사용할 필요가 없으며 상위 객체의 메소드에 문제가 있을 경우 상위 객체에서의 수정으로 인해 하위객체에서 문제가 있었던 메소드를 따로 수정할 필요없이 메소드를 사용할 수 있다.[3]

장점[편집]

객체 지향의 특징 중 첫번째 상속은 객체를 만들어 놓고 언제든지 다시 쓸 수 있다는 장점을 가지고 있다. 이 상속의 특징때문에 이미 만들어 놓은 상위 객체를 재사용해서 하위 객체를 쉽고 빠르게 만들수 있다. 그러므로 잘 만들어 놓은 객체만 있다면 고생할 필요가 없고 수정을 할때도 상위 객체를 수정해주면 상속받은 모든 하위 객체가 수정이 되므로 유지보수가 쉽다. [4]

단점[편집]

위와 같은 장점이 있는 상속은 장점만 있는 것이 아닌 단점들 또한 존재한다. 상위 클래스 기능에 버그가 생기거나 기능의 추가/변경 등으로 변화가 생겼을 때 상위 클래스를 상속받는 하위 클래스가 정상적으로 작동할 수 있을지에 대한 예측이 힘들다. 그 이유는 하위 클래스가 상위 클래스의 부분 집합이기 때문이다. 그리고 상속 구조가 복잡해질 수록 그 영향에 대한 예측이 힘들어진다. 상속을 받고 준 클래스들이 많을 수록 클래스간의 구조가 복잡해져 나중에는 오히려 보수가 힘들어지고 어떤 결과를 실행할 지에 대한 예측이 힘들어진다. 또한, 상위 클래스에서 의미 있었던 기능이 하위 클래스에서는 의미없는 기능일 수 있다는 단점이 있으며 하위 클래스는 반드시 상위 클래스로부터 물려 받은 기능들을 제공해야한다. 그리고 하위 클래스에서 기능들이 추가된다. 기능 확장에 따라 상위 클래스에서 파생된 클래스들이 많아지고, 그 규모가 커짐에 따라 일관성 있게 작성하지 않은 클래스들에 대한 이해도는 점차 복잡해지고 사용에 어려움이 생길 수 있기 때문에 조심해서 사용해야 한다.

다중 상속[편집]

죽음의다이아몬드(The Deadly Diamond of Death)

다중상속(Multiple inheritance)이란 객체 지향 프로그래밍의 특징 중 하나이며, 어떤 클래스가 하나 이상의 상위 클래스로부터 여러 가지 행동이나 특징을 상속받을 수 있는 것을 말한다. 이런 다중 상속은 한 객체에서 여러 부모의 메소드를 사용하기 위해서 사용하는데 한 객체가 여러 부모 클래스를 가지게 되는 것은 문제가 생긴다. 그 문제를 다이아몬드 문제라고 한다. 이 죽음의 다이아몬드는 ComboDrive라는 자식 클래스에서 Burn()이라는 메소드를 사용했다면 CDBurner, DvDBurner, DigitalRecoder 중 어느 객체에 접근해 함수를 사용할 것인지 애매한 상황이 생긴다. 그로인해 자바의 경우 단일 상속만을 사용한다.

장단점[편집]

여러 클래스로부터 상속받을 수 있기 때문에 복합적인 기능을 가진 클래스를 쉽게 작성할 수 있다는 장점이 있지만, 클래스 간의 관계가 매우 복잡해진다는 것과 서로 다른 클래스로부터 상속받은 멤버간의 이름이 같은 경우 구별할 수 있는 방법이 없다는 단점이 있다.[5]

단일 상속[편집]

C++에서는 여러 클래스로부터 상속받는 다중상속(Multiple Inheritance)을 허용하지만, 자바에서는 단일 상속만을 허용하기 때문에 하나 이상의 클래스로부터 상속을 받을 수 없다. 다중상속을 허용하면 여러 클래스로부터 상속받을 수 있기 때문에 복합적인 기능을 가진 클래스를 쉽게 작성할 수 있다는 장점이 있지만, 클래스간의 관계가 매우 복잡해진다는 것과 서로 다른 클래스로부터 상속받은 멤버들간의 이름이 같은 경우 구별할 수 있는 방법이 없다는 단점을 가지고 있다. 자바에서는 다중상속의 이러한 문제점을 해결하기 위해 다중상속의 장점을 포기하고 단일상속만을 허용한다. 대신 앞으로 배우게 될 인터페이스(interface)를 이용해서 보완된 형태의 다중상속을 구현할 수 있도록 하고 있다. 단일 상속이 하나의 조상클래스만을 가질 수 있기 때문에 다중상속에 비해 불편한 점도 있겠지만, 클래스간의 관계가 보다 명확해지고 코드를 더욱 신뢰성있게 만들어 준다는 점에서는 다중상속보다 유리하다.[6]

장단점[편집]

하나의 부모 클래스만을 가질 수 있기 때문에 다중 상속에 비해 불편하지만, 클래스 간의 관계가 보다 명확해지고 코드를 더욱 신뢰할 수 있게 만들어 준다는 점에서 다중상속보다 유리하다.[5]

사용 효과[편집]

이런 상속의 효과로는 부모 클래스를 재사용해서 자식 클래스를 빨리 개발 할 수 있다. 부모 클래스를 생성해두고 자식 클래스에서 부모 클래스를 상속받게 되면 한번 사용한 메소드를 다시 정의할 필요가 없기 때문에 재사용이 쉽고 그만큼 프로그램 개발에 용이하다. 또한 반복된 코드의 중복을 줄여준다. 이전에 사용했던 메소드가 있다면 상속을 받아 오버라이딩만 하면 되기 때문에 코드의 중복성을 줄여준다. 그리고 부모 클래스를 한 번만 수정함으로써 자식클래스를 수정할 필요가 없게되어 유지 보수에 있어 편리함을 제공해준다.[3]

활용[편집]

자바에서 상속을 구현하는 방법은 아주 간단하다. 새로 작성하고자 하는 클래스의 이름 뒤에 상속받고자 하는 클래스의 이름을 키워드 'extends'와 함께 써 주기만 하면 된다.[6]

public class Animal {
  String name;
  int legs;
  String getInfo() {
    return name + ", " + legs;
  }
}

public class Chicken extends Animal {
  int wings;

  String getInfo() {
    return name + ", " + legs + " legs, " + wings + " wings";
  }
}

상속은 자식 클래스가 부모 클래스의 필드메소드를 그대로 받아서 사용 할 수 있게 해주는 것으로 위 코드를 확인 했을 때 Animal이라는 클래스에서 정의한 name, legs와 같은 변수를 Chicken이라는 클래스에서 정의하지 않고 사용하는 것을 확인 할 수 있다.[7] 또한 Animal의 메소드를 그대로 가져와서 내용을 수정하는 오버라이딩을 쓸 수 있다.

상속 방법(선언)[편집]

상속의 선언법은 각 프로그래밍 언어 마다 다르며 선언 방법은 아래와 같다.

자바 상속[편집]

상속을 받는 방법은 상속받을 자식 클래스 뒤에 extends 키워드를 사용하고 부모 클래스를 적어주면 된다.

class 자식클래스 extends 부모클래스{ 내용 }

그리고 자바에서는 자식 클래스가 여러 부모로부터 다중 상속을 받은 것은 불가능하다. 즉, 1개의 부모 클래스로부터의 단일 상속만을 허용한다.

class 자식클래스 extends 부모클래스1, 부모클래스2{ 내용 } // 불가능

하지만 부모 클래스는 여러개의 자식 클래스에게 상속하는 것은 가능하다.[8]

class 자식클래스1 extends 부모클래스 { 내용 }
class 자식클래스2 extends 부모클래스 { 내용 } // 가능
class 부모클래스 extends 조상클래스 { 내용 }
class 자식클래스1 extends 부모클래스 { 내용 } // 가능
상속 제외[편집]
  1. 상속은 부모클래스로부터 멤버변수, 메소드르 상속 받는 것은 가능하지만 생성자는 상속이 불가능하다.
  2. 접근권한자가 private로 선언된 멤버변수와 메서드는 상속이 불가능하다.
  3. 부모클래스와 자식클래스가 다른 패키지에 존재한다면 default 접근 권한을 갖는 메소드는 상속이 불가능하다.[9]
부모생성자 호출[9][편집]
자식 객체 생성

자식 객체 생성은 부모 객체가 생성된 후 자식 객체를 생성한다. 자식생성자가 부모생성자를 명시적으로 선언하지 않았다면 컴파일러가 생성하여 자식생성자의 맨 첫줄에서 호출한다. 부모생성자의 호출방법은 자식생성자 맨 첫 줄에 super() 키워드를 사용한다. 만약 매개변수가 있는 부모생성자일 경우 반드시 명시적으로 선언해야 한다.

public class 자식클래스 {
	public 자식생성자() { 
		super();  //부모클래스의 부모생성자 호출
	}

인터페이스[편집]

자바에서는 다중상속이 불가능하다. 하지만 인터페이스를 사용한다면 다중상속이 가능하다. 인터페이스는 일종의 추상클래스이며 클래스처럼 메소드를 가질 수 있지만 기능을 구현할 수 없다. 그리고 멤버변수도 가질 수 없으며 상수만 가능하다.

extends를 이용한 클래스 상속은 하나만 되고 두개 이상은 인터페이스만 가능하다. 인터페이스를 implements 하는 것은 상속이 아니라 구현한다고 할 수 있으며, 두 개 이상의 구현 가능한 인터페이스를 콤마(,)로 구분해서 추가하면 된다.

class Animals implements IBird, IFlay {

	@Override
	public void eat() {
		// TODO Auto-generated method stub
	}
	@Override
	public void travel() {
 		// TODO Auto-generated method stub
	}
}

위 소스와 같이 인터페이스에서 구현해야 할 함수는 클래스에서 필히 추가해야한다.[10]

C++ 상속[편집]

C++의 경우 private 상속, protected 상속, public 상속이 있다. private는 외부에서 접근이 불가능하며, protected는 외부에서 접근이 불가능하나 파생 클래스에서는 접근이 가능하고, public은 어디서나 접근이 가능하다. 이전에 본 상속들은 모두 public 상속이다.[11]

상속 외부 접근 파생 클래스 접근
private 상속 X X
protected 상속 X O
public 상속 O O
private 상속[편집]

private 상속을 하게되면 private 제한자보다 접근 범위가 넓은 멤버는 모두 private 제한자로 바꾸어 상속을 하게 된다.

class Parent { 
	private:
		int num1; 
	public: 
		int num2;
	protected: 
		int num3;
};

class Base : private Parent { };

int main() 
{
	Base base;
	
	cout << base.num1 << endl; // error!
	cout << base.num2 << endl; // error!
	cout << base.num3 << endl; // error!
	return 0;
}

즉, 위와 같이 코드를 작성하면 에러가 생기게 되는데 private보다 접근 범위가 넓은 public, protected 멤버들은 전부 private로 바꾸어서 넘어와 버렸기 때문에 class Parent의 num2와 num3가 private로 변환되어 선언을 할 수 없게 되는 것이다.[11]

protected 상속[편집]

protected 상속은 protected 제한자 보다 접근 범위가 넓은 멤버는 모두 protected 제한자로 바꾸어 상속한다.

class Parent {
	private:
		int num1;
	public:
		int num2;
	protected:
		int num3;
};

class Base : protected Parent { };
int main() {
	
	Base base;
	
	cout << base.num1 << endl; // error!
	cout << base.num2 << endl; // error!
	cout << base.num3 << endl; // error!
	
	return 0;
}

즉 private, protected 멤버는 그대로 있고, Parent 클래스의 public 멤버는 protected로 바뀌어 상속한다. protected는 외부에서 접근할 수 없고 파생 클래스 내에서는 접근이 가능하다는 특징이 있어 위의 코드는 에러가 난다.[11]

public 상속[편집]

public 상속은 public 제한자보다 접근 범위가 넓은 멤버는 모두 public 제한자로 바뀌어 상속하게 되는데, public보다 접근 범위가 넓은 것은 없으므로 바뀌지 않고 그대로 상속하게 된다.

class Parent {
	private:
		int num1;
	public:
		int num2;
	protected:
		int num3;
};

class Base : public Parent { };
int main() {
	
	Base base;
	
	cout << base.num1 << endl; // error!
	cout << base.num2 << endl; // ok!
	cout << base.num3 << endl; // error!
	
	return 0;
}

위와 같이 코드를 작성했을 때, base의 num1과 num3는 에러가 나지만 기존에 public이었던 num2의 경우 에러가 생기지 않는다.

상속의 접근 범위에 따라서 제한자를 잘 사용해주어야하는데 제한자의 접근 범위를 한 줄로 정리하면 아래와 같다.[11]

private < protected < public

오버라이딩[편집]

오버라이딩은 부모의 기능을 자식 클래스에서 재정의하여 사용하는 것을 말한다. 이름 때문에 대부분 오버 로딩과 착각을 하는데 오버 로딩의 경우 동일한 이름의 메서드를 여러개 만드는 것으로 이름말고는 관련이 없다.

//부모 클래스
public class Animal {
  String name;
  int legs;

  //메소드 정의
  String getInfo() {
    return name + ", " + legs;
  }
}

//자식 클래스
public class Chicken extends Animal {
  int wings;

  //오버라이딩
  String getInfo() {
    return name + ", " + legs + " legs, " + wings + " wings";
  }
}

this와 super[편집]

상속에서 자주 사용하는 키워드로 자기 자신을 가르키는 this와 부모 클래스를 가르키는 super가 있다.

this[편집]

class Cal {
	public int sum(int v1, int v2) {
		return v1 + v2;
	}
	public int sum(int v1, int v2, int v3) {
		return this.sum(v1, v2) + v3; //this 사용
	}
}

위와 같은 코드에서 Cal의 sum 메소드에서 this를 사용하였는데 this의 형태는 'this.명칭' 이다. 이는 sum이라는 메소드를 불러올 때 자신의 클래스 내에 있는 메소드를 불러오기 위해 사용하는 형태라고 할 수 있다.[7]

super[편집]

class Cal3 extends Cal {

	public int sum(int v1, int v2) {
		System.out.println("Cal3!!!");
		return super.sum(v1, v2); //super 사용
	}
	
	public int minus(int v1, int v2) {
		return v1 - v2;
	}
}

그리고 Cal3에서 super를 사용하였다. 'super.명칭'으로 사용하며 이는 부모 클래스를 지정한다. 부모 클래스 Cal로부터 상속을 받은 Cal3는 super를 이용해 부모클래스에 있는 sum을 불러올 수 있다.[7]

각주[편집]

  1. 나무위키, 〈상속(프로그래밍〉, 《나무위키》
  2. 위키백과, 〈상속(객체 지향 프로그래밍)〉, 《위키피디아》, 2014-07-03
  3. 3.0 3.1 재희 jaiyah, 〈객체 지향 프로그래밍의 상속과 다형성〉, 《Web Club》
  4. Ara Blog, 〈자바(JAVA) 객체지향 특징 상속/캡슐화/다형성〉, 《개인블로그》, 2015-10-29
  5. 5.0 5.1 에스콸리, 〈다중상속과 단일상속의 장단점〉, 《개인블로그》, 2009-03-11
  6. 6.0 6.1 명우니닷컴, 〈제7장 객체지향개념 2-1. 상속(Inheritance)〉, 《개인블로그》
  7. 7.0 7.1 7.2 susu1991, 〈객체지향 프로그래밍 : 상속〉, 《개인블로그》, 2020-06-17
  8. joker, 〈상속의 개념 및 부모/자식 클래스〉, 《개인블로그》, 2017-03-17
  9. 9.0 9.1 신용권, 〈이것이 자바다〉, 《한빛출판네트워크》, 2015-01-06
  10. 녹두장군 - 상상을 현실로, 〈자바 다중상속 인터페이스 다루는 방법〉, 《개인블로그》, 2015
  11. 11.0 11.1 11.2 11.3 끝나지않는 프로그래밍 일기, 〈C++ 강좌 12편. 상속(Inheritance)〉, 《개인블로그》, 2012-11-12

참고자료[편집]

같이 보기[편집]


  검수요청.png검수요청.png 이 상속 (프로그래밍) 문서는 프로그래밍에 관한 글로서 검토가 필요합니다. 위키 문서는 누구든지 자유롭게 편집할 수 있습니다. [편집]을 눌러 문서 내용을 검토·수정해 주세요.