개발꿈나무
[C# 교과서] 22. C# 활용(16) - 상속으로 클래스 확장하기 본문
상속으로 클래스 확장하기
상속은 부모 클래스에 정의된 기능을 다시 사용하거나 확장 또는 수정하여 자식 클래스로 만드는 것이다.
클래스 상속하기
- 개체 지향 프로그래밍의 장점 중 하나는 이미 만든 클래스를 재사용하는 것인데, 이 재사용의 개념이 바로 상속이다.
- 부모 클래스(기본 클래스)의 모든 멤버를 자식 클래스(파생 클래스)가 재사용하도록 허가하는 기능이다.
- 여러 클래스 간의 관계를 설정할 때 수평 관계가 아닌 부모와 자식 간 관계처럼 계층적인 관계를 표현할 때 사용하는 개념을 상속이라고 한다.
- 클래스의 상속은 단일 상속(single inheritance)과 다중 상속(multiple inheritance)으로 구분할 수 있는데, C#의 클래스 상속은 단일 상속만 지원한다.
public class 기본클래스이름
{
//기본 클래스의 멤버 정의
}
public class 파생클래스이름 : 기본클래스이름
{
//기본 클래스의 멤버를 포함한 자식 클래스의 멤버 정의
}
- System.Object 클래스: System.Object 클래스는 모든 클래스의 부모 클래스로 닷넷에서 가장 높은 층에 속하는 조상 클래스라고 할 수 있다.
- 기본(base) 클래스: 다른 클래스의 부모가 되는 클래스를 뜻하며, Base 클래스, Super 클래스라고도 한다.
- 파생(derived) 클래스: 다른 클래스의 자식 클래스가 되는 클래스로 Derived 클래서, Sub 클래스, 자식 클래스로 표현된다.
부모 클래스와 자식 클래스
상속을 주는 클래스를 부모 클래스라고 하며, 상속을 받는 클래스를 자식 클래스라고 한다.
using System;
namespace InheritanceDemo
{
//[1] 부모 클래스 선언
class Parent //: System.Object 생략되어 있음
{
public void Foo() => Console.WriteLine("부모 클래스의 멤버 호출");
}
//[2] 자식 클래스 선언
class Child : Parent
{
public void Bar() => Console.WriteLine("자식 클래스의 멤버 호출");
}
class InheritanceDemo
{
static void Main()
{
// 자식 클래스의 인스턴스 생성
var child = new Child();
child.Foo(); // 부모 클래스의 멤버 호출
child.Bar(); // 자식 클래스의 멤버 호출
}
}
}
부모 클래스 형식 변수에 자식 클래스의 개체 할당하기
자식 클래스의 인스턴스를 부모 클래스 형식 변수에 담을 수 있는데, 이는 자식 클래스의 인스턴스와 동일하다.
클래스의 멤버에 정의된 override 키워드는 Object 클래스의 ToString() 메서드를 재정의(오버라이드) 하겠다는 의미이다.
using System;
namespace InheritancePractice
{
class Developer
{
public override string ToString()
{
return "개발자";
}
}
class WebDeveloper : Developer
{
public override string ToString() => "웹 개발자";
}
class MobileDeveloper : Developer
{
public override string ToString() => "모바일 개발자";
}
class InheritancePractice
{
static void Main()
{
var web = new WebDeveloper();
Console.WriteLine(web); // 웹 개발자
var mobile = new MobileDeveloper();
Console.WriteLine(mobile); // 모바일 개발자
}
}
}
this와 this() 그리고 base와 base()
클래스 내에서 this는 자신을 의미하고 this()는 자신의 생성자를 나타낸다.
마찬가지로 base는 부모 클래스를 의미하고, base()는 부모 클래스의 생성자를 나타낸다.
using System;
namespace ClassInheritance
{
public class ParentClass : Object //[A] 모든 클래스는 Object 클래스로부터 상속
{
protected void Print1() => Console.WriteLine("부모 클래스에서 정의한 내용");
}
public class ChildClass : ParentClass //[B] 콜론 기호로 부모 클래스 지정
{
public void Print2() =>
base.Print1(); //[C] 자식에서 base 키워드로 부모 요소에 접근
}
class ClassInheritance : Object
{
static void Main()
{
//[1] 부모 클래스의 인스턴스 생성
ParentClass p = new ParentClass();
Console.WriteLine(p.ToString()); // ClassInheritance.ParentClass
//[2] 자식 클래스의 인스턴스 생성
ChildClass c = new ChildClass();
//c.Print1(); // Print1() 메서드는 protected로 설정되어 있어 외부에서 접근 불가
c.Print2(); // 자식 클래스에 직접 구현한 기능
}
}
}
부모 클래스의 public, protected 멤버는 자식 클래스에서 물려받아 사용 가능하다.
base 키워드를 사용하여 부모 클래스의 생성자 호출하기
클래스에서 this()를 사용하여 생성자로 이동하는 것과 마찬가지로, 자식 클래스의 생성자에서 콜론 기호 뒤에 base()를 사용하여 부모 클래스의 생성자를 호출할 수 있다.
using System;
namespace BaseKeyword
{
public class Car
{
private string name;
public Car(string name)
{
this.name = name;
}
public void Run() => Console.WriteLine($"{this.name}가 달린다.");
}
public class My : Car
{
public My() : this("나의 자동차") { } //My 클래스의 생성자 호출 (default: 나의 자동차)
public My(string name) : base(name) { } //부모 클래스의 생성자 호출
}
public class Your : Car
{
public Your() : base("너의 자동차") { }
}
class BaseKeyword
{
static void Main()
{
(new My()).Run(); //나의 자동차가 달린다.
(new My("나의 끝내주는 자동차")).Run(); //나의 끝내주는 자동차가 달린다.
new Your().Run(); //너의 자동차가 달린다.
}
}
}
클래스 내에서 this()는 자신의 매개변수가 없는 생성자를 의미하고,
this(매개변수)는 매개변수가 있는 생성자를 호출한다.
봉인 클래스
클래스를 만들었는데, 더 이상 다른 클래스에 상속되지 않게 할 때 사용하는 클래스를 봉인(sealed) 클래스라고 한다.
봉인 클래스는 최종 클래스라고도 하며, 클래스 선언부에 sealed 키워드를 붙여 만든다.
using System;
namespace SealedClass
{
class Animal
{
public void Eat() => Console.WriteLine("밥을 먹습니다.");
}
// 봉인(Sealed) 클래스: 최종 클래스로 더 이상 상속되지 않습니다.
sealed class Cat : Animal { }
//class MyCat : Cat
//{
// // sealed 키워드가 붙은 클래스는 상속할 수 없습니다.
//}
class SealedClass
{
static void Main()
{
Cat cat = new Cat();
cat.Eat();
}
}
}
추상 클래스
클래스를 선언할 때 추가로 abstract 키워드를 붙여 추상 클래스를 선언할 수 있는데, 이 추상 클래스는 다른 클래스의 부모(parent) 또는 기본(base) 클래스 역할을 한다.
추상 클래스는 일반적인 클랫들의 부모 역할을 하는 클래스, 즉 공통 기능들을 모아 놓은 클래스 역할을 하며, 다른 클래스에 상속을 준 후 추가 기능을 하위 클래스에 구현하도록 하는 강제성을 띈다.
- 다른 클래스에 상속할 때 사용하는 클래스
- 추상 클래스를 사용하여 개체를 만들 수 없다. 즉, 추상 클래스와 인터페이스는 인스턴스화할 수 없다.
- 클래스를 설계할 때 부모 클래스 역할을 하면서 강제로 자식 클래스에 특정 멤버 이름을 물려줄 때 사용한다.
- 프로젝트를 작성할 때 멤버 이름을 맞추고 싶다면 추상 클래스에 먼저 정의한 후 자식 클래스에서 해당 멤버를 구현한다.
- 추상 클래스는 public 같은 액세스 한정자를 가진다.
- 추상 클래스는 멤버로 필드, 속성, 생성자, 소멸자, 메서드, 이벤트, 인덱서를 가진다.
using System;
namespace AbstractClassNote
{
public abstract class TableBase //추상 클래스
{
public int Id { get; set; }
public bool Active { get; set; }
}
public class Children : TableBase
{
public string Name { get; set; }
}
class AbstractClassNote
{
static void Main()
{
var child = new Children() { Id = 1, Active = true, Name = "아이" };
if (child.Active)
{
Console.WriteLine($"{child.Id} - {child.Name}");
}
}
}
}
추상 클래스와 추상 메서드
using System;
namespace AbstractClassShape
{
//[1] 추상 클래스
public abstract class Shape
{
//[2] 추상 멤버: 추상 메서드
public abstract double GetArea();
}
//[3] 추상 클래스를 상속하는 클래스
public class Square : Shape
{
private int _size;
public Square(int size)
{
_size = size;
}
// 부모 클래스인 Shape 추상 클래스의 추상 멤버인 GetArea() 메서드를 구현
public override double GetArea()
{
return _size * _size;
}
}
class AbstractClassShape
{
static void Main()
{
//[A] 자신의 이름으로 인스턴스 생성
Square square = new Square(10);
Console.WriteLine(square.GetArea()); // 100
//[B] 부모의 이름으로 인스턴스 생성
Shape shape = new Square(5);
Console.WriteLine(shape.GetArea()); // 25
}
}
}
추상 클래스의 추상 멤버는 자식 클래스에서 반드시 구현되어야 한다.
자식 클래스에만 멤버 상속하기
부모 클래스의 특정 멤버 중에서 자식 클래스에만 상속하려면 해당 멤버의 액세스 한정자를 public에서 protected로 설정한다.
필드 숨기기
using System;
namespace FieldHiding
{
class Parent
{
//[1] 필드 숨김: 필드는 무조건 private으로 설정
private string _word;
//[2] protected는 자식 클래스에서만 호출 가능한 멤버
protected string Word
{
get { return _word; }
set { _word = value; }
}
}
class Child : Parent
{
public void SetWord(string word)
{
base.Word = word;
}
public string GetWord()
{
return Word; // 부모 클래스의 Word 속성 접근
}
}
class FieldHiding
{
static void Main()
{
Child child = new Child();
child.SetWord("필드 숨기기 및 자식 클래스에게만 멤버 상속하기");
Console.WriteLine(child.GetWord());
}
}
}
기본 클래스의 멤버 숨기기
using System;
namespace MethodNew
{
//[1] 부모 클래스 생성
class Parent
{
public void Work() => Console.WriteLine("프로그래머");
}
//[2] 자식 클래스 생성
class Child : Parent
{
// 기본 멤버 숨기기: new -> 새롭게 정의, 다시 정의, 재정의
public new void Work() => Console.WriteLine("프로게이머");
}
class MethodNew
{
static void Main()
{
//[!] 자식 클래스의 인스턴스 생성
var child = new Child();
child.Work(); // "프로게이머"
}
}
}
부모 클래스에 만든 특정 메서드를 자식 클래스에서 새롭게 정의해서 사용할 때는
new 키워드로 자식 클래스에서 메서드를 재정의할 수 있다.
<Reference>
'C# 기초' 카테고리의 다른 글
[C# 교과서] 24. C# 활용(18) - 인터페이스 (0) | 2022.01.21 |
---|---|
[C# 교과서] 23. C# 활용(17) - 메서드 오버라이드 (0) | 2022.01.20 |
[C# 교과서] 21. C# 활용(15) - 대리자(delegate), 이벤트(Event) (0) | 2022.01.20 |
[C# 교과서] 20. C# 활용(14) - 인덱서와 반복기 (0) | 2022.01.20 |
[C# 교과서] 19. C# 활용(13) - 속성 사용하기 (0) | 2022.01.20 |