https://dart.dev/guides/language/evolution
다트가 지난 23년 1월 2.19v로 진화하더니
23년 5월 3.0v로 다시 한번 더욱 발전 하였습니다.
이번 3.0은 100% sound null safety 보장한다고 합니다.
건전한 100% 널 안전성을 보장한다는 의미는 Dart 3.0에서는 모든 라이브러리가 널안전성을 지원하며
예기치못한 null 과 관련한 문제로부터 예외처리, 역참조문제 등을 완전히 피할수 있게 해줍니다.
문법적으로는 크게 3가지로 요약할 수 있습니다.
1.Records - 복수 자료형의 리턴
2. Patterns - 값을 일치시키고 분해시키는 패턴
3. Class modifiers - 클래스 제한자
지금부터 하나씩 살펴보겠습니다.
1. Record
JavaScript에서 매력적이던 record가 도입되어,
이제부터는 복수개의 자료형을 반환할 수 있게 된다.
선언 - 익명 / 이름
void main() {
// positional params만 있고 named params가 없는 record 선언
(int, String, bool) myRecord;
myRecord = (0, "hello", true);
print(myRecord);
print(myRecord.$3); //익명이므로 $기호로 1번부터 3번까지 파람에 접근가능
print("{$myRecord.runtimeType}\n");
// positional과 named를 모두 가진 record 선언
(bool p, {int i, String n, bool b}) myRecord2 = (true, i: 0, n:"zero", b:true);
print(myRecord2);
print(myRecord2.n); // 이름으로 접근가능
print(myRecord2.runtimeType);
}
이렇게 익명으로 선언하면 $기호를 통해 접근가능하며
이름이 있는 record는 변수명으로 특정 위치에 접근가능하다.
특징
- Records는 익명성, 불변성, 집계성을 가진 타입이며
- 다른 컬렉션 유형(List, Set, Map 등등)과 마찬가지로 여러 개체를 하나의 개체로 묶는 타입이다.
- 하지만 다른 컬렉션 유형과 달리 Record는 크기와 유형이 고정되어 있다.
- Record라는 클래스가 추상클래스로 존재하지만 사용하지 않으며,
타입 선언에는 괄호() 안에 들어간 인수 목록을 런타임에서 갖는 ‘익명’ 타입이다. - 또한 필드의 길이(갯수), 위치, 이름이 지정된다.
- 그리고 코드 값과 레코드 유형은 인수 목록 및 단순화된 함수 유형 매개변수 목록과 유사하게 작성된다.
2. Pattern
패턴은 값이 내가 원하는 형식과 일치하는지 항시 확인한다. 즉, 값이 패턴과 일치하는지 확인하는 것이다.
void main() {
final [x,y,...rest,z,_] = [1,2,3,4,5,7,8];
print(x);
print(y);
print(rest);
print(z);
}
List에서 값을 받아 x,y,z, rest 를 초기화하는 방법에 활용되었다.
_ (언더스코어) 와 같은 와일드카드로
위와같이 위치를 조정하는데 사용하거나,
아래와 같이 변수바인딩에 사용할 수 있다.
final record = (1,"abc");
switch (record) {
case (int _, "abc" ):
print('첫번째가 int값 아무거나 가지면 true');
case (2, "abc"):
print('First field is int and second is String.');
}
패턴매칭은 Switch나 if에서 자주 활용될 수 있다.
var isPrimary = switch (color) {
case Color.red :
return true;
case Color.yellow :
return true;
case Color.blue :
return true;
default:
return false;
};
위와 같이 긴 Switch문이 Logical or 패턴으로
아래와 같이 요약될 수 있다.
var isPrimary = switch (color) {
Color.red || Color.yellow || Color.blue => true,
_ => false
};
언더스코프는 default 옵션처럼
예외 상황을 포함시키도록 활용한다.
대수적 활용
sealed class Shape {}
class Square implements Shape {
final double length;
Square(this.length);
}
class Circle implements Shape {
final double radius;
Circle(this.radius);
}
double calculateArea(Shape shape) => switch (shape) {
Square(length: var l) => l * l,
Circle(radius: var r) => math.pi * r * r
};
sealed에 대해서는 아래에서 다루겠습니다만,
하위클래스로 상속과 구현이 가능하게하는 클래스 제한자입니다.
switch문에 집중해서보면 대수적으로 클래스타입에 해당 속성을 가지면
계산값을 반환하도록 대수적인 활용을 할 수 있습니다.
3. 클래스 제한자
기존에 클래스는 어떤 클래스로든 제한이 없이 다채롭게 사용이 가능했지만,
자유도는 그대로 높여두고 OOP적 개념을 더욱 도입하여 이펙티브하게 사용할 수 있도록 제약을 줄수있게 되었습니다.
클래스앞에 사용가능한 한정자는 아래와 같습니다.
- abstract
- base
- final
- interface
- sealed
- mixin
abstract
전체 인터페이스의 완전하고 구체적인 구현이 필요하지 않은 클래스를 정의하려면 abstract 한정자를 사용합니다 .
추상 클래스에는 종종 추상 메서드가 있습니다 .
// Library a.dart
abstract class Vehicle {
void moveForward(int meters);
}
// Library b.dart
import 'a.dart';
// Error: Cannot be constructed
Vehicle myVehicle = Vehicle();
// Can be extended
class Car extends Vehicle {
int passengers = 4;
// ···
}
// Can be implemented
class MockVehicle implements Vehicle {
@override
void moveForward(int meters) {
// ...
}
}
- 추상클래스는 구현없이 인스턴스를 만들수 없습니다.
- Extends (상속)과 Implements(구현) 가능합니다
- 구현시에는 추상클래스에 선언된 함수들을 오버라이드하여 구현 해주어야 합니다.
base
Class , Mixin의 상속만 가능한 base수정자
// Library a.dart
base class Vehicle {
void moveForward(int meters) {
// ...
}
}
// Library b.dart
import 'a.dart';
// Can be constructed
Vehicle myVehicle = Vehicle();
// Can be extended
base class Car extends Vehicle {
int passengers = 4;
// ...
}
// ERROR: Cannot be implemented
base class MockVehicle implements Vehicle {
@override
void moveForward() {
// ...
}
}
- 상속만 할수 있도록 제약하여, 부모 자식의 관계를 가집니다.
- BASE 를 사용했다면, 상속한 모두가 같은 부모 타입에도 속하게 됩니다.
interface
// Library a.dart
interface class Vehicle {
void moveForward(int meters) {
// ...
}
}
// Library b.dart
import 'a.dart';
// Can be constructed
Vehicle myVehicle = Vehicle();
// ERROR: Cannot be inherited
class Car extends Vehicle {
int passengers = 4;
// ...
}
// Can be implemented
class MockVehicle implements Vehicle {
@override
void moveForward(int meters) {
// ...
}
}
- 인터페이스는 말그래도 implements (구현)해야 하는 클래스 제한자 입니다.
- 상속 확장은 불가능합니다.
- 그래서 abstract 와 interface를 결합하여 상속을 막고 구현만 가능하도록 한정하여 종종 사용할 수 있습니다.
final
final 계층 구조를 모두 막을수 있습니다.
클래스에서 하위 유형을 만들어 낼 수 없습니다.
상속과 구현을 모두 허용하지 않으면 하위 유형 지정이 완전히 방지하면 아래 내용을 보장합니다.
- API에 증분 변경 사항을 안전하게 추가할 수 있습니다.
- 타사 하위 클래스에서 덮어쓰지 않았음을 알고 인스턴스 메서드를 호출할 수 있습니다
// Library a.dart
final class Vehicle {
void moveForward(int meters) {
// ...
}
}
// Library b.dart
import 'a.dart';
// Can be constructed
Vehicle myVehicle = Vehicle();
// ERROR: Cannot be inherited
class Car extends Vehicle {
int passengers = 4;
// ...
}
class MockVehicle implements Vehicle {
// ERROR: Cannot be implemented
@override
void moveForward(int meters) {
// ...
}
}
sealed
알려진 enumerable (열거 가능한) 하위 유형 집합을 만들려면 sealed 한정자를 사용합니다.
수정 sealed자는 클래스가 자체 라이브러리 외부에서 확장되거나 구현되는 것을 방지합니다.
sealed class Vehicle {}
class Car extends Vehicle {}
class Truck implements Vehicle {}
class Bicycle extends Vehicle {}
// ERROR: Cannot be instantiated
Vehicle myVehicle = Vehicle();
// Subclasses can be instantiated
Vehicle myCar = Car();
String getVehicleSound(Vehicle vehicle) {
// ERROR: The switch is missing the Bicycle subtype or a default case.
return switch (vehicle) {
Car() => 'vroom',
Truck() => 'VROOOOMM',
};
}
- sealed는 클래스의 enum화를 가능하게 하는 키워드입니다.
- 상속과 구현을 허용하고 자기자신은 인스턴스를 가지지 못하는게 Abstract와 같습니다.
- 열거형으로 Switch문에 유용하게 활용되어 하위클래스 타입케이스를 철처히 검사하게 합니다.
Mixin
한번에 여러클래스를 상속받을 수 있는 mixin키워드
mixin, mixin class 는 다르다.
mixin은 혼자 인스턴스화 될 수 없고, 믹스인 클래스는 믹스인 가능한 클래스이다.
믹스인은 Mixin을 붙여 만들어주며, with 키워드를 통해 혼합시킬수 있습니다.
mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
mixin은 혼자 인스턴스화 될 수 없고,
믹스인 클래스는 믹스인 가능한 클래스이다.
여러 클래스의 mixin 예제
mixin A {
String getMessage() => 'A';
}
mixin B {
String getMessage() => 'B';
}
class P {
String getMessage() => 'P';
}
class AB extends P with A, B {}
class BA extends P with B, A {}
void main() {
// 같은메소드를 가진 클래스를 mixin하면
// 마지막 클래스 기준으로 오버라이딩
String result = '';
AB ab = AB();
result += ab.getMessage();
print(result);
BA ba = BA();
result += ba.getMessage();
print(result);
}
'Study > Flutter' 카테고리의 다른 글
[flutter] flutter mac에서 설치 및 설정 (0) | 2023.08.16 |
---|---|
[Flutter] flutter 3.10 Release Note 요약 (0) | 2023.06.06 |
[GPT] GPT에게 물어본 좋은 개발자 (0) | 2023.04.02 |
[Flutter] BuildContext와 활용법 (0) | 2023.03.31 |
[Flutter] 앱 이름, 아이콘, 스플래시 이미지설정 (0) | 2023.01.25 |