[Dart 심화] 개인과제를 풀기 위해 강의 예제를 보고 클래스의 기초 개념부터 다시 짚어보고 가기로 했다.
클래스를 구성하고, 부모 클래스로 부터 상속받는 자식 클래스를 구현하는 방법
특정 객체를 생성하기 위해 변수와 메소드를 정의하는 일종의 박스 개념
객체를 정의하기 위한 메소드와 변수로 구성
클래스: 박스를 만드는 생산라인
객체: 생산라인에서 만들어진 박스
// 예제) 어떤 사람에 대한 이름과 나이를 출력하기
class Person {
// 속성. 이름과 나이를 적어준다.
String name;
int age;
// 생성자: 클래스를 생성(객체로 만드는 일)할 때 속성 값을 초기화 하는 함수
// 박스를 만들어 준 것.
Person(this.name, this.age);
// 메서드: 클래스 안의 함수
void printInfo() {
print('내 이름은 $name! $age살 이지!');
}
}
void main() {
// 객체화
Person joy = Person("조이", 33);
// 생성자를 호출해서 파라미터 순서대로 넘겨줘서 완성
// 객체(= person에)를 변수(joy)에 담고 앞에 타입(클래스. Person)을 적어주면 된다.
}
클래스를 생성(객체로 만드는 일)할 때 초기화(속성에 값을 할당)하는 특별한 함수
class Person {
String name;
int age;
// 기본생성자
Person(this.name, this.age);
}
class Person2 {
String name;
int age;
bool? isMale;
// 네임드 파라미터를 사용하는 기본생성자
Person2({required this.name, required this.age, this.isMale});
}
void main() {
Person p1 = Person("홍길동", 30);
// Person p2 = Person(30, "홍길동"); 불가능함
Person2 pp = Person2(name: "홍길동", age: 30);
Person2 pp2 = Person2(age: 30, name: "홍길동"); // 가능함
Person2(name: "홍길동", age: 30); // 가능함
Person2(name: "홍길동", age: 30, isMale: true); // 가능함
// Person2(name: "홍길동", isMale: true); // 불가능함
}
이름이 지정되어 있는 생성자. 코드의 가독성을 높이고 특정 목적에 맞는 객체 생성에 용이함
class Person {
String name = "홍길동";
int age = 30;
Person();
Person.student() : name = "김민지", age = 20;
//student라고 이름을 붙인 생성자
}
void main(List<String> school) {
Person student = Person.student();
// 클래스 뒤에 이름을 만든 생성자를 붙여서 쓰면 된다.
}
//클래스
class Car { // 자동차를 만드는 설계도
int wheel = 4; //속성(변수). 바퀴는 4개
void drive(){ // 메서드(함수). 운전을 한다
print("부릉부릉!");
}
}
void main() {
//생성자. 자동차를 실제로 만든다. Car(); 객체
Car c = Car(); // 객체를 클래스(Car)를 담는 그릇(c)에 객체(Car();)를 담아준다.
c.drive(); // 실행. 부릉부릉! 정상출력
ElectricCar c2 = ElectricCar(); // 2 c2라는 객체를 만들어서
c2.drive();
c2.charge(); // 2 실행. 부릉부릉! 충전중입니다 정상출력
}
// 2 설계도를 토대로 전기차를 만든다면
class ElectricCar extends Car { // extends: 상속
// 자동차가 가진 모든걸 물려받을 수 있다. 상속을 통해 부모클래스로 부터 받았다.
// 똑같은 내용을 적을 필요 없어져서 지워줌.
/*
int wheels = 4;
void drive() {
print("부릉부릉!");
}
*/
int batteryLevel = 100;
void charge() {
print("충전중입니다");
}
// 3 이렇게 상속받은 클래스에서는 부모가 가지고 있는 메서드를 다시 정리할 수 있다. = override한다
@override
void drive() { // 아래 파란줄이 뜨는데 우클릭해서 Quick Fix -> @override
print("지이잉"); // 3 실행. 부릉부릉! 지이잉 충전중입니다 정상출력
}
}
//다트 생성자를 정의하지 않으면 파라미터가 아무것도 들어가지 않은 생성자는 기본으로 둔다. Car();
// @로 시작하는 것을 어노테이션이라고 함. 코드를 위한 메타데이터
// @override => 실행에 영향을 주지 않아서 없어도 상관없음. 다른 개발자한테 재정의 했다고 명시적으로 알려주려고 쓰는 것!
class Car {
int wheel = 4;
void drive(){
print("부릉부릉!");
}
}
void main() {
Car c = Car();
c.drive();
Car c2 = ElectricCar(); // 3 상속으로 모든 전기차는 자동차이다. 라고 되었기 때문에 'class ElectricCar extends Car'
// 전기차를 상속받은 부모클래스 Car로 타입을 바꿔줌.
// 단, 이렇게 바꿔주면 아래 자녀클래스가 가진 메서드를 사용할 수 없어서
c2.drive();
if(c2 is ElectricCar) { // 이때, 타입을 안전하게 다시 ElectricCar로 변경해주는 방법이 있다.
(c2 as ElectricCar).charge(); // (c2를 ElectricCar로)강제로 바꾼 후 메서드(.charge();)를 호출할 수 있게 된다.
}
}
class ElectricCar extends Car {
int batteryLevel = 100;
void charge() {
print("충전중입니다");
}
@override
void drive() {
print("지이잉"); // 아까와 같이 정상출력 됨
}
}
// is : 타입을 확인하는 키워드. (여기서는 위의 타입이 맞으므로 true기 때문에 실행됨)
// as : 타입을 강제로 바꾸는 키워드.
클래스 앞에 abstract 키워드를 사용하고
미완성 메소드(추상 메소드)를 포함할 수 있으며
직접 객체를 생성할 수 없고
반드시 상속을 통해 완성해야 하는 클래스
// 자동차 라는 표준을 잡음. 생성자를 사용할 수 없기 때문에 main에서 만들어준다.
abstract class Car {
int wheel = 4;
void drive(){
print("부릉부릉!");
}
void turnSignal(); // 깜빡이. 중괄호가 없는 메서드 = 추상메서드. 아래에서 만들어 줌
}
class Audicar extends Car{
@override
void turnSignal() {
print(">>>");
print("<<<");
}
}
class TeslaCar extends Car{
@override
void turnSignal() {
print("<<<");
print("]]]");
}
}
void main() {
// Car(); -> 생성자 사용불가. 빨간줄 뜸 객체로 만들 수 없어서 반드시 상속을 통해야 한다.
Car car1 = Audicar(); // 모든 자동차는 아우디가 아니지만 아우디는 자동차에 속한다. 상위호환이 가능함. 부모클래스로 타입을 물려받음
Car car2 = TeslaCar();
car1.turnSignal();
car2.turnSignal(); // 실행. 정상적으로 출력됨
}
클래스의 구조를 이해하고 상속하는 방법에 대한 개념을 익혔다.