
"일부는 내가 미리 만들어주고, 핵심 기능만 각자 만들게 할 순 없을까?"
이럴 때 사용하는 것이 바로 추상 클래스(Abstract Class)입니다.
마치 일부는 완성되어 있고, 일부는 비어있는 '미완성 설계도'와 같습니다.
이번 글에서는 이 미완성 설계도, 추상 클래스가 무엇인지 파헤쳐 보겠습니다!
이름 그대로 '추상적'인 클래스입니다. 즉, 그 자체로는 완전한 객체를 만들 수 없고,
다른 클래스가 상속하여 비어있는 부분을 완성해야만 의미를 갖는 클래스입니다.
비유: 도형 그리기
'도형'이라는 개념을 생각해 봅시다. 우리는 '도형' 그 자체를 그릴 수 없습니다.
'원', '사각형', '삼각형' 등 구체적인 도형을 그릴 수 있죠.
여기서 '도형'이 추상 클래스(Abstract Class)에 해당하고, '원', '사각형'이
추상 클래스를 상속받은 구체적인(Concrete) 클래스가 됩니다.
1. abstract키워드를 사용하여 클래스를 선언합니다.
2. 인터페이스처럼, 그 자체로 객체를 생성할 수 없습니다. (new Shape() -> 불가능!)
3. 일반 메서드(구현 완료)와 추상 메서드(껍데기만)를 모두 가질 수 있습니다.
(이것이 인터페이스와의 가장 큰 차이점!)
4. abstract로 선언된 추상 메서드는 자식 클래스에서
반드시 override키워드를 사용해 재정의(구현)해야 합니다.
5. 단 하나의 추상 클래스만 상속받을 수 있습니다. (단일 상속 원칙)
'도형' 만들기 예제를 코드로 직접 만들어보며 이해해 봅시다.
모든 도형은 '색상'을 가지고 있고, '그리는' 기능이 필요하다고 가정해 보겠습니다.
Shape클래스는 Describe()라는 완성된 메서드와,
Draw()라는 미완성된 추상 메서드를 모두 가지고 있습니다.
[코드]
// "이 클래스는 미완성이야!" 라고 abstract 키워드로 선언
public abstract class Shape
{
public string Color { get; set; }
// 1. 일반 메서드: 모든 도형이 공통으로 사용할 수 있는 완성된 기능
public void Describe()
{
Console.WriteLine($"이 도형의 색상은 {Color}입니다.");
}
// 2. 추상 메서드: 껍데기만 있음. "나를 상속받는 놈은 Draw를 반드시 구현해야 해!"
public abstract void Draw();
}
이제 Shape라는 미완성 설계도를 바탕으로
구체적인 '원'과 '사각형' 클래스를 만들어 보겠습니다.
[코드]
// 1. 원 클래스
public class Circle : Shape
{
// Shape 추상 클래스와의 약속을 지키기 위해 Draw() 메서드를 반드시 구현(override)
public override void Draw()
{
Console.WriteLine("원을 그립니다. ●");
}
}
// 2. 사각형 클래스
public class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("사각형을 그립니다. ■");
}
}
추상 클래스도 인터페이스처럼 다형성을 활용하여 코드를 유연하게 만들 수 있습니다.
[전체 코드]
using System;
// "이 클래스는 미완성이야!" 라고 abstract 키워드로 선언
public abstract class Shape
{
public string Color { get; set; }
// 1. 일반 메서드: 모든 도형이 공통으로 사용할 수 있는 완성된 기능
public void Describe()
{
Console.WriteLine($"이 도형의 색상은 {Color}입니다.");
}
// 2. 추상 메서드: 껍데기만 있음. "나를 상속받는 놈은 Draw를 반드시 구현해야 해!"
public abstract void Draw();
}
// 1. 원 클래스
public class Circle : Shape
{
// Shape 추상 클래스와의 약속을 지키기 위해 Draw() 메서드를 반드시 구현(override)
public override void Draw()
{
Console.WriteLine("원을 그립니다. ●");
}
}
// 2. 사각형 클래스
public class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("사각형을 그립니다. ■");
}
}
class Program
{
static void Main()
{
// Shape 타입 변수에 자식 클래스 인스턴스를 담을 수 있다.
Shape circle = new Circle { Color = "빨간색" };
Shape rectangle = new Rectangle { Color = "파란색" };
circle.Describe(); // 부모의 완성된 메서드 호출
circle.Draw(); // 자식이 재정의한 메서드 호출 (다형성)
rectangle.Describe(); // 부모의 완성된 메서드 호출
rectangle.Draw(); // 자식이 재정의한 메서드 호출 (다형성)
}
}
[실행 결과]
이 도형의 색상은 빨간색입니다.
원을 그립니다. ●
이 도형의 색상은 파란색입니다.
사각형을 그립니다. ■
추상 클래스는 객체로 만들 수 없지만, 자신을 상속받은 자식 클래스를
담는 변수 타입으로는 사용할 수 있습니다. 이것이 바로 다형성입니다.
둘 다 미완성이라는 공통점이 있지만, 사용하는 목적과 철학이 다릅니다.
protected, internal멤버가 필요할 때.