인터페이스로 직접 인스턴스 생성 불가 (파생클래스의 참조는 할 수 있다.)
interface
키워드로 선언.
식별자 앞에 I 를 붙이는 것이 국룰
interface ISomething{
// 구현부는 포함하지 않는다. 포함하는 경우는 뒤에서 설명
int method1 ( 매개변수들 );
void method2 ( 매개변수들 );
}
인터페이스를 상속한 파생클래스는, 인터페이스에 정의된 것을 모두 구현해야 한다.
이 때, 인터페이스를 따라 구현하는 것은 public
한정자가 붙어야 한다.
아래 예시에서는 메소드 뿐 아니라 프로퍼티도 인터페이스에 선언
되어있는데,
자동구현 프로퍼티와 모양은 같지만 선언
된 것임을 잘 기억해야 한다.
인터페이스에 선언
한다고 해서 자동구현
되는 것은 아니다.
인터페이스를 상속하는 클래스에서 인터페이스를 따라 선언했을 때 자동구현된다.
namespace Program
{
interface ILogger
{
void ConsoleLog(string str);
void FileLog(string str);
void CloseFile();
}
class Logger : ILogger
{
private StreamWriter writer;
public Logger(int id)
{
writer = new StreamWriter($"output_{id}.txt");
}
public void ConsoleLog(string str)
{
Console.WriteLine(str);
}
public void FileLog(string str)
{
writer.WriteLine(str);
writer.AutoFlush = true;
}
public void CloseFile()
{
writer.Close();
}
}
interface IAnimal {
string Sound { get; set; }
void cry();
}
class Gorani : IAnimal
{
public ILogger Log {get; set;}
public string Sound { get; set; }
public void cry ()
{
Log.ConsoleLog(Sound);
Log.FileLog(Sound);
}
public void stopCrying()
{
Log.CloseFile();
}
}
class Program
{
static public void Main(string[] Args)
{
IAnimal gorani_1 = new Gorani() {
Sound = "weeaeaeak!",
Log = new Logger(1)
};
Gorani gorani_2 = new Gorani()
{
Sound = "waaaaaarark!",
Log = new Logger(2)
};
gorani_1.cry(); // weeaeaeak!
gorani_2.cry(); // waaaaaarark!
}
}
}
인터페이스는 다중상속 할 수 있다.
클래스 다중상속시 나타나는 죽음의 다이아몬드 문제
가 인터페이스 다중상속시에는 나타나지 않기 때문이다.
(애초에 구현부를 작성하지 않기 때문)
죽음의 다이아몬드 문제
: 여러 클래스를 상속받을 때 같은 식별자 + 같은 종류의 멤버가 있다면, 어떤 것을 상속받을 지 결정할 수 없는 것.
namespace Program
{
interface IA { void A(); }
interface IB { void B(); }
class Child : IA, IB
{
public void A() { Console.WriteLine("A()"); }
public void B() { Console.WriteLine("B()"); }
}
class Program
{
static public void Main(string[] Args)
{
Child child = new Child();
child.A(); // A()
child.B(); // B()
}
}
}
같은 인터페이스를 상속해도, (당연히)각자 자기가 구현한 메소드를 뱉는다.
아래 예시는 다형성뿐 아니라 느슨한 결합
이라는 특징을 보여준다.
namespace Program
{
interface I { void WhoAmI(); }
class Child_A : I
{
public void WhoAmI() { Console.WriteLine("Child_A"); }
}
class Child_B : I
{
public void WhoAmI() { Console.WriteLine("Child_B"); }
}
class Printer
{
public void Print(I instance)
{
instance.WhoAmI();
}
}
class Program
{
static public void Main(string[] Args)
{
Child_A a = new Child_A();
Child_B b = new Child_B();
Printer p = new Printer();
// 다형성
a.WhoAmI(); // Child_A
b.WhoAmI(); // Child_B
Console.WriteLine();
// 느슨한 결함
p.Print(a); // Child_A
p.Print(b); // Child_B
}
}
}
어떤 클래스에 태그를 붙이듯 사용할 수 있다.
namespace Program
{
interface IStudent { }
class Person { public virtual void Say() { } }
class Person_A : Person, IStudent
{
public override void Say()
{
Console.WriteLine("I AM Person_A");
}
}
class Person_B : Person
{
public override void Say()
{
Console.WriteLine("I AM Person_B");
}
}
class Program
{
static public void Main(string[] Args)
{
Person_A a = new Person_A();
Person_B b = new Person_B();
Person[] arr = { a, b };
foreach(Person item in arr) // I AM Person_A
{
if (item is IStudent)
item.Say();
}
}
}
}
메소드 UseCallback()
은 인수로 ISomething형을 받는다.
Isomething인터페이스를 상속한 클래스는 Print()
를 갖고 있을 것이기 때문에, 이를 믿고 호출할 수 있다.
namespace Program
{
interface ISomething { string Print(); }
class Something : ISomething
{
public string Print() { return "SOMETHING"; }
public void CallAnotherMethod()
{
AnotherClass another = new AnotherClass();
another.UseCallback(this);
}
}
class AnotherClass
{
public void UseCallback(ISomething instance)
{
Console.WriteLine(instance.Print());
}
}
class Program
{
static public void Main(string[] Args)
{
Something st = new Something();
st.CallAnotherMethod(); // SOMETHING
}
}
}
인터페이스도 인터페이스를 상속할 수 있다.
클래스 상속이랑 개념은 비슷함.
namespace Program
{
interface IParent
{
void AAA();
}
interface IChild : IParent
{
void AAA(int n); // 오버로드
void BBB(string s);
}
class Something : IChild // IParent, IChild를 모두 받는것이나 마찬가지
{
public void AAA() { Console.WriteLine("hihi"); }
public void AAA(int n) { Console.WriteLine(n); }
public void BBB(string s) { Console.WriteLine(s); }
}
class Program
{
static public void Main(string[] Args)
{
Something something = new Something();
something.AAA();
something.AAA(53);
something.BBB("HIHIHIHIHIHIHIHIHIHIHI");
}
}
}
인터페이스는 구현부 없는 메소드 뿐 아니라, 구현이 있는 메소드도 가질 수 있다.
이를 기본구현 메소드
라고 하는데, 이 메소드는 파생클래스에서 호출할 수 없다.
인터페이스를 상속받은 클래스를, 인터페이스로 업캐스팅 해야 호출할 수 있다.
만약 기본구현 메소드와 같은 식별자로 파생클래스에서 재정의하는 경우, 하이딩과 같이 동작한다.
namespace Program
{
interface I
{
void AAA();
void BBB()
{
Console.WriteLine("BBB() in interface I");
}
}
class Something : I
{
public void AAA() { Console.WriteLine("AAA() in class Something"); }
}
class Something_hasBBB : I
{
public void AAA() { Console.WriteLine("AAA() in class Something"); }
public void BBB() { Console.WriteLine("BBB() in class Something"); }
}
class Program
{
static public void Main(string[] Args)
{
// 기본구현메소드 BBB 가짐
Something obj_1 = new Something();
obj_1.AAA(); // AAA() in class Something
//obj_1.BBB(); // 컴파일에러
((I)obj_1).BBB(); // BBB() in Interface I
Console.WriteLine();
// BBB 덮어씌워짐
Something_hasBBB obj_2 = new Something_hasBBB();
obj_2.AAA(); // AAA() in class Something
obj_2.BBB(); // BBB() in class Something
((I)obj_2).BBB(); // BBB() in class Something
}
}
}
인터페이스는 interface, 추상클래스는 abstract
abstract class Something { }
인터페이스는 public, 추상클래스는 private
기본구현 메소드
override
키워드를 붙여 정의해야 하는 추상메소드
namespace Program
{
abstract class AbstractSomething
{
public abstract int AbstractNum { get; set; }
public int Num { get; set; } = 53;
public abstract void AbstractPrint();
public void Print()
{
Console.WriteLine("Print()");
}
}
class Something : AbstractSomething
{
public override int AbstractNum { get; set; } = 535353;
public override void AbstractPrint()
{
Console.WriteLine("AbstractPrint()");
}
}
class Program
{
static public void Main(string[] Args)
{
Something AA = new Something();
Console.WriteLine(AA.Num); // 53
Console.WriteLine(((AbstractSomething)AA).Num); // 53
AA.Print(); // Print()
((AbstractSomething)AA).Print(); // Print()
Console.WriteLine(AA.AbstractNum); // 535353
Console.WriteLine(((AbstractSomething)AA).AbstractNum); // 535353
AA.AbstractPrint(); // AbsPrint()
((AbstractSomething)AA).AbstractPrint(); // AbsPrint()
}
}
}