동기
인터넷에 Dart 상속에 대해서 쓴 글들이 많지만 부모 클래스와 자식 클래스의 생성자가 동시에 필요할 때 어떻게 해야 하고, super와 this의 구체적인 작동 방식에 대해서 다루는 글이 없어서 기록해 보게 되었다.
오랫동안 생각하고 java의 상속에 대해 비교해가면서 작성하였으므로 참고하여 도움이 되었으면 한다.
Dart 상속에 대해서
다른 언어들과 마찬가지로 상속에 대한 개념은 같다.
부모(상위) 클래스의 기능, 멤버 등을 자식(하위) 클래스가 물려받는 것이다.
그런데 Dart에서는 상속을 다루는 법이 약간 특이하다.
Java에서의 상속은 아래 코드와 같다.
class Cal {
int left, right;
//기본 생성자
public Cal(){} // 최소한 하나의 생성자가 있어야 자식 클래스에서 생성자를 만들 수 있다!!! 중요
// 기본 생성자가 없을 경우 super(left, right);로 인자가 있는 생성자를 호출해줘야함
// 자식클래스에서 무조건 부모 클래스 생성자가 한번은 실행되어야한다는 것
public Cal (int left, int right) { // 생성자를 통해 Cal 클래스의 left, right 값 초기화
this.left = left;
this.right = right;
}
public void sum() {
System.out.println(this.left + this.right);
}
}
class SubCal extends Cal {
public SubCal(int left, int right){
//super(left, right); //특이한점 super를 쓴다면 super가 가장 먼저 나와야한다. 뒤에서 사용하면 컴파일오류 발생
this.left = left; // 상속받은 멤버는 this. 붙임
this.right = right;// 상위 생성자를 호출하는 super(left, right);로 이 두 줄은 대체 가능하다.
}
public void Sub(){
System.out.println(this.left - this.right);
System.out.println(super.left); // 이해가 안되는부분 => this.left와 super.left의 값이 왜 같지?
//생성자가 자동으로 둘다 초기화? 답:자식 클래스에 left가 없으므로 여기서 this는 부모클래스의 left를 가리킨다.
//하지만, 자식 클래스의 left가 있다면 this.left는 자식 클래스의 left를 가리킨다.
}
}
class Calculator{
public static void main(String[] args){
Cal c1 = new Cal(2, 3);
c1.sum();
SubCal c2 = new SubCal(2, 3);
c2.Sub();
c2.sum();
}
}
Dart에서도 super를 사용하여 자식 클래스의 생성자 정의가 가능하다. 하지만 Java와는 달리 부모 클래스의 생성자에 인자가 있을 경우 super를 필수적으로 사용하여야 한다. 또한, 생성자의 초기화 리스트(Initialize list)에서 가장 마지막에 super를 사용하여야 한다.
안 그러면 컴파일 오류가 발생한다.
또한, super.age = age; 이런 방식으로 초기화를 하지 못하는 이유는 Java와 다르게 Dart에서 멤버의 초기화는 해당 멤버에 속한 클래스에서만 이루어져야 하기 때문이다. 그래서 super(age, name) 처럼 부모 클래스의 생성자를 통해 초기화하는 것이다.
void main(List<String> args) {
Person me = new Person(21, '준범');
Student you = new Student(6, '승아', '유치원');
me.showName();
you.showName();
you.showSchool();
you.prf();
}
class Person {
int age = 0;
String name = 'Default';
Person(int age, String name) {
this.age = age;
this.name = name;
}
void showName() {
print(this.name);
}
}
class Student extends Person {
String school = 'default';
int age = 0;
// 초기화 리스트로 super을 사용하고 나머지는 생성자 내부에서 처리하는 생성자
// Student(int age, String name, String school) : super(age, name) {
// this.school = school;
// }
// 초기화 리스트만 이용한 생성자
Student(int age, String name, String school)
: this.school = school,
super(age, name);// super가 마지막에 위치해야함
void showSchool() {
print(this.school);
}
void prf() {
print('상위 클래스의 age : ${super.age}');
print('하위 클래스의 age: ${this.age}');
}
}
그리고 Java와 비슷한 점은 this의 경우 하위 클래스의 멤버가 존재하지 않으면 super와 같이 상위 클래스의 멤버를 가리킨다는 것이다.
Student Class의 prf() 함수의 출력값을 보면 이를 알 수 있다.
위 코드를 그대로 실행할 경우에는 아래와 같이 출력되고,
준범
승아
유치원
상위 클래스의 age : 0
하위 클래스의 age: 6
Student class의 int age = 0;을 지우고 실행하면 아래와 같은 값이 출력됨을 알 수 있다.
준범
승아
유치원
상위 클래스의 age : 6
하위 클래스의 age: 6
하지만 Java와 다른점도 있는데 위의 예시처럼 그냥 값을 불러올 때는 this.age가 super.age를 가리킬 수도 있지만, Java와 달리 초기화할 때에는 자식 클래스의 age가 존재하지 않아도 this.age는 무조건 해당 클래스(자식클래스의 age)를 가리키므로 존재하지 않는 age변수를 찾을 수 없어 오류가 발생한다.
실제로 Student의 초기화 리스트에 this.name = name, 을 추가해보면, this.name이 해당 클래스에 들어있지 않다는 오류가 발생하게 된다.
왜 이렇게 헷갈리게 만들어놨는지 이걸 알아내는데에만 5시간 넘게 쓴 거 같다.
'Programming' 카테고리의 다른 글
Tag Counter : 데이터셋에서 csv 태그 형식의 라벨을 카운팅 하는 프로그램 (0) | 2023.02.18 |
---|---|
(UAC 우회) 윈도우 로그오프 상태에서 프로그램을 자동으로 실행하는 방법 (0) | 2022.05.14 |
Selenium 으로 만든 11번가 구매 매크로 (0) | 2022.05.14 |