Bezpieczeństwo wartości null w klasach
Część sekcji Programowanie obiektowe ścieżki Dart w Coddy. Lekcja 28 z 110.
Projektując klasy, musisz zdecydować, które pola są wymagane, a które opcjonalne. Bezpieczeństwo wartości null wpływa na sposób deklarowania zmiennych instancji i pisania konstruktorów.
Pola, które nie dopuszczają wartości null, muszą zostać zainicjalizowane przed uruchomieniem treści konstruktora. Możesz to zrobić za pomocą list inicjalizujących, parametrów konstruktora lub wartości domyślnych:
class User {
String name; // Musi zostać zainicjalizowane
String? bio; // Opcjonalne — może mieć wartość null
int age = 0; // Ma wartość domyślną
User(this.name); // name jest inicjalizowane przez parametr
}Pola dopuszczające wartość null możesz pozostawić niezainicjalizowane, ponieważ domyślnie mają wartość null. Jest to przydatne w przypadku danych opcjonalnych:
class Product {
String title;
String? description; // Opcjonalny opis produktu
double? discount; // Domyślnie bez rabatu
Product(this.title);
}
var item = Product('Laptop');
print(item.description); // null
print(item.discount); // nullPodczas uzyskiwania dostępu do pól dopuszczających wartość null używaj poznanych operatorów obsługujących null:
class Order {
String id;
User? customer;
Order(this.id);
String getCustomerName() {
return customer?.name ?? 'Guest';
}
}Słowo kluczowe required w parametrach nazwanych gwarantuje, że wywołujący podadzą niezbędne wartości:
class Account {
String email;
String? phone;
Account({required this.email, this.phone});
}
var acc = Account(email: 'test@mail.com'); // phone jest opcjonalneŚwiadomie wybierając między polami dopuszczającymi wartość null a polami niedopuszczającymi wartości null, tworzysz klasy, które jasno określają, które dane są niezbędne, a które opcjonalne.
Wyzwanie
ŁatwyZbudujmy system rezerwacji hotelowych, który pokaże, jak projektować klasy z przemyślanym połączeniem wymaganych i opcjonalnych pól przy użyciu bezpieczeństwa nulli.
Podzielisz kod na dwa pliki:
reservation.dart: Zdefiniuj klasęReservation, która reprezentuje rezerwację hotelową. Każda rezerwacja wymaga określonych niezbędnych informacji, ale niektóre szczegóły są opcjonalnymi dodatkami, które goście mogą podać albo nie. Twoja klasa powinna mieć:- Nieprzyjmujące wartości null
String guestName— każda rezerwacja musi zawierać imię i nazwisko gościa - Nieprzyjmujące wartości null
int roomNumber— do każdej rezerwacji musi być przypisany pokój - Nieprzyjmujące wartości null
int nightsz domyślną wartością1— minimalny pobyt to jedna noc - Przyjmujące wartość null
String? specialRequests— goście mogą mieć specjalne życzenia - Przyjmujące wartość null
DateTime? checkInTime— dokładna godzina przyjazdu może nie być znana - Konstruktor z parametrami nazwanymi, w którym
requiredjest użyte dlaguestNameiroomNumber, anightsjest opcjonalne i ma domyślną wartość - Metodę
getRequestsSummary(), która zwraca podane specjalne życzenia albo'No special requests', używając operatora?? - Metodę
getArrivalInfo(), która zwraca tekstową reprezentację godziny zameldowania przy użyciu?.albonull, jeśli godzina nie została ustawiona - Metodę
printConfirmation(), która wyświetla szczegóły rezerwacji
- Nieprzyjmujące wartości null
main.dart: Zaimportuj klasę rezerwacji i utwórz rezerwacje prezentujące różne kombinacje wymaganych i opcjonalnych danych:- Utwórz rezerwację dla gościa
'Sarah Johnson'w pokoju101na3noce, ze specjalnymi życzeniami'Late checkout'i godziną zameldowania ustawioną naDateTime(2024, 6, 15, 14, 30) - Utwórz rezerwację dla gościa
'Mike Chen'w pokoju205(użyj domyślnej liczby nocy), ustawiając tylko specjalne życzenia na'Extra pillows' - Utwórz rezerwację dla gościa
'Emma Davis'w pokoju310na2noce (pozostaw oba pola opcjonalne jako null) - Dla każdej rezerwacji, w podanej kolejności, wywołaj
printConfirmation(), a następnie wypiszArrival: [result], używającgetArrivalInfo()
- Utwórz rezerwację dla gościa
Metoda printConfirmation() powinna wypisywać dane w następującym formacie:
=== Reservation Confirmed ===
Guest: [guestName]
Room: [roomNumber]
Nights: [nights]
Requests: [getRequestsSummary()]Oczekiwany wynik:
=== 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: nullSpróbuj swoich sił
import 'reservation.dart';
void main() {
// TODO: Utwórz rezerwację dla 'Sarah Johnson'
// Pokój 101, 3 noce, życzenia specjalne: 'Late checkout'
// Godzina zameldowania: DateTime(2024, 6, 15, 14, 30)
// TODO: Utwórz rezerwację dla 'Mike Chen'
// Pokój 205, użyj domyślnej liczby nocy, życzenia specjalne: 'Extra pillows'
// Nie podano godziny zameldowania
// TODO: Utwórz rezerwację dla 'Emma Davis'
// Pokój 310, 2 noce, bez życzeń specjalnych i bez godziny zameldowania
// TODO: Dla każdej rezerwacji po kolei:
// 1. Wywołaj printConfirmation()
// 2. Wypisz 'Arrival: [result]', używając getArrivalInfo()
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP
Pliki zewnętrzneBiblioteki i importyWprowadzenie do OOPKlasy a obiektySłowo kluczowe thisMetodyZmienne instancjiPodstawy konstruktorówPowtórka – prosty kalkulator4Bezpieczeństwo wartości null
Wprowadzenie do bezpieczeństwa wartości nullTypy dopuszczające null i niedopuszczające nullOperatory ? i !Słowo kluczowe late a bezpieczeństwo wartości nullOperatory uwzględniające nullBezpieczeństwo wartości null w klasachPodsumowanie — system profili użytkowników7Klasy abstrakcyjne i interfejsy
Klasy abstrakcyjneMetody abstrakcyjneInterfejsy w DartInterfejsy niejawneImplementowanie a rozszerzanieWiele interfejsówPowtórka — kalkulator kształtów10Kolekcje i typy generyczne
Przegląd List, Set i MapKolekcje bezpieczne typowoKlasy generyczneMetody generyczneOgraniczenia typów generycznychIterable i IteratorPowtórka — generyczne przechowywanie danych13Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieMetody rozszerzająceKlasy wywoływalneKlasy zapieczętowane (Dart 3)Rekordy (Dart 3)Wzorce i dopasowywanie (3.0)Typy wyliczeniowe z metodami16Projekt: Zarządzanie biblioteką
Omówienie projektuKlasy książek i użytkowników2Konstruktory w Dart
Konstruktor domyślnyKonstruktory nazwaneListy inicjalizująceKonstruktory stałeKonstruktory fabryczneKonstruktory przekierowującePodsumowanie — budowanie kształtów5Hermetyzacja
Elementy publiczne a prywatneKonwencja prefiksu _Prywatność na poziomie bibliotekiGettery i settery — szczegółowoUkrywanie informacjiPodsumowanie — dane uczniów8Domieszki
Wprowadzenie do domieszekTworzenie domieszekKorzystanie z wielu domieszekSłowo kluczowe on w domieszkachDomieszka a dziedziczenieDomieszka a interfejsPodsumowanie – system zwierząt11Metody specjalne
Nadpisanie toString()Nadpisanie hashCode i ==Interfejs ComparableMetoda call()Nadpisanie noSuchMethodPodsumowanie — własna kolekcja14Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy3Właściwości klas
Elementy instancji a elementy statycznePola final i constZmienne inicjalizowane z opóźnieniemMetody i pola statyczneGettery i setteryPowtórzenie – menedżer kont bankowych6Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe superPrzesłanianie metodAdnotacja @overrideSłowo kluczowe final dla klasKonstruktory i dziedziczeniePowtórzenie – hierarchia pracowników9Polimorfizm
Podstawy polimorfizmuPolimorfizm za pomocą interfejsówSprawdzanie typu w czasie działaniaOperatory is i asSłowo kluczowe covariantPodsumowanie — procesor płatnościPoćwicz samodzielnie: Kompilator Dart online