Menu
Coddy logo textTech

Null Safety en clases

Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 28 de 110.

Al diseñar clases, debes decidir qué campos son obligatorios y cuáles son opcionales. La seguridad contra nulos (null safety) afecta cómo declaras las variables de instancia y escribes los constructores.

Los campos que no admiten valores nulos deben inicializarse antes de que se ejecute el cuerpo del constructor. Puedes hacerlo mediante listas de inicialización, parámetros del constructor o valores predeterminados:

class User {
  String name;           // Debe ser inicializado
  String? bio;           // Opcional - puede ser nulo
  int age = 0;           // Tiene un valor por defecto
  
  User(this.name);       // name se inicializa a través del parámetro
}

Para los campos que admiten valores nulos, puedes dejarlos sin inicializar ya que por defecto son null. Esto es útil para datos opcionales:

class Product {
  String title;
  String? description;   // Descripción opcional del producto
  double? discount;      // Sin descuento por defecto
  
  Product(this.title);
}

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

Al acceder a campos que admiten valores nulos, utiliza los operadores null-aware que has aprendido:

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

La palabra clave required en parámetros nombrados asegura que quienes realizan la llamada proporcionen valores esenciales:

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

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

Al elegir cuidadosamente entre campos nullable y non-nullable, creas clases que comunican claramente qué datos son esenciales y cuáles son opcionales.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de reservas para un hotel que demuestre cómo diseñar clases con una mezcla reflexiva de campos obligatorios y opcionales utilizando null safety.

Organizarás tu código en dos archivos:

  • reservation.dart: Define una clase Reservation que represente una reserva de hotel. Cada reserva necesita cierta información esencial, pero algunos detalles son extras opcionales que los huéspedes pueden o no proporcionar. Tu clase debe tener:
    • Un String guestName no nulable: cada reserva debe tener un nombre de huésped.
    • Un int roomNumber no nulable: a cada reserva se le debe asignar una habitación.
    • Un int nights no nulable con un valor por defecto de 1: la estancia mínima es de una noche.
    • Un String? specialRequests nulable: los huéspedes pueden tener peticiones especiales.
    • Un DateTime? checkInTime nulable: la hora exacta de llegada podría no conocerse.
    • Un constructor que utilice parámetros nombrados con required para guestName y roomNumber, mientras que nights debe ser opcional con su valor por defecto.
    • Un método getRequestsSummary() que devuelva las peticiones especiales si se proporcionan, o 'No special requests' utilizando el operador ??.
    • Un método getArrivalInfo() que devuelva la representación en cadena de la hora de entrada utilizando ?., o null si no está establecida.
    • Un método printConfirmation() que muestre los detalles de la reserva.
  • main.dart: Importa tu clase de reserva y crea reservas que muestren diferentes combinaciones de datos obligatorios y opcionales:
    • Crea una reserva para el huésped 'Sarah Johnson' en la habitación 101 por 3 noches, con peticiones especiales 'Late checkout' y la hora de entrada establecida en DateTime(2024, 6, 15, 14, 30).
    • Crea una reserva para el huésped 'Mike Chen' en la habitación 205 (usa las noches por defecto), con solo las peticiones especiales establecidas en 'Extra pillows'.
    • Crea una reserva para el huésped 'Emma Davis' en la habitación 310 por 2 noches (deja ambos campos opcionales como nulos).
    • Para cada reserva en orden, llama a printConfirmation(), luego imprime Arrival: [result] usando getArrivalInfo().

El método printConfirmation() debe imprimir en este formato:

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

Salida esperada:

=== 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

Hoja de referencia

Los campos no anulables deben inicializarse antes de que se ejecute el cuerpo del constructor, mientras que los campos anulables pueden permanecer sin inicializar (por defecto a null):

class User {
  String name;           // Debe ser inicializado
  String? bio;           // Opcional - por defecto es null
  int age = 0;           // Tiene un valor por defecto
  
  User(this.name);
}

Utilice campos anulables para datos opcionales:

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

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

Acceda a los campos anulables con operadores que detectan nulos (null-aware):

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

Utilice la palabra clave required en parámetros con nombre para valores esenciales:

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

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

Pruébalo tú mismo

import 'reservation.dart';

void main() {
  // TODO: Crear reserva para 'Sarah Johnson'
  // Habitación 101, 3 noches, peticiones especiales: 'Late checkout'
  // Hora de check-in: DateTime(2024, 6, 15, 14, 30)
  
  // TODO: Crear reserva para 'Mike Chen'
  // Habitación 205, usar noches por defecto, peticiones especiales: 'Extra pillows'
  // No se especificó hora de check-in
  
  // TODO: Crear reserva para 'Emma Davis'
  // Habitación 310, 2 noches, sin peticiones especiales, sin hora de check-in
  
  // TODO: Para cada reserva en orden:
  // 1. Llamar a printConfirmation()
  // 2. Imprimir 'Arrival: [result]' usando getArrivalInfo()
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos