상속은 기존 클래스(부모 클래스)를 확장하거나 재사용하여 새로운 클래스(자식 클래스)를 생성하는 과정
자식 클래스는 부모 클래스의 멤버(필드, 메서드, 프로퍼티 등)를 상속받아 사용할 수 있다.
상속을 통해 부모 클래스의 기능을 확장하거나 수정하여 새로운 클래스를 정의할 수 있다.
코드의 재사용성
: 상속을 통해 기존 클래스의 코드를 재사용할 수 있으므로, 반복적인 코드 작성을 줄일 수 있습니다.
계층 구조의 표현
: 클래스 간의 계층 구조를 표현하여 코드의 구조를 명확하게 표현할 수 있습니다.
유지보수성의 향상
: 상속을 통해 기존 클래스의 수정이 필요한 경우, 해당 클래스만 수정하면 됩니다. 이로써 코드의 유지보수성이 향상됩니다.
단일 상속
: 하나의 자식 클래스가 하나의 부모 클래스만 상속받습니다. 예를 들어, C#에서는 단일 상속만을 지원합니다.
다중 상속
: 하나의 자식 클래스가 여러 개의 부모 클래스를 동시에 상속받습니다. 하지만 C#은 다중 상속을 지원하지 않습니다.
인터페이스 상속
: 클래스가 인터페이스를 상속하는 것을 말합니다. 인터페이스는 다중 상속을 지원하며, 클래스는 하나의 클래스와 여러 개의 인터페이스를 동시에 상속받을 수 있습니다.
부모 클래스의 멤버 접근
: 자식 클래스는 상속받은 부모 클래스의 멤버에 접근할 수 있으며, 이를 통해 부모 클래스의 기능을 재사용할 수 있습니다.
메서드 재정의
: 자식 클래스는 부모 클래스의 메서드를 재정의하여 자신에게 맞게 수정할 수 있습니다.
이를 통해 다형성(Polymorphism)을 구현할 수 있습니다.
상속의 깊이
: 클래스는 여러 계층의 상속 구조를 가질 수 있습니다. 상속의 깊이가 깊어질수록 클래스 간의 관계가 복잡해질 수 있으므로, 적절한 상속의 깊이를 유지하는 것이 중요합니다.
상속 관계에서 멤버의 접근 제한자는 중요한 역할을 합니다. 부모 클래스의 멤버의 접근 제한자에 따라 자식 클래스에서 해당 멤버에 접근할 수 있는 범위가 결정됩니다.
접근 제한자를 통해 상속된 멤버의 가시성을 조절하여 캡슐화와 정보 은닉을 구현할 수 있습니다.
// 부모 클래스
public class Animal
{
public string Name { get; set; }
public int Age { get; set; }
public void Eat()
{
Console.WriteLine("Animal is eating.");
}
public void Sleep()
{
Console.WriteLine("Animal is sleeping.");
}
}
// 자식 클래스
public class Dog : Animal
{
public void Bark()
{
Console.WriteLine("Dog is bark.");
}
}
public class Cat : Animal
{
public void Sleep()
{
Console.WriteLine("Cat is sleeping.");
}
public void Meow()
{
Console.WriteLine("Cat is meow.");
}
}
// 사용 예시
Dog dog = new Dog();
dog.Name = "Bobby";
dog.Age = 3;
dog.Eat(); // Animal is eating.
dog.Sleep(); // Animal is sleeping.
dog.Bark(); // Dog is barking
Cat cat = new Cat();
cat.Name = "KKami";
cat.Age = 10;
cat.Eat();
cat.Sleep();
cat.Meow();
virtual
키워드를 사용해서 선언하고, 자식 클래스에서는 override
키워드를 사용해서 재정의할 수 있습니다.
public class Unit
{
public virtual void Move() // virtual : 자식이 재정의를 했을 수 있다.
{
Console.WriteLine("두발로 걷기");
}
// virtual 이면 실형태가 다를 수 있으니 실형태에 재정의가 되어있는지 확인
public void Attack()
{
Console.WriteLine("Unit 공격");
}
}
public class Marine : Unit
{
// Marine 클래스에서는 Move 메서드를 재정의하지 않음.
}
public class Zergling : Unit
{
public override void Move() // override : 재정의
{
Console.WriteLine("네발로 걷기");
}
}
static void Main(string[] args)
{
//Marine marine = new Marine();
//marine.Move();
//marine.Attack();
//Zergling zergling = new Zergling();
//zergling.Move();
//zergling.Attack();
// unit 참조의 형태, marine과 zergling은 실형태
List<Unit> list = new List<Unit>();
list.Add(new Marine());
list.Add(new Zergling());
foreach (Unit unit in units)
{
unit.Move(); // Marine은 "두발로 걷기", Zergling은 "네발로 걷기"
}
}
추상 클래스: 직접 인스턴스를 생성할 수 없는 클래ㅅ이다. abstract
키워드를 사용해서 선언하며, 주로 상속을 위한 베이스 클래스로 사용된다.
추상 메서드: 구현부가 없는 메서드로, 자식 클래스에서 반드시 구현해야 한다. 추상 클래스에 포함될 수 있다.
abstract class Shape // 추상 클래스
{
public abstract void Draw(); // 추상 메서드
}
class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a Circle");
}
}
class Square : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a Circle");
}
}
class Triangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a Circle");
}
}
static void Main(string[] args)
{
List<Shape> list = new List<Shape>();
list.Add(new Circle());
list.Add(new Square());
list.Add(new Triangle());
foreach (Shape shape in list)
{
shape.Draw();
}
}
기준 | 오버라이딩(Overriding) | 오버로딩(Overloading) |
---|---|---|
정의 | 부모 클래스에서 정의된 메서드를 자식 클래스에서 재정의하는 것 | 같은 이름의 메서드를 매개변수의 개수, 타입, 순서가 다르게 여러 번 정의하는 것 |
목적 | 부모 클래스의 메서드 동작을 자식 클래스에서 변경하거나 확장하기 위함 | 같은 이름의 메서드를 다양한 매개변수로 사용하기 위함 |
메서드 시그니처 | 메서드 이름, 매개변수의 수와 타입, 반환 타입이 부모 클래스의 메서드와 동일해야 함 | 메서드 이름은 같지만 매개변수의 수, 타입, 순서가 달라야 함 |
키워드 | override (C# 등 일부 언어에서 사용) | overload 라는 특별한 키워드는 없음 (매개변수의 차이로 구분) |
사용 예시 | 부모 클래스 Animal 에 MakeSound 메서드가 있고, 자식 클래스 Dog 에서 MakeSound 를 Bark 로 재정의할 수 있음 | Add(int, int) 메서드와 Add(int, int, int) 메서드가 동일한 클래스 내에서 공존할 수 있음 |
다형성의 개념과 사용 방법은 객체 지향 프로그래밍에서 매우 중요하다. 다형성을 이해하고 잘 사용하면, 유연하고 확장성 있는 코드를 작성할 수 있다.
public class Shape
{
public virtual void Draw()
{
Console.WriteLine("Drawing a shape.");
}
}
public class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle.");
}
}
public class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a rectangle.");
}
}
Shape shape1 = new Circle();
Shape shape2 = new Rectangle();
shape1.Draw(); // Drawing a circle.
shape2.Draw(); // Drawing a rectangle.
오버로딩(Overloading)
동일한 메서드 이름을 가지고 있지만, 매개변수의 개수, 타입 또는 순서가 다른 여러 개의 메서드를 정의하는 것을 의미합니다.
오버로딩을 통해 동일한 이름을 가진 메서드를 다양한 매개변수 조합으로 호출할 수 있습니다.
C#
복사
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public int Add(int a, int b, int c)
{
return a + b + c;
}
}
Calculator calc = new Calculator();
int result1 = calc.Add(2, 3); // 5
int result2 = calc.Add(2, 3, 4); // 9