[This is C#] Chapter 8. 인터페이스와 추상 클래스
Table of Contents
이것이 C#이다 책을 읽고 공부한 노트입니다.
인터페이스의 선언 #
- 인터페이스(Interface)
- 메소드, 이벤트, 인덱서, 프로퍼티만을 가질 수 있다.
- 인터페이스는 파생 클래스가 반드시 자신이 가진 것을 구현하도록 강제한다.
- 접근 제한 한정자를 사용할 수 없고, 모든 것이
public
으로 선언된다. - 인스턴스도 만들 수 없다.
- 참조는 가능하다.
interface IInterface
{
void Method();
}
class MyClass : IInterface
{
public void Method()
{
//...
}
}
class MainApp
{
static void Main(string[] args)
{
IInterface a = new MyClass();
a.Method();
}
}
인터페이스를 상속하는 인터페이스 #
- 인터페이스를 상속할 수 있는 것
- 클래스
- 구조체
- 인터페이스
- 이미 해당 인터페이스를 상속하는 클래스들이 많거나, 상속하려는 인터페이스가 소스 코드가 아닌 어셈블리로만 제공되는 경우, 인터페이스를 상속하는 인터페이스를 만들 수 있겠다.
interface IBase
{
void BaseMethod();
}
interface IDerived : IBase // 인터페이스를 상속하는 인터페이스
{
void DerivedMethod();
}
class MyClass : IDerived
{
public void BaseMethod()
{
//...
}
public void DerivedMethod()
{
//...
}
}
class MainApp
{
static void Main(string[] args)
{
Derived d = new MyClass();
d.BaseMethod();
d.DerivedMethod();
}
}
여러 개의 인터페이스, 한꺼번에 상속하기 #
- C#은 여러 클래스를 한꺼번에 상속할 수 없다.
- 죽음의 다이아몬드 문제 때문이다.
- 하지만 여러 인터페이스를 상속할 순 있다.
interface IRun
{
void Run();
}
interface IFly
{
void Fly();
}
class FlyingCar : IRun, IFly
{
public void Run()
{
Console.WriteLine("Run");
}
public void Fly()
{
Console.WriteLine("Fly");
}
}
class MainApp
{
static void Main(string[] args)
{
FlyingCar car = new FlyingCar();
car.Run();
car.Fly();
IRun run = new FlyingCar();
run.Run();
IFly fly = new FlyingCar();
fly.Fly();
}
}
- 포함(Containment) 기법
- 다른 클래스의 기능을 새로운 클래스에 넣는 방법으로써, 물려받고 싶은 기능을 가진 클래스들을 필드로 선언해 넣는 것이다.
class MyVehicle
{
Car car = new Car();
Plane plane = new Plane();
public void Run() { car.Run(); }
public void Fly() { plane.Fly(); }
}
인터페이스의 기본 구현 메소드 #
- 기존 인터페이스에 메소드를 추가하고 싶다.
- 하지만 인터페이스를 상속하는 파생 클래스가 수 없이 많아서 일일이 다 수정하기에는 너무 번거롭다.
- 그렇다면, 인터페이스에 기본 구현을 제공하는 메소드를 만들자.
interface IInterface
{
void PreviousMethod();
void NewMethod() // 기본 구현 메소드
{
Console.WriteLine("New Method");
}
}
class Myclass : IInterface
{
public void PreviousMethod()
{
Console.WriteLine("Previous Method");
}
}
class MainApp
{
static void Main(string[] args)
{
IInterface i = new Myclass();
i.PreviousMethod();
i.NewMethod();
Myclass m = new Myclass();
m.PreviousMethod();
m.NewMethod(); // 컴파일 에러! NewMethod()를 오버라이딩 하진 않았다.
}
}
명시적인 인터페이스 구현 #
- 암묵적 구현
public
으로 구현하는 방식이다.
- 명시적 구현
private
으로 구현하며[인터페이스이름.함수명]
형식으로 함수 이름을 표시한다.
public interface IMyInterface
{
string Name { get; set; }
int Age { get; set; }
}
public class MyClass : IMyInterface
{
// 암시적인 구현 = public
public string Name { get; set; }
// 명시적인 구현 = private = 인터페이스로 형변환 시에만 호출할 수 있다.
int IMyInterface.Age { get; set; }
}
class Program
{
static void Main(string[] args)
{
MyClass a = new MyClass();
a.Name = "Kim";
a.Age = 1; // (X) 접근 불가
((IMyInterface)a).Age = 1; // (O) 접근 가능
}
}
- 여러 개의 인터페이스를 상속받을 때, 이름이 같은 서로 다른 멤버를 선언하는 사례도 해결할 수 있겠다.
interface ILeft
{
int P { get;}
}
interface IRight
{
int P();
}
class Middle : ILeft, IRight
{
public int P() { return 0; }
int ILeft.P { get { return 0; } }
}
추상 클래스: 인터페이스와 클래스 사이 #
- 추상 클래스(Abstract class)
- 구현을 가질 수 있지만, 인스턴스를 만들 수 없다.
- 추상 메소드를 가질 수 있다.
- 추상 메소드는 파생 클래스가 반드시 이 메소드를 구현하도록 강제한다.
- 추상 메소드는 반드시
public
,protected
,internal
,protected internal
중에 하나여야 한다.private
이면 안 된다.
abstract class AbstractBase
{
public abstract void AbstractMethod();
}
class MyClass : AbstractBase
{
public override void AbstractMethod()
{
//...
}
}
- 추상 클래스가 또 다른 추상 클래스를 상속하는 경우
abstract class AbstractBase
{
public abstract void AbstractMethod();
}
abstract class AbstractDerived : AbstractBase
{
public abstract override void AbstractMethod();
}
class MyClass : AbstractDerived
{
public override void AbstractMethod()
{
Console.WriteLine("Hello");
}
}
class MainApp
{
static void Main(string[] args)
{
AbstractBase b = new MyClass();
b.AbstractMethod();
}
}