인프런 무료 강의
https://www.inflearn.com/course/lecture?courseSlug=dart-%EC%96%B8%EC%96%B4-%EC%9E%85%EB%AC%B8&unitId=107600
DartPad
https://dartpad.dev/
객체지향 프로그래밍 (OOP : Object Oriented Programming)
void main(){
Test test = Test();
}
class Test {} // (1)
class Test extends Object {} // (2)
간단히 말하자면,
(1)
과 (2)
는 똑같다.
extends Object
가 생략된 것. (부모 클래스인 Object
가 extends
된 사실)
☝🏻 그래서 Object
가 가지고 있는 기본적인 속성 4가지를
기본으로 탑재하고 있다고 생각하면 된다.
Class
인 Idol
생성final
/ const
사용class Idol {
final String name;
final List<String> members;
// constrouctor (생성자)
const Idol(String name, List<String> members)
: this.name = name,
this.members = members;
void sayHello() => print('우리는 ${this.name}');
void introduce() => print('안녕하세요 저희는 ${this.name}입니당!');
}
위 class
를 상속받아 instance
를 만들어보자.
void main() {
// const constructor 로 immutable 하게! 효율을 높여준대 (이유는 Flutter 에서)
// const 로 선언하면, 같은 주소값을 참조해서 값이 엄격일치됨! 같은 Instance가 됨
Idol winner = const Idol('위너', ['진우', '민호', '승훈', '승윤']);
winner.sayHello(); // 우리는 위너
winner.introduce(); // 안녕하세요 저희는 위너입니당!
Idol blackpink = Idol.fromList([['제니','리사','로제','지수'], '블랙핑크']);
blackpink.sayHello();
blackpink.introduce();
}
instance
를 생성할 때, new Idol()
이런식으로 생성자 함수를 쓰는데, Dart
에서는 'new'
생략 가능!class
에서 immutable(final/const
)로 선언했다면, instance
에서도 const
로 선언해야 함const
로 선언하면 같은 주소값을 참조해서 값이 엄격일치된다.constructor
는 이러한 방식으로 만들 수도 있다. 👇🏻
// constrouctor (생성자)
const Idol(String name, List<String> members)
: this.name = name,
this.members = members;
// 👇🏻 위 방법보다, 더 간결한 방법
// 이렇게 써도 됨 (이미 타입은 위에 지정하기 때문)
const Idol(this.name, this.members);
// 이런 방법도 있음
// 여기엔 const 를 안붙였으므로, 생성할 때 const 붙이면 에러남
Idol.fromList(List values):
this.members = values[0], this.name = values[1];
void main() {
Idol winner = const Idol('위너', ['진우', '민호', '승훈', '승윤']);
winner.sayHello(); // 우리는 위너
winner.introduce(); // 안녕하세요 저희는 위너입니당!
Idol blackpink = Idol.fromList([['제니','리사','로제','지수'], '블랙핑크']);
blackpink.sayHello(); // 우리는 블랙핑크
blackpink.introduce(); // 안녕하세요 저희는 블랙핑크입니당!
}
private
속성을 부여하고 싶다면, 앞에 underscore(_
) 붙여주자.import
해도 사용할 수 없음getter
: 데이터 가져올 때setter
: 데이터 설정할 때// 부모 class 선언
class _Idol {
String name;
List<String> members;
_Idol(this.name, this.members);
void sayHello() => print('우리는 ${name}');
void introduce() => print('안녕하세요 저희는 ${name}입니당!');
// getter
String get firstMember {
return members[0];
}
// setter
set firstMember(String name) {
members[0] = name;
}
}
void main() {
_Idol winner = _Idol('위너', ['진우', '민호', '승훈', '승윤']);
winner.firstMember = '앙두';
print(winner.firstMember); // 앙두
print(winner.members); // [앙두, 민호, 승훈, 승윤]
}
setter
로 데이터를 설정하여, getter
로 설정한 데이터를 가져온다.getter / setter
잘 이해안됐다..class
의 모든 속성을class
가 모두 부여받는다.class Idol {
String name;
int membersCnt;
// Constructor
// named parameter
Idol({
required this.name,
required this.membersCnt,
});
void sayName() => print('우리는 ${this.name}!');
void sayCnt() => print('우리는 ${this.membersCnt}명의 멤버가 있어!');
}
// 클래스 BoyGroup은 Idol의 속성들을 모두 상속받아 사용할 수 있다.
class BoyGroup extends Idol {
// 부모 클래스를 지칭하는 건 'super'
// 부모의 constructor 와 일치시켜줘야 한다
// positional parameter
BoyGroup(String name, int membersCnt)
: super(name: name, membersCnt: membersCnt);
void sayMale() => print('(남자 아이돌)');
}
// 클래스 GirlGroup Idol 의 속성들을 모두 상속받아 사용할 수 있다.
class GirlGroup extends Idol {
// 부모 클래스를 지칭하는 건 'super'
// 부모의 constructor 와 일치시켜줘야 한다
// positional parameter
GirlGroup(String name, int membersCnt)
: super(name: name, membersCnt: membersCnt);
void sayFemale() => print('(여자 아이돌)');
}
void main() {
Idol winner = Idol(name: '위너', membersCnt: 5);
print('========winner=======');
winner.sayName(); // 우리는 위너!
winner.sayCnt(); // 우리는 5명의 멤버가 있어!
BoyGroup bigbang = BoyGroup('빅뱅', 4);
print('========bigbang=======');
bigbang.sayName(); // 우리는 빅뱅!
bigbang.sayCnt(); // 우리는 4명의 멤버가 있어!
bigbang.sayMale(); // (남자 아이돌)
GirlGroup sistar = GirlGroup('씨스타', 4);
print('========sistar=======');
sistar.sayName(); // 우리는 씨스타!
sistar.sayCnt(); // 우리는 4명의 멤버가 있어!
sistar.sayFemale(); // (여자 아이돌)
print('=======Type Comparison=======');
// Idol을 상속받은 자식 클래스는
// 자기자신이 될 수도 있고, 부모 클래스가 될 수도 있다.
print(bigbang is Idol); // true
print(bigbang is BoyGroup); // true
print(sistar is Idol); // true
print(sistar is GirlGroup); // true
}
method
: function
(class
내부에 있는 함수)override
: 덮어쓰다 (우선시하다)// 부모 class 선언
class TimesTwo {
final int number;
TimesTwo(this.number);
// method
int calculate(){
return number * 2;
}
}
// extends 로 부모 class 를 상속
class TimesFour extends TimesTwo {
// 부모를 지칭하는 super 를 사용함으로써, 부모의 number 가 됨
TimesFour(int number): super(number);
// @override 를 안적어도 작동은 되지만, 직관적이기 위해 적어주자!
int calculate(){
return number * 4;
}
// 위와 동일. override를 사용. 부모 메서드를 덮어쓴 것.
int calculate(){
return super.calculate() * 2;
}
}
void main(){
TimesTwo two = TimesTwo(2);
print(two.calculate()); // 4
TimesFour four = TimesFour(2);
print(four.calculate()); // 8
}
static
은 instance
에 귀속되지 않고 class
에 귀속된다.class Employee {
// class 내의 고정값이 되어, 인스턴스에서 귀속되지 않고, class로 다이렉트로 값이 먹힘
static String? building;
// final 로 선언하면 변경 불가
final String name;
Employee(this.name);
void printNameAndBuilding() =>
print('제 이름은 $name입니다. $building 빌딩에서 근무하고 있습니다.');
static void printBuilding() => print('저는 $building 건물에 있습니다.');
}
위에서 building
을 따로 값을 지정해주지 않았음.
void main() {
Employee hj = Employee('현주');
// 값을 설정해놓으면, 어떤 인스턴스던, 이 값이 들어감
Employee.building = '위워크';
hj.printNameAndBuilding(); // 제 이름은 현주입니다. 위워크 빌딩에서 근무하고 있습니다.
}
building
값을 할당해주면, 생성된 instance
마다 저 값이 자동으로 들어감.class
가 속성들을 물려주는 것이라면,interface
는 특정 구조를 강제하는 것 (목적이 instance
화가 아님)abstract
를 붙여줌으로 구별interface
를 상속받을 때는 implements
를 사용// interface
// abstract(추상) 를 붙여주면 'instance로 쓰지말고, 설계도로만 봐라' 라는 의미
abstract class IdolInterface {
String name;
IdolInterface(this.name);
// 어떤 형태인지만 구조를 정해놓는 것이기 때문에, 함수가 있다면 body를 제외해도 됨
void sayName();
}
// extends 는 class 를 상속받을 때
// implements 는 interface 를 상속받을 때
// interface 와 설계틀을 똑같이 맞춰줘야 한다.
class GirlGroup implements IdolInterface {
String name;
GirlGroup(this.name);
void sayName() {
print('우리는 여자아이돌 그룹, $name 이야.');
}
}
// interface 와 설계틀을 똑같이 맞춰줘야 한다.
class BoyGroup implements IdolInterface {
String name;
BoyGroup(this.name);
void sayName() {
print('우리는 남자아이돌 그룹, $name 이야.');
}
}
interface
구조와 똑같이 맞춰줘야 에러가 나지 않는다.void main() {
GirlGroup sistar = GirlGroup('씨스타');
BoyGroup bigbang = BoyGroup('빅뱅');
sistar.sayName(); // 우리는 여자아이돌 그룹, 씨스타 이야.
bigbang.sayName(); // 우리는 남자아이돌 그룹, 빅뱅 이야.
}
Type
을 외부에서 받을 때 사용<>
안에 아무 대문자나 넣어두 됨class Lecture<T> {
// 제너릭 T 자리에 들어오는 type에 따라 id type이 정해짐
// => class 내 변수의 type을 외부에서 받아 사용
final T id;
final String name;
Lecture(this.id, this.name);
void printIdType() {
print(id.runtimeType);
}
}
// 제너릭 타입을 2, 3개 무한히 넣어줄 수 있음
// 대신 다 외부에서 만들 때, 설정해주면 됨
class Lecture2<T, A> {
final T id;
final A name;
Lecture2(this.id, this.name);
void printIdType() {
// Type 검사
print(id.runtimeType);
print(name.runtimeType);
}
}
void main() {
Lecture<String> math = Lecture('01', 'math');
Lecture<int> science = Lecture(07, 'science');
math.printIdType(); // String
science.printIdType(); // int
Lecture2<int, String> social = Lecture2(22, 'social');
social.printIdType(); // int, string
}