1. Pattern Matching and Enhanced Control Flow
- Switch Expressions with Patterns: Go beyond simple value comparisons in switch statements. Use patterns to match complex data structures and their properties:
enum Season { spring, summer, autumn, winter }
String describeSeason(Season season) {
switch (season) {
case Season.spring:
return 'Time for flowers!';
case Season.summer:
return 'Get ready for the beach!';
case Season.autumn:
return 'Leaves are changing!';
case Season.winter:
return 'Bundle up!';
}
}
void main() {
print(describeSeason(Season.summer)); // Output: Get ready for the beach!
}
- If-Case Statements: Similar to pattern matching in switch, you can now use if statements with case clauses for more concise conditional logic:
String getGreeting(int timeOfDay) {
if (timeOfDay < 12) {
return 'Good morning!';
} else if (timeOfDay < 17) {
return 'Good afternoon!';
} else {
return 'Good evening!';
}
}
void main() {
print(getGreeting(10)); // Output: Good morning!
}
2. Records: Lightweight Data Structures
- Immutable and Concise: Create anonymous, immutable data structures without defining a full class. Ideal for returning multiple values from functions:
Point getLocation() {
return const Point(x: 10, y: 20); // Concise record creation
}
void main() {
final point = getLocation();
print(point.x); // Output: 10 (Records provide property access)
}
3. Destructuring with Pattern Matching
- Extract Values from Data Structures: Elevate code readability by extracting values directly from patterns in assignments:
void processPerson(Person person) {
final name = person.name;
final age = person.age;
// ... use name and age
}
void main() {
final person = Person(name: 'Alice', age: 30);
processPerson(person);
// With destructuring:
final Person(name, age) = person; // Extract name and age directly
// ... use name and age
}
4. Class Modifiers for Enhanced Control
-
sealed
: Restrict subclasses and ensure all possible cases are handled when using a class hierarchy.
sealed class Shape {
const Shape();
}
class Circle extends Shape {
final double radius;
const Circle(this.radius);
}
class Square extends Shape {
final double sideLength;
const Square(this.sideLength);
}
void main() {
// Compile-time error: Triangle is not a subclass of sealed class Shape
// class Triangle extends Shape {}
}
-
abstract
: Enforce subclasses to implement abstract methods and prevent direct instantiation of the abstract class.
abstract class HospitalStaff {
// Abstract method - subclasses must define work behavior
void performDuties();
String get name; // Concrete property (optional)
}
class Doctor extends HospitalStaff {
final String specialization;
Doctor(this.name, this.specialization);
@override // Ensures correct implementation
void performDuties() {
print('$name the doctor is diagnosing patients.');
}
}
class Nurse extends HospitalStaff {
final String department;
Nurse(this.name, this.department);
@override
void performDuties() {
print('$name the nurse is assisting patients in the $department department.');
}
}
void main() {
// Cannot directly create a HospitalStaff object
// HospitalStaff staff = HospitalStaff(); // Compile-time error
// Create concrete subclass objects (Doctor and Nurse)
Doctor drAmitJ = Doctor('Dr. Amit J', 'Cardiology');
Nurse varshaK = Nurse('Varsha K', 'Emergency');
drAmitJ.performDuties(); // Dr. Amit J the doctor is diagnosing patients.
varshaK.performDuties(); // Varsha K the nurse is assisting patients in the Emergency department.
}
-
mixin
: Promote code reusability by allowing multiple classes to inherit behavior from a mixin.
mixin Treatable {
// Abstract method - subclasses must define treatment behavior
void provideTreatment();
String get diagnosis; // Optional property
}
class Doctor {
final String name;
final String specialization;
Doctor(this.name, this.specialization);
void performDuties() {
print('$name the doctor is diagnosing patients.');
}
}
class Nurse extends HospitalStaff {
final String department;
Nurse(this.name, this.department);
void performDuties() {
print('$name the nurse is assisting patients in the $department department.');
}
}
class Patient with Treatable {
final String name;
final String condition;
Patient(this.name, this.condition);
@override
void provideTreatment() {
print('Providing treatment for $condition to $name.');
}
// Can inherit diagnosis from Treatable if needed
}
void main() {
// Doctor and Nurse classes remain unchanged
Patient raviKumar = Patient('Ravi Kumar', 'Flu');
raviKumar.provideTreatment(); // Providing treatment for Flu to Ravi Kumar.
}
-
non-nullable
: Enforce non-null semantics on specific class members for stricter type safety.
class Patient {
final String name; // Non-nullable - must be initialized with a value
// Can be nullable if a patient might not have a diagnosis yet
final String? diagnosis;
Patient(this.name, {this.diagnosis});
}
class Doctor {
final String name;
final String specialization; // Non-nullable - specialization is required
Doctor(this.name, this.specialization);
void treatPatient(Patient patient) {
if (patient.diagnosis != null) { // Check for null before accessing diagnosis
print('Dr. $name is treating $patient.name for ${patient.diagnosis}.');
} else {
print('Dr. $name is examining $patient.name.');
}
}
}
void main() {
Patient raviKumar = Patient('Ravi Kumar'); // Valid - name is non-nullable
// Patient raviKumar; // Error: 'name' must be initialized
Doctor drAmit = Doctor('Dr. Amit J', 'Cardiology'); // Valid - both fields are non-nullable
drAmit.treatPatient(raviKumar); // Outputs: Dr. Amit J is examining Ravi Kumar. (diagnosis is null)
}
Want to learn more about Dart3? Check out the resources link!
Until next time, keep calm and code on.🧑🏻💻
Top comments (0)