
메서드 , 생성자 , 제너릭 클래스 , 상속

void function() {
print('저는 함수입니다 !');
}
class Class {
void method() {
print('저는 메서드입니다 !');
}
}
void main() {
function(); // 저는 함수입니다 !
Class object = Class();
object.method(); // 저는 메서드입니다 !
}
method() 는 클래스 안에 있어서 클래스 의존적이지만,
function() 은 클래스 안에 있지 않아서 클래스 와 독립된 친구랍니다 🙂
class Person {
String name = '강미래';
int age = 25;
void introduce() {
print('안녕 ? 나는 $age살 $name !');
}
}
introduce() 가 인스턴스 메서드 에 해당해요.
class Person {
String name = '강미래';
int age = 25;
void printName() {
print(name);
}
void printNameAndAge() {
this.printName();
print(age);
}
}
물론, this 없이 printName(); 도 가능하겠죠 ?
class Person {
String name = '강미래';
int age = 25;
void printName() {
print(name);
}
void printNameAgain() {
printName();
}
}
class Circle {
static double pi = 3.14159;
static void printPi() {
print('원주율은 $pi !');
}
}
printPi() 의 맨 앞에 static 이 붙은 것 보이시나요 ?
static 이 붙은 클래스는 정적 클래스 랍니다 !
class Circle {
static double pi = 3.14159;
double radius = 0;
static void printPi() {
print(pi);
}
void printRadius() {
print(radius);
}
}
void main() {
Circle.printPi(); // 3.14159
Circle.printRadius(); // 오류 발생
}
printRadius() 는 인스턴스 메서드 이기 때문에
클래스 이름으로 접근할 수 없지만,
printPi() 는 정적 메서드 이기 때문에
클래스 이름으로 접근할 수 있어요.
정적 메서드 는 클래스 꺼니까
클래스 이름으로 접근하는게 당연 .. 🙂
class Circle {
static double pi = 3.14159;
double radius = 0;
static void printPi() {
print(pi);
}
void printRadius() {
print(radius);
}
}
void main() {
Circle circle = Circle();
circle.radius = 5;
circle.printRadius(); // 5
circle.printPi(); // 오류 발생
}
printPi() 는 정적 메서드 여서 객체 로 접근할 수 없지만,
printRadius() 는 인스턴스 메서드 여서 객체 로 접근 가능 !
정적 메서드 는 객체 에 종속된 친구가 아니기 때문에
객체 로 접근할 수 없는게 당연 .. 🙂
class Circle {
static double pi = 3.14159;
double radius = 0;
static void printPi() {
print(pi);
}
void printPiAgain() {
this.printPi(); // 오류 발생
}
}
this 는 객체 를 가리키는 키워드인데,
정적 메서드 는 객체에 종속되는 친구가 아니기 때문에
당연히 정적 메서드 에 this 를 못 쓰지 않을까요 ? 🤔
class Circle {
double pi = 3.14159;
double radius = 0;
static void printPi() {
print(pi); // 오류 발생
}
}
class Circle {
static double pi = 3.14159;
double radius = 0;
void printArea() {
print(pi * radius * radius);
}
}
정적 변수 인 pi 를 인스턴스 메서드 에 사용해 주었네요 !
class Person {
String name;
int age;
Person(this.name, this.age);
}
void main() {
Person paul = Person('Paul', 25);
Person mark = Person('Mark', 30);
}
paul 과 mark 모두 Person 클래스로 생성한 객체 랍니다 !
매개변수 생성자 로 생성해준 객체 라는 것도 잊지 말고 체크 👀
class Person {
String name;
int age;
Person(this.name, this.age);
}
void main() {
Person paul = Person('Paul', 25);
Person mark = Person('Mark', 30);
}
paul 과 mark 모두 Person 클래스로 생성한 객체 랍니다 !
매개변수 생성자 로 생성해준 객체 라는 것도 잊지 말고 체크 👀
class Person {
String name;
int age;
Person(this.name, this.age);
}
void main() {
Person paul1 = Person('Paul', 25);
Person paul2 = Person('Paul', 25);
print(paul1 == paul2); // false
}
심지어는 같은 값이 들어갔어도 다른 객체로 인식해요 😱
class Car {
Car();
}
Car() 이 Car 클래스의 기본 생성자 랍니다 !
class Car {
}
void main() {
Car car = Car();
}
기본 생성자 는 생략해 주어도 되고,
class Car {
Car();
}
void main() {
Car car = Car();
}
직접 명시해 주어도 돼요 🙂
class Car {
}
void main() {
Car car = new Car();
}
new 를 붙여도 된답니다 ! (하지만 귀찮으니까 저는 안 붙일래요 ..)
class Car {
String name = '';
List<String> models = [];
Car();
}
name 과 models 에 각각 초기값이 있기 때문에 Good 👍🏼
class Car {
String name; // Error: Field 'name' should be initialized because its type 'String' doesn't allow null.
List<String> models; // Error: Field 'models' should be initialized because its type 'List<String>' doesn't allow null.
Car();
}
name 과 models 에 초기값이 없는 상태로
기본 생성자를 정의하면 이렇게 오류가 나요 😅
class Car {
String name;
List<String> models;
Car(this.name, this.models);
}
Car(this.name, this.models) 를 통해 받아온
this.name 과 this.models 가
Car 의 변수인 name 과 models 에 대입돼요.
class Car {
String name;
List<String> models;
Car(String name, List<String> models)
: this.name = name,
this.models = models;
}
Car(String name, List<String> models) 를 통해 받아온
name 과 models 가
Car 의 변수인 name 과 models 에 각각 대입돼요.
class Car {
String name = '';
List<String> models = [];
Car(String name, List<String> models) {
this.name = name;
this.models = models;
}
}
Car(String name, List<String> models) 를 통해 받아온
name 과 models 가 각각
Car 의 변수인 name 과 models 에 대입돼요.
class Car {
String name;
List<String> models;
Car(this.name, this.models);
}
void main() {
Car car = Car(); // Error: Too few positional arguments: 2 required, 0 given.
}
생성자 에 2개의 매개변수 를 넣어줘야 한다는 오류 메시지가 떠요 !
class Car {
String name;
List<String> models;
Car.fromList(List values)
: this.name = values[0],
this.models = values[1];
}
Car.fromList(List values) 에 있는
values 의 첫번째 요소는 Car 의 인스턴스 변수인 name 에,
values 의 두번째 요소는 Car 의 인스턴스 변수인 models 에
대입돼요.
class Car {
String name;
List<String> models;
Car.fromList(List values)
: this.name = values[0],
this.models = values[1];
void speakName() {
print('저희는 $name 입니다 !');
}
void speakModels() {
print('$models 모델을 가지고 있습니다 !');
}
}
void main() {
Car car = Car.fromList([
'BMW',
['320i', '340i', 'M3']
]);
car.speakName(); // 저희는 BMW 입니다 !
car.speakModels(); // [320i, 340i, M3] 모델을 가지고 있습니다 !
}
‘BMW’ 는 name 에,
['320i', '340i', 'M3'] 은 models 에 대입되겠죠 ?
class Car {
String name;
List<String> models;
Car.fromList(List values)
: this.name = values[0],
this.models = values[1];
}
void main() {
Car car = Car.fromList([
'BMW',
[1, 2, 3]
]); // TypeError: Instance of 'JSArray<int>': type 'List<int>' is not a subtype of type 'List<String>'
}
name 은 String 타입이기 때문에
values 의 첫번쨰 요소에 String 타입의 값을,
models 는 List<String> 타입이기 때문에
values 의 두번째 요소에 List<String> 타입의 값을
넣어줘야 해요.
class Box<T> {
T value;
Box(this.value);
T getValue() {
return value;
}
}
타입 파라미터는 보통 영어 대문자 한글자 (ex. E, T) 로 표현하고,
실제 코드를 실행할 때 타입 파라미터에 실제 데이터 타입을 넣으면 된다고
했던 것 기억하시나요 ? 👀
class IntBox {
int value;
IntBox(this.value);
int getValue() {
return value;
}
}
class StringBox {
String value;
StringBox(this.value);
String getValue() {
return value;
}
}
void main() {
var intBox = IntBox(10);
print(intBox.getValue()); // 10
var stringBox = StringBox('Hello');
print(stringBox.getValue()); // Hello
}
class Box<T> {
T value;
Box(this.value);
T getValue() {
return value;
}
}
void main() {
var intBox = Box<int>(10);
print(intBox.getValue()); // 10
var stringBox = Box<String>('Hello');
print(stringBox.getValue()); // Hello
}
class Person {
void eat() {
print('냠냠 !');
}
}
class Student extends Person {
void study() {
print('열공 !');
}
}
부모 클래스 를 확장한다는 의미에서 extends 라는 키워드를 사용해요.
Person 은 부모 클래스, Student 는 자식 클래스 겠죠 ?
class Person {
String name = '';
void eat() {
print('냠냠 !');
}
}
class Student extends Person {
void study() {
print('열공 !');
}
}
void main() {
Student student = Student();
student.name = '강미래';
student.eat(); // 냠냠 !
student.study(); // 열공 !
}
Student 에는 name 이랑 eat() 를 정의되어 있지 않은데, 사용할 수 있답니다 !
class Person {
void eat() {
print('냠냠 !');
}
}
class Student extends Person {
void study() {
print('열공 !');
}
}
void main() {
Person person = Person();
person.study(); // Error: The method 'study' isn't defined for the class 'Person'.
}
class Person {
String name = '강미래';
void eat() {
print('냠냠 !');
}
}
class Student extends Person {
void eatAndIntroduce() {
super.eat();
print('맛있게 먹는 ${super.name} ㅋㅋ');
}
}
void main() {
Student student = Student();
student.eatAndIntroduce();
}
/*
냠냠 !
맛있게 먹는 강미래 ㅋㅋ
*/
this 는 그 객체 를 가리킨다고 했던 것 기억하시나요 ?
this 랑 비슷하게 super 는 부모 클래스 를 가리킨답니다 !
super.eat(); 을 하면 Person 에 있는 eat() 이 호출되고,
super.name 을 하면 Person 에 있는 인스턴스 변수 인 name 이 들어가요 🙂
class Person {
String name = '강미래';
void eat() {
print('냠냠 !');
}
}
class Student extends Person {
String name = '여러분';
void study() {
print('열공하는 $name !');
}
}
void main() {
Student student = Student();
student.study(); // 열공하는 여러분 !
}
Student 의 name 은 Person 의 name 을 재정의 한 속성 이랍니다 !
Student 의 name 은 ‘강미래’ 대신 ‘여러분’ 으로 재정의 되겠죠 ?
class Person {
void eat() {
print('냠냠 !');
}
}
class Student extends Person {
void eat() {
print('쩝쩝 !');
}
}
void main() {
Student student = Student();
student.eat(); // 쩝쩝 !
}
Person 의 eat() 을 Student 에서 재정의 해서
냠냠 ! 이 아니라 쩝쩝 ! 이 출력되는군요 🙂
class Person {
void eat() {
print('냠냠 !');
}
}
class Student extends Person {
void eat() {
super.eat();
print('쩝쩝 !');
}
}
void main() {
Student student = Student();
student.eat();
}
/*
냠냠 !
쩝쩝 !
*/
super 를 통해 기존에 정의되어 있던 메서드 를 호출한 후에 재정의 하면 돼요 🙂
final class Person {
void eat() {
print('냠냠 !');
}
}
class Student extends Person {
// Error: The type 'Student' must be 'base', 'final' or 'sealed' because the supertype 'Person' is 'final'.
}
상속 당하지 않게 하고 싶은 클래스 의 이름 옆에 final 을 붙이면
그 클래스 를 상속 했을 때 오류가 난답니다 🚨
class BMW {
String name = 'BMW';
List<String> models = ['320i', '340i', 'M3'];
void introduceName() {
print('안녕하세요, 저희는 $name 입니다 !');
}
void introduceModels() {
print('저희는 $models 이렇게 ${models.length}가지 모델을 가지고 있습니다 !');
}
}
class Benz {
String name = 'Benz';
List<String> models = ['A-Class', 'C-Class', 'E-Class', 'S-Class'];
void introduceName() {
print('안녕하세요, 저희는 $name 입니다 !');
}
void introduceModels() {
print('저희는 $models 이렇게 ${models.length}가지 모델을 가지고 있습니다 !');
}
}
void main() {
BMW bmw = BMW();
bmw.introduceName(); // 안녕하세요, 저희는 BMW 입니다 !
bmw.introduceModels(); // 저희는 [320i, 340i, M3] 이렇게 3가지 모델을 가지고 있습니다 !
Benz benz = Benz();
benz.introduceName(); // 안녕하세요, 저희는 Benz 입니다 !
benz.introduceModels(); // 저희는 [A-Class, C-Class, E-Class, S-Class] 이렇게 4가지 모델을 가지고 있습니다 !
}
이렇게 매번 클래스를 생성해 줄 수도 있지만 중복 코드가 생겨서 효율적이지 않겠죠 😅
class Car {
String name;
List<String> models;
Car(this.name, this.models);
void introduceName() {
print('안녕하세요, 저희는 $name 입니다 !');
}
void introduceModels() {
print('저희는 $models 이렇게 ${models.length}가지 모델을 가지고 있습니다 !');
}
}
void main() {
Car bmw = Car('BMW', ['320i', '340i', 'M3']);
print(bmw.name); // BMW
print(bmw.models); // [320i, 340i, M3]
bmw.introduceName(); // 안녕하세요, 저희는 BMW 입니다 !
bmw.introduceModels(); // 저희는 [320i, 340i, M3] 이렇게 3가지 모델을 가지고 있습니다 !
Car benz = Car('Benz', ['A-Class', 'C-Class', 'E-Class', 'S-Class']);
print(benz.name); // Benz
print(benz.models); // [A-Class, C-Class, E-Class, S-Class]
benz.introduceName(); // 안녕하세요, 저희는 Benz 입니다 !
benz.introduceModels(); // 저희는 [A-Class, C-Class, E-Class, S-Class] 이렇게 4가지 모델을 가지고 있습니다 !
}
우리는 효율을 중요하게 생각하는 개발자니까
객체 지향 프로그래밍 을 통해서 효율적인 코드를 만들어 보자구요 💪🏼