클래스 선언과 인스턴스 선언은 타 언어의 객체 지향 프로그래밍과 동일한 것 같다.
class People{
String name = "yjh" // member
void intro (){ // method
print("im ${this.name}")
// class 내 member 지칭 시 this 키워드 사용.
// 만약 해당 intro method 내 name 이라는 이름의 변수 존재 시, this를 꼭 써줘야 한다.
print("im ${name}");
}
}
void main(){
People me = People() // instance
me.intro() // "im yjh"
}
인스턴스 생성 시, 변수 타입을 클래스명으로 지정해, () 를 붙여, 인스턴스화하여, 선언한다.
클래스의 인스턴스를 생성하는 메소드
클래스와 같은 이름을 사용하며, () 내 원하는 매개변수를 입력한다.
class People{
final String name; // 생성자에서 입력받는 변수들은 일반적으로 final
// constructor
People(String name) : this.name = name;
// method
void intro(){
print("im ${this.name}")
}
}
void main(){
People me = People("yjh");
me.intro(); // "im yjh"
People you = People("jbl");
you.intro() // "im jbl"
}
이 외 다르게 생성자를 사용할 수 있다.
class People{
final String name;
final int age;
// constructor
People(this.name) // this 사용 시, 해당 변수에 자동으로 매개변수 저장.
// 여러 개의 constructor
People(String name, int age) : this.name = name , this.age = age;
// named constructor : {클래스명, 네임드 생성자명} 형식
People.fromMap(Map<String, dynamic> map) :
this.name = map['name'], this.age = map['age']
// method
void intro(){
print("im ${this.name}")
}
}
void main() {
People me = People("yjh", 27) // 여러 개의 constructor 사용
people me = People.fromMap({
'name' : "yjh", 'age': 27, // named constructor
})
}
named constructor는 클래스를 여러 방식으로 인스턴스화 할 때 유용하다.
타 언어에서는 class 내부 변수를 칭하지만, 다트에서는 같은 파일에서만 접근 가능한 변수를 말함.
String _name = ""
형태로 , 변수명에 _기호로 시작한다.
class People{
final String name;
final int age;
void hello() { print("hi")}
}
class Student extends People
// extends Constructor
Student(
String name, int age
) : super(name, age) // super(부모 클래스)
부모 클래스 또는 인터페이스에 정의된 메소드 재정의
시 사용
키워드 생략은 가능하나 협엽 및 유지보수 위해선 @override
명시.
상속은 공유 기능을 상속 받는 개념이지만, 인터페이스는 공통으로 필요한 기능
을 정의 해두는 역할
class student implements People {}
상속과의 차이는 기능 상속 시, 재정의가 필요가 없다. 하지만, 인터페이스는 모든 기능을 재정의해 주어야 한다.
특정 클래스에 원하는 기능들만 골라쓰는 기능.
mixin Work on People{
void study(){}
} // Student 클래스 내에 Study라는 method 존재.
class Student extends People with Work{
// mixIn 적용 시에는 with 키워드 사용.
}
인스턴스화가 필요없는 공통 부모 클래스 만들 때 사용. 자식 클래스들에 필수적으로 또는 공통적으로 정의해야하는 메소드가 존재할 때 사용한다.
Service Logic와 동일하다고 생각하면 될 것 같다.
abstract class People {}
class Student implement People
클래스나 함수 정의를 선언 시가 아닌 인스턴스화 또는 실행할 때로 미룬다.
특정 변수의 타입을 하나의 타입으로 제한하고 싶지 않을 때 사용한다.
예시로 setInt() / setString() 함수를 따로 만들지 않도록, (다형성)
<T> 변수 타입 / <E> element 타입 / <K> key / <V> value
클래스 멤버가 인스턴스의 귀속되는 것이 아닌, 클래스 자체에 귀속
(생성자를 통한 값 할당이 불가.)
인스턴스에서, 해당 속성, 메소드를 연속해서 사용.
People a = People("yjh", 27)
..sayHello() // 메소드까지 연속해서 사용
해당 클래스는 미래의 받아올 값 을 뜻한다. (서버 요청 같은 작업)
void main(){
addNum(1,2);
}
void addNum(int num1, int num2){
print('$num1 + $num2' 시작);
}
Future.delayed(Duration(seconds: 3), (){
print(${num1 + num2});
})
print('끝');
//print는 "1 + 2 시작" , '끝' , '3' 순서로 이루어질 것임.
비동기 연산으로, resource를 낭비하지 않고, 다음 코드를 실행함.
위 코드는 문맥상 맞지 않는 순서로 실행되고 있음.
void main(){
addNum(1,2);
}
Future<void> addNum(int num1, int num2) async{ // 비동기 함수 정의
print('$num1 + $num2' 시작);
}
//비동기 함수를 논리적 순서로 실행
await Future.delayed(Duration(seconds: 3), (){ // 대기되고자 하는 비동기 함수
print(${num1 + num2});
})
print('끝');
//print는 "1 + 2 시작" , '3' , '끝' 순서로 이루어질 것임.
동기 프로그래밍처럼 실행되는 것으로 보이겠지만, 비동기 프로그래밍을 유지함.
Future는 단 한 번의 return을 하는 비동기 프로그래밍, 지속적인 값 반환을 위해서는 Stream을 사용한다.
데이터가 한 번 이상 반환되는가?
- Yes
: for, await를 사용해서 표현하는게 효율적인가?
O :await for
X :Stream listen()
- No
: 여러 개의 병렬로 실행할 수 있는 비동기 함수가 존재하는가?
O :await Future.wait()
wait()
은 하나의 Future로 구성된 List를 매개변수로 입력받음.
해당 함수에 입력된 비동기 함수들은 모두 동시 실행되며, 응답값을 요청을 보낸 순서대로 저장해둔다.
X :await
기본적인 Stream 사용법은 이러하다.
import 'dart:async';
void main(){
final controller = StreamController();
final stream = controller.stream // 스트림 가져오기.
//StreamController를 Listen(값이 주입될 때마다, 콜백 실행)
final streamListener = stream.listen((val) {
print(val);
})
}
controller.sink.add(1);
controller.sink.add(2);
controller.sink.add(3);
controller.sink.add(4);
실행결과
1
2
3
4
하나의 스트림으로 여러 번 Listen()을 실행하고 싶다면, 브로드캐스트 스트림
import 'dart:async';
void main(){
final controller = StreamController();
final stream = controller.stream.asBroadcastStream();
final streamListener1 = stream.listen(val){
print('listen1');
print(val)
}
final streamListener = stream.listen(val){
print('listen2')
print(val)
}
controller.sink.add(1);
controller.sink.add(2);
controller.sink.add(3);
controller.sink.add(4);
실행결과
listen1
1
listen2
1
listen1
2
listen2
2
...
}
함수로 Stream return
import 'dart:async';
Stream<String> calc(int num) async*{
for(int i=0; i<5; i++){
//StreamController의 add()처럼 yield 키워드를 이용해서 return
yield 'i = $i';
await Future.delayed(Duration(second: 2));
}
}
void playStream(){
calc(1).listen(val){
print(val);
}
}
void main(){ playStream(); }
실행결과
i=0
i=1
i=2
...
코드팩토리의 플러터 프로그래밍 책을 훑으면서, Dart의 기본문법을 정리해봤다.
기존 사용하던 js, ts와 유사한 개념이 많아, 읽으면서, 필요한 내용만 기록했다.
또한, 모호하고 애매했던 지식들을 채우는데 도움이 된 것 같다.