상세 컨텐츠

본문 제목

[C#] 인터페이스와 추상클래스

.NET/C#

by 클리엘 클리엘 2021. 1. 28. 09:01

본문

728x90

1. 인터페이스

 

인터페이스는 interface 키워드를 통해 다음과 같이 선언됩니다.

interface ICar
{
	public int Stop(int i);
}

인터페이스명은 통상 Interface의 앞글자를 앞에 붙인 이름을 사용하며 내부에 메서드의 한정자, 반환 형식 그리고 메서드 이름과 매개 변수만을 가집니다. 한정자는 생략할 수 있으나 이런 경우 private이 기본이 됩니다. 또한 인터페이스는 한정자로 private/public만을 가질 수 있습니다.

 

위에서 처럼 만들어진 인터페이스는 클래스에서 상속받을 수 있으며 클래스는 인터페이스에 선언된 메서드의 본체를 구현해야 합니다.

class Car : ICar
{
	public int Speed;
	public string Color;

	public void Drive()
	{
		Console.WriteLine($"Speed : {Speed}");
	}

	public int Stop(int i)
	{
		Speed = i;

		Console.WriteLine($"Stop!");

		return Speed;
	}
}

따라서 위 예제에서는 Car클래스가 ICar인터페이스를 상속받고 있으므로 인터페이스에 선언된 Stop메서드의 실체를 구현하고 있습니다. 이렇게 인터페이스를 사용하면 인터페이스를 상속받는 클래스는 인터페이스에 선언된 메서드를 구현하고 있음을 보장할 수 있게 됩니다.

 

인터페이스는 자체적으로 인스턴스를 생성할 수는 없으나 인터페이스를 상속받는 클래스로부터 참조를 생성할 수는 있기 때문에 다음과 같은 구현이 가능합니다.

static void Main(string[] args)
{
	ICar ic = new Car();
	ic.Stop(0);
}

이 특징을 이용하면 클래스에서 굳이 인터페이스를 상속받지 않더라도 인터페이스를 상속받아 실체가 구현된 클래스를 통해 동작 방식을 유연하게 바꿀 수 있습니다.

class CarStop : ICar
{
	int Speed = 0;
	public int Stop(int i)
	{
		Speed = i;
		Console.WriteLine($"Speed : {Speed}");
		Console.WriteLine("Stop");

		return Speed;
	}
}

에제에서는 ICar를 상속받아 CarStop이라는 클래스를 생성하였습니다. CarStop에서는 전달받은 매개변수의 값을 출력하고 Stop메시지를 뿌린 뒤 Speed값을 반환합니다.

class Car
{
	ICar ic;

	public int Speed;
	public string Color;

	public Car(ICar ic)
	{
		this.ic = ic;
	}

	public void Drive()
	{
		Console.WriteLine($"Speed : {Speed}");
	}

	public int Stop(int i)
	{
		ic.Stop(i);
		return i;
	}
}

Car클래스에서는 생성자에서 ICar의 인터페이스를 매개변수로 받아 Stop메서드를 호출할 때 인터페이스의 Stop을 호출할 수 있도록 하였습니다.

static void Main(string[] args)
{
	Car ic = new Car(new CarStop());
	ic.Stop(10);
}

Car클래스의 인스턴스를 생성할 때 생성자에게 CarStop클래스의 객체를 매개변수로 전달합니다. 이 객체는 ICar의 참조로 전달되는데 이것은 CarStop클래스가 ICar 인터페이스로부터 상속받은 클래스이기에 가능한 것입니다.

 

이러한 방식은 클래스가 인터페이스에 정의된 메서드를 구현하고 있음을 보장하면서도 메서드 실체 구현 부분을 유연하게 바꿀 수 있도록 도와줍니다. 예를 들어 Stop메서드에서 Speed값을 확인해 0이 아닌 경우 강제로 Speed를 0으로 만들어야 한다면 해당 로직이 구현된 ICar인터페이스를 상속받는 또 다른 클래스를 만들어

class CarStop2 : ICar
{
	int Speed = 0;
	public int Stop(int i)
	{
		if (i > 0)
			i = 0;

		Console.WriteLine($"Speed : {Speed}");
		Console.WriteLine("Stop");

		return Speed;
	}
}

Car클래스의 생성자 부분을 바꿔주기만 하면 됩니다.

static void Main(string[] args)
{
	Car ic = new Car(new CarStop2());
	ic.Stop(10);
}

클래스가 다른 클래스로부터 상속되는 경우 하나의 클래스에서만 상속받을 수 있지만 인터페이스는 여러 인터페이스에서 다중적으로 상속받을 수 있습니다. 인터페이스 입장에서 더 많은 메서드의 선언이 필요한 경우 기존의 인터페이스를 수정하거나 혹은 다른 인터페이스를 만들어 인터페이스끼리 상속받을 수 있도록 하면 그만큼 클래스에서 메서드를 구현할 수 있도록 강제할 수 있습니다.


2. 추상 클래스

 

추상 클래스는 abstract키워드를 통해 생성되며 내부에 인터페이스와 달리 실체를 가진 메서드를 가질 수 있습니다.

abstract class AbCar
{
	public int Speed;
	public string Color;

	public void Drive()
	{
		Console.WriteLine($"Speed : {Speed}");
	}
}

class Car : AbCar
{
	
}

추상 클래스를 상속받은 파생 클래스는 일반적인 클래스와 동일하게 추상 클래스의 멤버를 그대로 물려받게 됩니다.

static void Main(string[] args)
{
	Car c = new Car();
	c.Speed = 100;
	c.Drive();
}

그러나 추상 클래스는 일반적인 클래스와 달리 인스턴스를 생성할 수 없고 실체가 없는 추상 메서드를 가질 수 있습니다.

abstract class AbCar
{
	public int Speed;
	public string Color;

	public void Drive()
	{
		Console.WriteLine($"Speed : {Speed}");
	}

	public abstract void Stop();
}

추상 메서드도 abstract로 선언되며 선언 부외에 메서드의 구현 실체는 정의될 수 없고 추상 클래스를 상속하는 클래스에서 해당 메서드를 반드시 override 하여 재정의해야 합니다.

class Car : AbCar
{
	public override void Stop()
	{
		this.Speed = 0;

		Console.WriteLine($"Speed : {Speed}");
		Console.WriteLine("Stop");
	}
}

static void Main(string[] args)
{
	Car c = new Car();
	c.Speed = 100;
	c.Drive();

	c.Stop();
}

이와 같이 추상 클래스를 사용하면 반드시 파생 클래스로만 사용될 수 있고 파생 클래스에서 특정 메서드를 재정의해야 하는 경우를 강제할 수 있습니다.

 

지금까지 에제를 클래스의 메서드로만 했지만 인터페이스나 추상 클래스는 메서드뿐만 아니라 프로퍼티, 이벤트, 인덱서 등을 가질 수 있으며 역시 이를 상속하는 클래스는 인터페이스에 구현된 모든 멤버를 구현해야 합니다.

interface ICar
{
	public int Speed
	{
		get;
		set;
	}
	
	public int Stop(int i);
}

인터페이스에서 프로퍼티는 구체화할 수 없습니다.

abstract class AbCar
{
	public string Color;

	public abstract int Speed
	{
		get;
		set;
	}

	public void Drive()
	{
		Console.WriteLine($"Speed : {Speed}");
	}
}

추상클래스에서 프로퍼티는 구체화가 가능하기 때문에 컴파일할 때 예상치 못한 오류가 발생할 수 있습니다. 그러니 프로퍼티는 abstract키워드를 사용해서 구체화되지 않은 추상 프로퍼티로 선언해야 합니다.

728x90

'.NET > C#' 카테고리의 다른 글

[C#] 배열및 컬렉션과 인덱서활용 그리고 나열하기  (0) 2021.02.08
[C#] 프로퍼티  (0) 2021.02.01
[C#] 인터페이스와 추상클래스  (0) 2021.01.28
[C#] 클래스  (0) 2021.01.26
[C#] 구조체와 튜플  (0) 2021.01.25
[C#] 메서드  (0) 2021.01.22

관련글 더보기

댓글 영역