oop라는 명칭이 생겨난 이유는 모든 클래스가 기본적으로 object를 extend 하기 때문이다. Object라는 최상위 클래스가 제공해주는 모든 파라미터들을 모든 클래스들이 사용할 수 있다는 의미다. 대부분의 언어에서는 이 Object라는 클래스에 A 인스턴스와 B 인스턴스를 비교하는 알고리즘이 정의되어 있다. Dart 에서는 operator라는 함수에 정의 되어있고, 이를 override 함으로써 값 비교 알고리즘을 자유롭게 변경할 수 있다.
Hash Code
hashcode 함수는 Map 또는 Set에서 키의 역할을 하게 된다. Map이나 Set은 키가 중복으로 저장될 수 없기 떄문에 Set, Map의 키로 Object가 저장되었을 때 어떻게 키값을 정의할지가 중요하다.
프로퍼티(변수)가 늘어나면 늘어날수록 operator와 hashcode를 작성하는것이 귀찮아진다. dart에서는 equatable클래스를 상속받아 이 문제를 해결하고 있다.
Equatable 클래스를 상속받고 props 라는 메소드를 override 해주면 된다.
Mixin은 " Mixins are a way of defining code that can be reused in multiple class hierarchies. (여러 클래스 계층에서 코드 정의한 부분을 재사용하기 위한 방법 중 하나이다.)" 공식문서에 표현되어 있다.
- Dart에서는 Interface라는 키워드 대신 class를 사용하여 인터페이스를 정의한다.
- Dart 3.0 부터는 interface class로 선언할 수 있으며 implement만 가능하다.
- 인스턴스 생성을 못하게 abstract 키워드를 붙이고, 메소드 정의만 하도록 한다.
- 인터페이스 구현은 implements 를 사용한다.
abstractclass Food {
String? name;
void printName(); // 메소드 정의
}
class Fruit implements Food {
String? name;
Fruit(String name) : this.name = name;
void printName() { // 메소드 구현
print('Fruit name is ${this.name}!');
}
}
void main() {
Fruit fruit = Fruit('Apple');
fruit.printName();
}
클래스 확장, 인터페이스 구현, 추상 클래스 외에도 다트는 믹스인(mixin) 개념을 제공하고 여기서 with 키워드를 사용한다.
Mixins
- 믹스인은 여러 클래스 계층에서 클래스의 코드를 재사용하는 방법이다. - with 키워드를 사용하면 상속하지 않고 다른 클래스의 기능을 가져오거나 override 할 수 있다. - 믹스인을 구현하려면 생성자를 선언하지 않는 클래스를 만든다. - 믹스인을 일반 클래스로 사용하려면 class 대신 mixin 키워드를 사용한다.
다트에서 비동기에 관련된 핵심 클래스는 Future 와 Stream 이며, async 와 await 키워드와 함께 사용한다.
다트 코드는 단일 쓰레드에서 실행된다.
Future 클래스
비동기 함수는 Future 클래스를 리턴한다. 이 Future 클래스는 미래에 비동기 작업이 끝나면 값을 받아오는 클래스이다 Future클래스에는 콜백을 2개 등록할 수 있다. - 비동기 함수가 성공시 성공에 대한 콜백 - 비동기 함수가 error가 발생 시 exception을 일으키는 콜백
Future는 두가지의 state를 가진다. - Uncompleted, 비동기 함수가 아직 끝나지 않은 상태 - Completed, 비동기 함수가 작업이 끝난 상태. 성공하면 value를, 실패하면 error 값을 가져온다
Future<T>는 비동기 처리가 성공시 T타입의 값을 가져온다. 만약 T가 String이면 Future<String> 으로 적어준다. 값을 리턴하지 않을 경우 Future<void> 라고 적는다.
void main(){
print('Before the Future');
Future((){
print('Running the Future');
}).then((_) =>print('Future is complete.') );
print('After the Future');
/*
1. 다트에 의해서 future 객체가 내부적인 배열에 등록
2. Future 관련해서 실행되어야 하는 코드들이 이벤트 Queue 에 등록
3. 불완전한 future 객체 반환
4. Synchronous 방식으로 실행되어야 할 코드 먼저 실행
5. 최종적으로 실제적인 data 값이 future 객체로 전달
*/
/* 실행 결과
Before the Future
After the Future
Running the Future
Future is complete.
*/
}
Future의 then(), catchError() 함수
then() 함수를 통해 Future가 complete 되면 실행해줄 콜백을 등록할 수 있다. 또한 catchError() 함수를 통해 에러처리를 해줄 수 있다.
String createOrderMessage(){
var order = fetchUserOrder();
return'Your order is : ${order}';
}
Future<String> fetchUserOrder(){
return Future.delayed(
Duration(seconds: 2),
() =>'Large Latte',
);
}
void main(){
print('Fetching user order...');
print(createOrderMessage());
}
/* 실행결과
Fetching user order...
Your order is : Instance of 'Future<String>'
*/
원하지 않은 결과가 출력되었다.
async : async 함수 본문 앞에 키워드를 사용하여 비동기로 표시할 수 있다. async function : async 함수는 async 키워드로 표시된 함수다. await : await 키워드를 사용하여 비동기식의 완성된 결과를 얻을 수 있다.
await 키워드는 단지 async 내에서 작동하는 기능이다.
Async method
- 메소드를 통해 나오는 결과물은 Future
- await 키워드를 만날 때까지 synchronous 방식으로 코드 처리
- await 키워드를 만나면 future 가 완료될 때까지 대기
- future 가 완료되자마자 그 다음 코드들을 실행
코드를 아래와 같이 수정한다.
future가 완료될 때까지 일시 중단하려면 async 함수에서 await를 사용하면 된다.
Future<String> createOrderMessage() async {
print('synchronous');
var order = await fetchUserOrder();
return'Your order is : ${order}';
}
Future<String> fetchUserOrder(){
return Future.delayed(
Duration(seconds: 2),
() =>'Large Latte',
);
}
void main() async {
print('Fetching user order...');
print(await createOrderMessage());
}
/* 실행 결과
Fetching user order...
synchronous
Your order is : Large Latte
*/
이제 원하는 결과가 출력되었다.
void main() async {
methodA();
await methodB();
await methodC("main");
methodD();
}
void methodA() {
print('A method run.');
}
methodB() async {
print('B method start.');
await methodC('B');
print('B method end.');
}
methodC(String s) {
print('C method start FROM ${s}');
Future(() =>print('C Future running FROM ${s}'))
.then((_) =>print('C Future is complete FROM ${s}'));
둘 다 불변의 상수를 의미하는 keyword 이지만 const 가 더욱 불변의 강도가 강하다.
final : final 지시어는 어떤 변수를 참조하는 값이 한번 설정되면 다른 값으로 변경될 수 없다는 것을 의미한다.
run-time constant 이며, APP 실행과정에서 값이 정해진다.
const : 한번만 설정할 수 있지만 compile-time constant로 컴파일 타임에 그 값을 알 수 있어야 한다.
Java 언어에서는 public static final 이라는 복잡한 키워드를 사용하지만,
다트 언어에서는 const 로 단순화할 수 있다.
void main() {
String name1 ='홍길동';
print(name1);
const String name2 ='홍길동';
print(name2);
finalString name3 ='홍길동';
print(name3);
}
void main() {
final name ='홍길동'; // 타입 생략 가능
print(name);
}
void main() {
String name1 ='홍길동';
name1 ='이순신';
print(name1);
const String name2 ='홍길동';
name2 ='이순신'; // 에러
print(name2);
finalString name3 ='홍길동';
name3 ='이순신'; // 에러
print(name3);
}
The Const keyword in Dart behaves exactly like the final keyword.
The only difference between final and const is that the const makes the variable constant from compile-time only. Using const on an object, makes the object’s entire deep state strictly fixed at compile-time and that the object with this state will be considered frozen and completely immutable.
void main() {
final x1 = DateTime.now();
print(x1);
// Error: Cannot invoke a non-'const' constructor where a const expression is expected.
const x2 = DateTime.now(); // 컴파일 타임에 값을 알 수 없어 에러 발생