인터페이스는 메소드, 이벤트, 인덱서, 프로퍼티 만들 가질 수 있다.
그런데 그나마도 "구현부"가 음슴.
또한 인터페이스는 접근 제한 한정자를 사용할 수 없고, 모든 것이 public으로 선언된다.
또한 인터페이스는 객체도 만들 수 없다.
클래스 내부에 구현을 해서
파생클래스가 구현하도록 해주어야한다.
파생클래스는 인터페이스에 선언된 모든 메소드 (및 프로퍼티)를 구현해주어야 하며, 이 메소드들은
public한정자로 수식해아한다.
Class TestClass : ILogger
{
public void WriteLog(); // 이 인터페이스 반드시 구현해야함.
}
파생클래스 기반 클래스의 관계와 같이
TestClass의 객체는 ILogger의 객체로 취급할 수 있다.
namespace TestCSharp
{
interface ILogger
{
void WriteLog(string msg);
}
class ConsoleLogger : ILogger
{
public void WriteLog(string msg)
{
Console.WriteLine($"{DateTime.Now.ToLocalTime()} {msg}");
}
}
class FileLogger : ILogger
{
private StreamWriter _writer;
public FileLogger(string path)
{
_writer = File.CreateText(path);
_writer.AutoFlush = true;
}
public void WriteLog(string msg)
{
_writer.WriteLine($"{DateTime.Now.ToLocalTime()} {msg}");
}
}
class Climate
{
private ILogger logger;
public Climate(ILogger logger)
{
this.logger = logger;
}
public void start()
{
while(true)
{
Console.WriteLine(" 온도를 입력해 주세요 ");
string temp = Console.ReadLine();
if (temp == "")
break;
logger.WriteLog($"현재 온도 : {temp}");
}
}
}
public class Program
{
static void Main(string[] args)
{
Climate m = new Climate( new FileLogger("MyLog.txt") );
m.start();
}
}
}
Main안에서 new Climate 의 인자로 new ConsoleLogger, new FileLogger 를 넘겨줄 수 있다.
(FileLogger의 경우 텍스트 파일 생성함)
R이라는 클래스의 Ride라는 함수가 있고, Car, Plane이라는 클래스도 R을 상속받아
오버라이딩 했다고 하자.
이 두개의 클래스를 MyVehicle이라는 클래스가 상속받았다고 하면은
누구의 Ride 함수를 구현해야 할까?
죽음의 다이아몬드의핵심은 "모호성"이다.
클래스이름 | 함수 | 상속여부 |
---|---|---|
Ridable | Ride | 부모 |
Car | Ride() | Ridable을 상속받음 |
Plane | Ride() | Ridable을 상속받음 |
Myvehicle | ??? 누구를 상속 | Car, Plane을 상속받음 |
이럴경우 또하나의 문제가 발생하는데
"업캐스팅" 문제이다.
다중 상속이 허용이 된다면 업캐스팅 문제가 발생함.
그래서 C#에서는 "다중 상속"을 지원하지 않는다.
인터페이스의 다중상속은 가능.
인터페이스는 내용이 아닌 외형을 물려주기 때문.
알아야할게 인터페이스는 "다중 상속"이 가능하기때문에
클래스명 : 인터페이스1, 인터페이스2
이런식으로 상속이 가능하다. 다만, 반드시 구현을 해야한다.
interface ILogger
{
void WriteLog() {}
}
이 인터페이스 상속받는 애들은 무조건 다 구현을 해야됨.
여러개의 애들이 이것을 일단 다 구현을 해놨는데
ILogger인터페이스에 멤버 함수를 추가했다고 하자.
interface ILogger
{
void WriteLog() {}
void WriteLog2() {} // 추가한 함수
}
그러면 이거 상속받은 클래스들은 다 에러 뜬다.
구현부를 구현해주지 않아서 -> 일일히 다 수정 못함.
그럴때 사용하는것이 "기본 구현 메소드"이다.
interface ILogger
{
void WriteLog() {}
void WriteLog2(string error)
{
WriteLog(error);
} // 추가한 함수
}
이렇게하면 ILogger를 상속받은 애들은 컴파일 에러 뜨지않는다.
추상클래스나 클래스도 그 안에서 선언되는 모든 필드, 메소드, 프로퍼티 이벤트 등 모든 접근 한정자를 명시하지 않는다면,
priavate이다.
추상클래스는 abstract, public, private, protected, internal들의 한정자중 하나로 수식될 것을 강요한다.
연습문제 1. 인터페이스와 클래스가 다른 점은 무엇입니까?
풀이.
인터페이스는 클래스와 달리 내부에 메서드(함수)와 프로퍼티, 인덱서만 선언 가능하며, new 키워드를 통해 인스턴스화 할 수 없다. 또한 접근 지정자가 기본적으로 public으로 설정되어 있다. 따라서 해당 인터페이스를 상속받는 클래스를 만들어, 업 캐스팅 형식의 참조를 통해 인터페이스를 활용해야 한다.
나는 인터페이스를 약속이라고 생각한다. 인터페이스를 상속받는 모든 파생 클래스들은 인터페이스에 선언되어 있는 함수를 무조건 정의해야 한다. 따라서 어떤 프로그래머가 해당 클래스가 어떤 인터페이스를 상속받고 있는지만 알고 있어도, 해당 클래스가 어떤 기능을 하는지 유추할 수 있다.
위와 같은 점들이 인터페이스와 클래스의 큰 차이점이라고 생각한다.
연습문제 2. 인터페이스와 추상 클래스의 다른 점은 무엇입니까?
풀이.
추상클래스는 인터페이스와 기본적으로 역할(함수의 정의를 강요)은 비슷하지만, 인터페이스와 달리 데이터(변수)를 선언할 수 있다. 또한 필요에 의해 함수를 정의해도 괜찮다. 추상 클래스에는 해당 클래스에서만 사용 가능한 추상 메서드라는 것이 존재하는데, 이는 인터페이스의 메서드와 같은 역할을 한다. 하지만 모든 추상 클래스 내의 메소드와 데이터는 private으로 설정되어 있기 때문에, 추상 메서드의 접근 지정자를 public으로 설정하는 것을 추천한다.