Introducción a la seguridad de nulos
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 23 de 110.
Una de las fuentes más comunes de errores en programación es intentar usar un valor que no existe. En Dart, esta "ausencia de valor" especial se representa mediante null. La característica de seguridad nula de Dart te ayuda a detectar estos errores incluso antes de que tu código se ejecute.
Sin seguridad nula (null safety), cualquier variable podría ser potencialmente null, lo que provoca fallos en tiempo de ejecución cuando intentas usarla:
// En versiones antiguas de Dart (sin null safety)
String name; // ¡Podría ser null!
print(name.length); // Error si name es nullCon la seguridad de nulos habilitada (Dart 2.12+), el compilador distingue entre variables que pueden contener null y aquellas que no. Por defecto, las variables son no nulas (non-nullable); siempre deben contener un valor válido:
String name = 'Alice'; // Debe tener un valor
print(name.length); // Seguro - name nunca es nullEl compilador se negará a compilar código donde una variable no anulable pueda ser null. Esto traslada la detección de errores del tiempo de ejecución al tiempo de compilación, haciendo que tus programas sean más confiables. En las próximas lecciones, aprenderás cómo declarar tipos anulables cuando realmente los necesites, y los operadores que Dart proporciona para trabajar con valores potencialmente nulos de forma segura.
Desafío
FácilVamos a construir un sistema simple de tarjetas de contacto que demuestre la importancia de las variables no nulas. Crearás un sistema donde cada contacto debe tener información válida y garantizada.
Organizarás tu código en dos archivos:
contact.dart: Define una claseContactque represente la información de contacto de una persona. Dado que estamos trabajando con seguridad nula (null safety), cada campo debe tener un valor válido: ¡no se permiten datos faltantes! Tu clase debe tener:- Un
String firstNamepara el nombre del contacto - Un
String lastNamepara el apellido del contacto - Un
String emailpara la dirección de correo electrónico del contacto - Un
int phoneDigitspara el número de dígitos en su número de teléfono - Un constructor que reciba los cuatro valores
- Un getter
fullNameque devuelva el nombre y el apellido combinados con un espacio entre ellos - Un método
displayCard()que imprima la información del contacto
- Un
main.dart: Importa tu clase de contacto y crea dos contactos para demostrar que todos los campos tienen valores garantizados:- Crea un contacto con el nombre
'Emma', apellido'Wilson', correo electrónico'emma@mail.com'y dígitos de teléfono10 - Crea un contacto con el nombre
'James', apellido'Brown', correo electrónico'james@work.com'y dígitos de teléfono11 - Llama a
displayCard()en cada contacto en el orden listado anteriormente
- Crea un contacto con el nombre
El método displayCard() debe imprimir en este formato exacto:
--- Contact Card ---
Name: [fullName]
Email: [email]
Phone digits: [phoneDigits]Observa cómo cada variable debe inicializarse con un valor real: ¡esto es la seguridad nula en acción! El compilador garantiza que cuando accedas a fullName, email o cualquier otro campo, siempre contendrá datos válidos.
Salida esperada:
--- Contact Card ---
Name: Emma Wilson
Email: emma@mail.com
Phone digits: 10
--- Contact Card ---
Name: James Brown
Email: james@work.com
Phone digits: 11Hoja de referencia
Dart utiliza la seguridad nula (null safety) para prevenir errores causados por el uso de valores que no existen. El valor especial null representa la ausencia de un valor.
Con la seguridad nula habilitada (Dart 2.12+), las variables son no nulas por defecto; siempre deben contener un valor válido:
String name = 'Alice'; // Debe tener un valor
print(name.length); // Seguro - name nunca es nullEl compilador evita que el código se compile si una variable no nula podría ser null, trasladando la detección de errores del tiempo de ejecución al tiempo de compilación.
Pruébalo tú mismo
import 'contact.dart';
void main() {
// TODO: Crear el primer contacto con:
// - nombre: 'Emma'
// - apellido: 'Wilson'
// - email: 'emma@mail.com'
// - dígitos del teléfono: 10
// TODO: Crear el segundo contacto con:
// - nombre: 'James'
// - apellido: 'Brown'
// - email: 'james@work.com'
// - dígitos del teléfono: 11
// TODO: Llamar a displayCard() en cada contacto en orden
}
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
1Fundamentos de POO
Archivos externosLibrerías e importacionesIntroducción a la POOClases vs. ObjetosLa palabra clave thisMétodosVariables de instanciaConceptos básicos de constructoresResumen - Calculadora sencilla4Seguridad de nulos
Introducción a la seguridad de nulosNulables vs No nulablesLos operadores ? y !Palabra clave Late y Null SafetyOperadores Null-awareNull Safety en clasesResumen - Sistema de perfil de usuario7Clases abstractas e interfaces
Clases abstractasMétodos abstractosInterfaces en DartInterfaces implícitasImplementar vs. ExtenderInterfaces múltiplesRepaso - Calculadora de figuras10Colecciones y Genéricos
Visión general de List, Set y MapColecciones con seguridad de tiposClases genéricasMétodos genéricosRestricciones genéricasIterable e IteratorResumen - Almacenamiento genérico2Constructores en Dart
Constructor por defectoConstructores nombradosListas de inicializaciónConstructores constantesConstructores de fábricaConstructores de redireccionamientoResumen - Constructor de formas5Encapsulamiento
Miembros públicos vs. privadosLa convención del prefijo _Privacidad a nivel de libreríaProfundidad en Getters y SettersOcultamiento de informaciónResumen - Registros de estudiantes8Mixins
Introducción a los MixinsCreación de MixinsUso de múltiples MixinsPalabra clave on en MixinsMixin vs HerenciaMixin vs InterfazResumen - Sistema Animal11Métodos especiales
Sobrescritura de toString()Sobrescritura de hashCode y ==Interfaz ComparableMétodo call()Sobrescritura de noSuchMethodResumen - Colección personalizada14Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy3Propiedades de clase
Miembros de instancia vs. estáticosCampos Final y ConstVariables LateMétodos y campos estáticosGetters y SettersResumen - Gestor de cuentas bancarias6Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosLa anotación @overrideLa palabra clave final en clasesConstructores y herenciaResumen - Jerarquía de empleados9Polimorfismo
Fundamentos del polimorfismoPolimorfismo mediante interfacesComprobación de tipos en tiempo de ejecuciónLos operadores is y asPalabra clave covariantResumen - Procesador de pagos12POO Asíncrona
Futures y async/awaitFundamentos de StreamsStream ControllersConstructores asíncronosAsincronía en métodos de claseRepaso - Data Fetcher15Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository