Menu
Coddy logo textTech

Null Safety in Classes

Part of the Object Oriented Programming section of Coddy's Dart journey — lesson 28 of 110.

When designing classes, you need to decide which fields are required and which are optional. Null safety affects how you declare instance variables and write constructors.

Non-nullable fields must be initialized before the constructor body runs. You can do this through initializer lists, constructor parameters, or default values:

class User {
  String name;           // Must be initialized
  String? bio;           // Optional - can be null
  int age = 0;           // Has default value
  
  User(this.name);       // name is initialized via parameter
}

For nullable fields, you can leave them uninitialized since they default to null. This is useful for optional data:

class Product {
  String title;
  String? description;   // Optional product description
  double? discount;      // No discount by default
  
  Product(this.title);
}

var item = Product('Laptop');
print(item.description);  // null
print(item.discount);     // null

When accessing nullable fields, use the null-aware operators you've learned:

class Order {
  String id;
  User? customer;
  
  Order(this.id);
  
  String getCustomerName() {
    return customer?.name ?? 'Guest';
  }
}

The required keyword in named parameters ensures callers provide essential values:

class Account {
  String email;
  String? phone;
  
  Account({required this.email, this.phone});
}

var acc = Account(email: 'test@mail.com');  // phone is optional

By thoughtfully choosing between nullable and non-nullable fields, you create classes that clearly communicate which data is essential and which is optional.

challenge icon

Challenge

Easy

Let's build a booking system for a hotel that demonstrates how to design classes with a thoughtful mix of required and optional fields using null safety.

You'll organize your code into two files:

  • reservation.dart: Define a Reservation class that represents a hotel booking. Every reservation needs certain essential information, but some details are optional extras that guests may or may not provide. Your class should have:
    • A non-nullable String guestName - every reservation must have a guest name
    • A non-nullable int roomNumber - every reservation must be assigned a room
    • A non-nullable int nights with a default value of 1 - minimum stay is one night
    • A nullable String? specialRequests - guests may have special requests
    • A nullable DateTime? checkInTime - exact arrival time might not be known
    • A constructor using named parameters with required for guestName and roomNumber, while nights should be optional with its default value
    • A method getRequestsSummary() that returns the special requests if provided, or 'No special requests' using the ?? operator
    • A method getArrivalInfo() that returns the check-in time's string representation using ?., or null if not set
    • A method printConfirmation() that displays the reservation details
  • main.dart: Import your reservation class and create bookings that showcase different combinations of required and optional data:
    • Create a reservation for guest 'Sarah Johnson' in room 101 for 3 nights, with special requests 'Late checkout' and check-in time set to DateTime(2024, 6, 15, 14, 30)
    • Create a reservation for guest 'Mike Chen' in room 205 (use default nights), with only special requests set to 'Extra pillows'
    • Create a reservation for guest 'Emma Davis' in room 310 for 2 nights (leave both optional fields as null)
    • For each reservation in order, call printConfirmation(), then print Arrival: [result] using getArrivalInfo()

The printConfirmation() method should print in this format:

=== Reservation Confirmed ===
Guest: [guestName]
Room: [roomNumber]
Nights: [nights]
Requests: [getRequestsSummary()]

Expected output:

=== Reservation Confirmed ===
Guest: Sarah Johnson
Room: 101
Nights: 3
Requests: Late checkout
Arrival: 2024-06-15 14:30:00.000
=== Reservation Confirmed ===
Guest: Mike Chen
Room: 205
Nights: 1
Requests: Extra pillows
Arrival: null
=== Reservation Confirmed ===
Guest: Emma Davis
Room: 310
Nights: 2
Requests: No special requests
Arrival: null

Cheat sheet

Non-nullable fields must be initialized before the constructor body runs, while nullable fields can remain uninitialized (defaulting to null):

class User {
  String name;           // Must be initialized
  String? bio;           // Optional - defaults to null
  int age = 0;           // Has default value
  
  User(this.name);
}

Use nullable fields for optional data:

class Product {
  String title;
  String? description;   // Optional
  double? discount;      // Optional
  
  Product(this.title);
}

var item = Product('Laptop');
print(item.description);  // null

Access nullable fields with null-aware operators:

class Order {
  String id;
  User? customer;
  
  Order(this.id);
  
  String getCustomerName() {
    return customer?.name ?? 'Guest';
  }
}

Use required keyword in named parameters for essential values:

class Account {
  String email;
  String? phone;
  
  Account({required this.email, this.phone});
}

var acc = Account(email: 'test@mail.com');

Try it yourself

import 'reservation.dart';

void main() {
  // TODO: Create reservation for 'Sarah Johnson'
  // Room 101, 3 nights, special requests: 'Late checkout'
  // Check-in time: DateTime(2024, 6, 15, 14, 30)
  
  // TODO: Create reservation for 'Mike Chen'
  // Room 205, use default nights, special requests: 'Extra pillows'
  // No check-in time specified
  
  // TODO: Create reservation for 'Emma Davis'
  // Room 310, 2 nights, no special requests, no check-in time
  
  // TODO: For each reservation in order:
  // 1. Call printConfirmation()
  // 2. Print 'Arrival: [result]' using getArrivalInfo()
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming