Virtual와 Abstract는 아직도 많이 헷깔리는 녀석들입니다.
그래서 오늘은 마음먹고 정리...
'-^
기본적으로 Virtual_Abstract 클래스와 이 클래스를 상속받는 SubClass 클래스를 만들어 줍니다.
두 클래스에는 모두 간단한 출력을 해주는 getString() 메서드가 있습니다.
using System;
namespace TestApplication
{
class Virtual_Abstract
{
public Virtual_Abstract()
{
Console.WriteLine("[부모에서 생성]");
}
public Virtual_Abstract(int i)
{
Console.WriteLine(string.Format("[부모에서 인자{0}받아 생성]", i));
}
public void getString()
{
Console.WriteLine("[부모에서 호출]");
}
}
class SubClass : Virtual_Abstract
{
public SubClass()
{
Console.WriteLine("[자식에서 생성]");
}
public SubClass(int i)
{
Console.WriteLine("[자식에서 인자{0}받아 생성]", i);
}
public void getString()
{
Console.WriteLine("[자식에서 호출]");
}
}
class MainClass
{
static void Main(string[] args)
{
SubClass sc = new SubClass();
sc.getString();
Console.WriteLine("--------------");
SubClass sc2 = new SubClass(11);
sc2.getString();
Console.ReadLine();
}
}
}
이걸 실행하면
이렇게 나오네요
각 클래스에는 두개의 생성자가 있는데
기본생성자와 int파라미터를 받는 생성자가 있어요
출력된걸 보면 일단 부모클래스의 기본 생성자는 무조건 실행되는걸 알수 있죠
그리고 getString()한 결과를 보면 의도(?)대로 출력된걸 알수 있습니다.
문제 없이 잘 출력된듯 하지만
경고가 한 개 발생하는데 그 내용은
'TestApplication.SubClass.getString()'은(는) 상속된 'TestApplication.Virtual_Abstract.getString()'
멤버를 숨깁니다. 숨기려면 new 키워드를 사용하십시오.
아무런 작업없이 자식클래스에서 부모클래스와 같은 메서드를 만들어서 생긴 문제로
경고는 뜨지만 실행은 잘 됩니다.
실행이 되는 이유는 내부적으로(?) 컴파일러가(?) 해당 메서드에 new키워드를 붙였기 때문이구요
실제로 SubClass의 getString()메서드에 new를 붙이면 같은 경고없이 같은 결과를 볼수 있습니다.
getString은 자식 클래스에서 재정의된 메서드인데. 메서드를 재정의 할때는 new키워드 말고도 사용할수 있는게 override가 있죠
그럼 new와 override의 차이는 뭔까요?
new 대신 override를 사용해서 실행해 볼께요
그러면 다음과 같은 오류(!)가 발생합니다.
'TestApplication.SubClass.getString()': 상속된 'TestApplication.Virtual_Abstract.getString()' 멤버는
virtual, abstract 또는 override로 표시되지 않았으므로 재정의할 수 없습니다.
부모 클래스의 getString메서드가 virtual이나 abstract 메서드가 아니므로 재정의 할수 없다는 거네요.
그럼 부모 클래스의 getString메서드를 바꿔볼께요. virtual로 해보겠습니다.
new키워드를 쓴것과 차이가 없네요.
차이가 나는 결과를 보려면
Main클래스에서 getString하는 부분을 바꿔야 합니다.
(sc as Virtual_Abstract).getString();
그리고 실행을 해보면
new
override
getString을 자식클래스 객체에서 호출하는게 아니라
부모 클래스로 업캐스팅후 호출했을때는
두 결과가 틀리게 나옵니다.
new를 했을때는 부모클래스의 getString이 호출되고
override했을때는 자식클래스의 getString이 호출됩니다.
이유는?
new키워드를 사용해 재정의 하면 자식클래스에서 부모클래스의 해당 메서드를 숨깁니다.
뭐. 관계를 끊는다고 생각하면 될꺼 같아요.. (맞을라나.. -_-ㅋ)
그래서 부모클래스로 업캐스팅해서 getString을 호출하면 부모클래스이 getString이 호출되고
override했을경우에는 getString이 재정의 된 게 있다는걸 알기 때문에 업캐스팅이 되었지만 자식클래스의 getString이 호출되는거라고 생각합니다… -_-v
(MSDN을 참고해보면 getString이 호출될 때 컴파일러가 일단 재정의된 메서드가 있는지를 찾아본다고 하네요.)
그럼 이제
해당 메서드를 abstract로 정의해볼께요
메서드를 추상으로 만들면
클래스도 추상클래스여야 합니다.(안그러면 오류가 빠방!)
그리고 해당 메서드는 반드시 자식클래스에서 재정의 해줘야 합니다.
그냥 정의하면 안되고 재정의 해줘야 하고
new로는 안되고 override해줘야 합니다.
(안그럼 다 오류나요.. )
실행결과는
메서드를 virtual로 해서 override해준것과 같은 결과가 나옵니다.
(업캐스팅해서 호출해도 override한거처럼 나와요)
여기서 궁금한거…
아직도 잘 모르겠지만
그렇다면 abstract 메서드와 interface와 뭐가 틀린걸까요..
아….
검색해서 찾아보면
‘is와 as’의 차이로 생각하면 된다고 하는데
제 나름은 이렇게 결론냈습니다.
Abstract로 정의한 메서드는
종속관계(부모자식관계)에서 자식이 가져야 할 행동(메서드)를 강제하는것이고(무조건 재정의 해야 하니까요)
Interface는
특정 행동들을 강제하는것이다..
-_-ㅋ
좀 애매한데..
나름 예를 들어보면
동물(부모클래스)이 있고 멍멍이(자식클래스)와 오리가 있습니다.
이 동물은 모두 걸어댕기죠
하지만 멍멍이가 걷는것과 오리가 걷는건 좀 모양새가 틀립니다.
예를 들어보면
class Animal
{
public void Move()
{
}}
이런거죠..
근데 자식들이 Move하는게 틀리니까
이놈을
Abstract로 하는겁니다.
그렇게 하면
자식들은 무조건 move해야하지만
실제로 move하는 방법은 틀리게 할수 있는거죠
하지만 interface로도 같은 일을 할수 있죠
interface Animal
{
void Move();
}
이런 interface를 만들어서
이놈을 상속받으면 똑 같은 일을 할수 있죠
하지만
멍멍이와 오리가 아예 똑 같은 행동을 하는게 있다면
부모클래스에서 공통 부분을 만들어서 상속을 하는게
효과적이겠죠
그래서 제가 내린 정의(?)는
Abstract는
자식 클래스가 해야 할걸 정해주는거고
Interface는
해야할걸 정해주는거…
이렇게 생각합니다. 크크
그래서 interface는 복수상속이 가능하잖아요 크크
뭐 또 예를들어보면
Interface로 걷기, 말하기, 등등등 을 정의해놓고
가져다 쓰는거죠
아. 이놈은 무조건 걷는건 있어야해.. 하면
걷기 interface를 상속받아서 구현하고 뭐 그런..ㅎㅎ
그래서 오늘 이 둘을 합치는걸 알았습니다. -_-(MSDN만쉐..)
부모 클래스에서 interface를 상속받아서
그놈을 abstract로 만들어서 자식에서 구현..
이런게 있드라구요
사실 엄청 고민했던거였는데. ㅠ-ㅠ
이런걸 오늘 알았따는게 참 부끄럽네요.. 흐흑
RECENT COMMENT