JavaRush /Blog Java /Random-PL /Przerwa kawowa #230. Co to są rekordy w Javie i jak dział...

Przerwa kawowa #230. Co to są rekordy w Javie i jak działają?

Opublikowano w grupie Random-PL
Źródło: JavaTechOnline W tym artykule szczegółowo omówiono koncepcję rekordów w języku Java wraz z przykładami, w tym ich składnią, sposobem ich tworzenia i korzystania z nich. Przerwa kawowa #230.  Czym są rekordy w Javie i jak działają - 1Rekordy w Javie to jedna ze wspaniałych funkcji, która została po raz pierwszy wprowadzona w Javie 14 jako funkcja podglądu i ostatecznie została udostępniona w wersji Java 17. Wielu programistów aktywnie z niej korzysta, co pomaga im skutecznie zredukować ogromną ilość szablonowego kodu. Co więcej, dzięki rekordom nie musisz pisać ani jednej linii kodu, aby klasa była niezmienna.

Kiedy używać Record w Javie?

Jeśli chcesz przekazywać niezmienne dane pomiędzy różnymi warstwami aplikacji, dobrym wyborem może być użycie Record. Domyślnie rekordy w Javie są niezmienne, co oznacza, że ​​nie możemy zmienić ich właściwości po ich utworzeniu. Dzięki temu unikamy błędów i poprawiamy niezawodność kodu. Mówiąc najprościej, używając Record in Java, możemy automatycznie generować klasy.

Gdzie mogę używać Record w Javie?

Generalnie rekordów możemy używać w każdej sytuacji, w której musimy zadeklarować proste kontenery danych z niezmiennymi właściwościami i automatycznie generowanymi metodami. Na przykład poniżej znajduje się kilka przypadków użycia, w których rekordy mogą być przydatne: Obiekty przesyłania danych (DTO): Możemy użyć rekordu do zadeklarowania prostych obiektów przesyłania danych, które zawierają dane. Jest to przydatne podczas przesyłania danych między różnymi warstwami aplikacji, na przykład między warstwą usług a warstwą bazy danych. Obiekty konfiguracyjne : Rekord może służyć do deklarowania obiektów konfiguracyjnych zawierających zestaw właściwości konfiguracyjnych dla aplikacji lub modułu. Obiekty te zazwyczaj mają niezmienne właściwości, dzięki czemu są bezpieczne dla wątków i łatwe w użyciu. Obiekty wartości. Rekord może służyć do deklarowania obiektów wartości, które zawierają zestaw wartości reprezentujących konkretną koncepcję lub model domeny. Odpowiedzi API : podczas tworzenia interfejsu API REST dane są zazwyczaj zwracane w postaci JSON lub XML. W takich przypadkach może być konieczne zdefiniowanie prostej struktury danych reprezentującej odpowiedź API. Rekordy idealnie się do tego nadają, ponieważ pozwalają zdefiniować lekką i niezmienną strukturę danych, którą można łatwo serializować do formatu JSON lub XML. Dane testowe. Podczas pisania testów jednostkowych często trzeba utworzyć dane testowe reprezentujące określony scenariusz. W takich przypadkach może być konieczne zdefiniowanie prostej struktury danych reprezentującej dane testowe. Rekordy mogą być do tego idealne, ponieważ pozwalają nam zdefiniować lekką i niezmienną strukturę danych przy minimalnym kodzie standardowym. Obiekty krotki : rekordów można używać do deklarowania obiektów krotek, które zawierają stałą liczbę powiązanych wartości. Może to być przydatne podczas zwracania wielu wartości z metody lub podczas pracy z kolekcjami powiązanych wartości.

Co to jest rekord w Javie?

Record w Javie to klasa przeznaczona do przechowywania danych. Jest podobna do tradycyjnej klasy Java, ale jest lżejsza i ma pewne unikalne funkcje. Rekordy są domyślnie niezmienne, co oznacza, że ​​ich stanu nie można zmienić po ich utworzeniu. Dzięki temu idealnie nadają się do przechowywania niezmiennych danych, takich jak parametry konfiguracyjne czy wartości zwracane z zapytania do bazy danych. Jest to także sposób na utworzenie niestandardowego typu danych składającego się z zestawu pól lub zmiennych, a także metod dostępu i modyfikowania tych pól. Record in Java ułatwia pracę z danymi, zmniejszając ilość szablonowego kodu, który programiści muszą pisać w kółko. Składnia tworzenia wpisu w Javie jest następująca:
record Record_Name(Fields....)

Jak utworzyć i używać rekordu w Javie z przykładami?

Zobaczmy, jak programowo utworzyć i używać rekordu w Javie.

Tworzenie rekordu

Programowe tworzenie rekordu nie jest bardzo podobne do tworzenia zwykłej klasy w Javie. Zamiast class używamy słowa kluczowego record . Należy także zadeklarować pola z typami danych w nawiasach nazwy rekordu. Oto przykład kodu demonstrujący, jak utworzyć wpis w Javie:
public record Book(String name, double price) { }
W tym przykładzie utworzyliśmy rekord o nazwie Książka z dwoma polami: nazwa i cena . Słowo kluczowe public wskazuje, że dostęp do tego wpisu można uzyskać poza pakietem, w którym jest zdefiniowany.

Korzystanie z rekordu

Aby użyć rekordu w Javie, możemy utworzyć jego instancję za pomocą słowa kluczowego new, tak jak robimy to w przypadku zwykłej klasy. Oto przykład:
Book book = new Book( "Core Java" , 324.25);
Tutaj tworzona jest nowa instancja rekordu Book z polem nazwy ustawionym na Core Java i polem ceny ustawionym na 324.25 . Po utworzeniu rekordu dostęp do jego pól można uzyskać za pomocą nazwy samego pola. Nie ma metod get ani set. Zamiast tego nazwa pola staje się nazwą metody.
String name = book.name();

double price = book.price();
Tutaj widzimy wartość pól nazwy i ceny pobieranych z rekordu Księga . Oprócz pól rekordy mają również wbudowane metody, których można używać do manipulowania nimi. Na przykład toString() , równości() i hashCode() . Pamiętaj, że rekordy Java są domyślnie niezmienne, co oznacza, że ​​po ich utworzeniu nie można zmienić ich stanu. Przyjrzyjmy się innemu przykładowi tworzenia i używania rekordów w Javie. Weźmy przypadek, w którym potrzebujemy rekordu do przechowywania informacji o uczniu.
public record Student(String name, int age, String subject) {}
Spowoduje to utworzenie rekordu o nazwie Student z trzema polami: imię i nazwisko , wiek i przedmiot . Możemy utworzyć instancję tego wpisu w następujący sposób:
Student student = new Student("John Smith", 20, "Computer Science");
Możemy wówczas uzyskać wartości pól korzystając z nazwy pola:
String name = student.name();
int age = student.age();
String subject= student.subject();

Jak wygląda płyta po kompilacji?

Ponieważ Record jest po prostu specjalnym rodzajem klasy, kompilator konwertuje ją również na zwykłą klasę, ale z pewnymi ograniczeniami i różnicami. Kiedy kompilator konwertuje rekord (plik Java) na kod bajtowy po procesie kompilacji, wygenerowany plik .class zawiera dodatkowe deklaracje klasy Record . Na przykład poniżej znajduje się kod bajtowy wygenerowany dla wpisu Studenta przez kompilator Java:
record Student(String name, int age) {   }

Zapis do pliku .class (po kompilacji)

Możemy również znaleźć przekonwertowaną klasę poniżej, jeśli użyjemy narzędzia javap i zastosujemy poniższe polecenie z wiersza poleceń. Należy pamiętać, że wiersz poleceń Java zawiera narzędzie javap , którego można używać do przeglądania informacji o polach, konstruktorach i metodach pliku klasy. >javap Wniosek ucznia: Jeśli dokładnie sprawdzimy kod bajtowy, możemy mieć pewne obserwacje:
  • Kompilator zastąpił słowo kluczowe Record słowem class .
  • Kompilator ogłosił klasę ostateczną . Oznacza to, że tej klasy nie można rozszerzyć. Oznacza to również, że nie może być dziedziczona i ma niezmienną naturę.
  • Przekonwertowana klasa stanowi rozszerzenie Java.lang.Record . Oznacza to, że wszystkie rekordy są podklasą klasy Record zdefiniowanej w pakiecie java.lang .
  • Kompilator dodaje sparametryzowanego konstruktora.
  • Kompilator automatycznie wygenerował metody toString() , hashCode() i Equals() .
  • Kompilator dodał metody dostępu do pól. Zwróć uwagę na konwencję nazewnictwa metod - są one dokładnie takie same jak nazwy pól, przed nazwami pól nie powinno być get ani set .

Pola w rekordach

Rekordy w Javie definiują swój stan za pomocą zestawu pól, każde o innej nazwie i typie. Pola rekordu są deklarowane w nagłówku rekordu. Na przykład:
public record Person(String name, int age) {}
Wpis ten ma dwa pola: nazwę typu String i wiek typu int . Pola rekordu są domyślnie ostateczne i nie można ich ponownie przypisać po utworzeniu rekordu. Możemy dodać nowe pole, jednak nie jest to zalecane. Nowe pole dodane do rekordu musi być statyczne. Na przykład:
public record Person(String name, int age) {

   static String sex;
}

Konstruktorzy w rekordach

Podobnie jak tradycyjne konstruktory klas, konstruktory rekordów służą do tworzenia instancji rekordów. Records ma dwie koncepcje konstruktorów: konstruktor kanoniczny i konstruktor zwarty . Konstruktor kompaktowy zapewnia bardziej zwięzły sposób inicjowania zmiennych stanu w rekordzie, podczas gdy konstruktor kanoniczny zapewnia bardziej tradycyjny sposób z większą elastycznością.

Konstruktor kanoniczny

Domyślny kompilator Java udostępnia nam konstruktor obejmujący wszystkie argumenty (konstruktor wszystkich pól), który przypisuje swoje argumenty do odpowiednich pól. Jest znany jako konstruktor kanoniczny. Możemy również dodać logikę biznesową, taką jak instrukcje warunkowe, aby zweryfikować dane. Poniżej znajduje się przykład:
public record Person(String name, int age) {

       public Person(String name, int age) {
           if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
           }
      }
}

Kompaktowy projektant

Konstruktory kompaktowe ignorują wszystkie argumenty, łącznie z nawiasami. Odpowiednie pola są przypisywane automatycznie. Na przykład poniższy kod ilustruje koncepcję kompaktowego konstruktora w Records :
public record Person(String name, int age) {

      public Person {
          if (age < 18) {
              throw new IllegalArgumentException("You are not allowed to participate in general elections");
          }
     }
}
Oprócz konstruktora kompaktowego w Record można definiować zwykłe konstruktory , tak jak w zwykłej klasie. Należy jednak upewnić się, że wszyscy konstruktorzy inicjują wszystkie pola rekordu.

Metody w rekordach

Rekordy w Javie automatycznie generują zestaw metod dla każdego pola w rekordzie. Metody te nazywane są metodami akcesorowymi i mają taką samą nazwę jak pole, z którym są powiązane. Na przykład, jeśli rekord zawiera pole o nazwie cena , automatycznie będzie w nim dostępna metoda o nazwie cena() , która zwraca wartość pola ceny . Oprócz automatycznie generowanych metod akcesorowych, możemy także zdefiniować własne metody we wpisie, tak jak w zwykłej klasie. Na przykład:
public record Person(String name, int age) {
   public void sayHello() {
      System.out.println("Hello, my name is " + name);
   }
}
Wpis ten zawiera metodę sayHello() , która wyświetla powitanie przy użyciu pola nazwy .

Jak Record pomaga zredukować szablonowy kod

Spójrzmy na prosty przykład, aby zobaczyć, jak notacje Java mogą pomóc w wyeliminowaniu szablonowego kodu. Załóżmy, że mamy klasę o nazwie Osoba , która reprezentuje osobę z imieniem, wiekiem i adresem e-mail. W ten sposób definiujemy klasę w tradycyjny sposób. Kod bez użycia Record :
public class Person {

    private String name;
    private int age;
    private String email;

    public Person(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    public String getName() {

       return name;
    }

    public int getAge() {
       return age;
    }

    publicString getEmail() {
       return email;
    }

    public void setName(String name) {
       this.name = name;
    }

    public voidsetAge(int age) {
       this.age = age;
    }

    public void setEmail(String email) {
       this.email = email;
    }

    @Override
    public String toString() {
       return "Person{" +
         "name='" + name + '\'' +
         ", age=" + age +
         ", email='" + email + '\'' +
      '}';
    }
}
Jak widzimy, klasa ta wymaga dużej ilości standardowego kodu do zdefiniowania pól, konstruktora, modułów pobierających, ustawiających i metody toString() . Załóżmy dodatkowo, że chcemy, aby ta klasa była niezmienna. Aby to zrobić, będziemy musieli wykonać kilka dodatkowych kroków, takich jak:
  • Zadeklaruj klasę jako ostateczną , aby nie można jej było rozszerzyć.
  • Zadeklaruj wszystkie pola jako prywatne i końcowe , aby nie można było ich zmienić poza konstruktorem.
  • Nie podawaj żadnych metod ustawiających pola.
  • Jeśli którekolwiek z pól można modyfikować, powinieneś zwrócić ich kopię zamiast zwracać oryginalny obiekt.
Kod używając Record :
public record Person(String name, int age, String email) {}
To wszystko! Za pomocą tylko jednego wiersza kodu zdefiniowaliśmy klasę, która ma te same pola, konstruktor, metody pobierające, ustawiające i metodę toString() co tradycyjna klasa. Składnia Record zajmuje się za nas całym kodem standardowym. Z powyższego przykładu jasno wynika, że ​​używanie rekordów w Javie może pomóc w wyeliminowaniu szablonowego kodu, zapewniając bardziej zwięzłą składnię do definiowania klas ze stałym zestawem pól. Do definiowania i używania rekordów potrzeba mniej kodu niż w przypadku tradycyjnego podejścia, a rekordy zapewniają bezpośredni dostęp do pól przy użyciu metod aktualizacji pól. Dzięki temu kod jest bardziej czytelny, łatwiejszy w utrzymaniu i mniej podatny na błędy. Dodatkowo, korzystając ze składni Record , nie musimy robić nic więcej, aby klasa była niezmienna. Domyślnie wszystkie pola w rekordzie są ostateczne , a sama klasa rekordu jest niezmienna. Dlatego utworzenie niezmiennej klasy przy użyciu składni zapisu jest znacznie prostsze i wymaga mniej kodu niż tradycyjne podejście. W przypadku rekordów nie musimy deklarować pól jako final , udostępniać konstruktora inicjującego pola ani udostępniać modułów pobierających dla wszystkich pól.

Typowe klasy rekordów

Java może także definiować ogólne klasy Records . Ogólna klasa wejściowa to klasa wejściowa, która ma jeden lub więcej parametrów typu. Oto przykład:
public record Pair<T, U>(T first, U second) {}
W tym przykładzie Pair jest ogólną klasą rekordów, która przyjmuje dwa parametry typu T i U. Instancję tego rekordu możemy utworzyć w następujący sposób:
Pair<String, Integer>pair = new Pair<>( "Hello" , 123);

Zagnieżdżona klasa wewnątrz Record

W ramach wpisu można także zdefiniować zagnieżdżone klasy i interfejsy. Jest to przydatne do grupowania powiązanych klas i interfejsów oraz może pomóc w ulepszeniu organizacji i łatwości konserwacji bazy kodu. Oto przykład wpisu zawierającego klasę zagnieżdżoną:
public record Person(String name, int age, Contact contact){

    public static class Contact {

       private final String email;
       private final String phone;

       public Contact(String email, String phone){
           this.email = email;
           this.phone = phone;
       }

       public String getEmail(){
          return email;
       }

       public String getPhone(){
          return phone;
       }
    }
}
W tym przykładzie Person jest wpisem zawierającym zagnieżdżoną klasę Contact . Z kolei Contact jest statyczną klasą zagnieżdżoną, która zawiera dwa prywatne pola końcowe: adres e-mail i telefon. Posiada również konstruktor akceptujący adres e-mail i numer telefonu oraz dwie metody pobierające: getEmail() i getPhone() . Możemy utworzyć instancję Person w następujący sposób:
Person person = new Person("John",30, new Person.Contact("john@example.com", "123-456-7890"));
W tym przykładzie utworzyliśmy nowy obiekt Osoba o nazwie Jan , wiek 30 i nowy obiekt Kontakt z adresem e-mail john@example.com i numerem telefonu 123-456-7890 .

Zagnieżdżony interfejs wewnątrz Record

Oto przykładowy wpis zawierający zagnieżdżony interfejs:
public record Book(String title, String author, Edition edition){
    public interface Edition{
       String getName();
   }
}
W tym przykładzie Book jest pozycją zawierającą zagnieżdżony interfejs Edition . Z kolei Edition jest interfejsem definiującym pojedynczą metodę getName() . Instancję Book możemy utworzyć w następujący sposób:
Book book = new Book("The Hitchhiker's Guide to the Galaxy", "Douglas Adams", new Book.Edition() {

   public String getName() {

      return "Science Fiction";
   }
});
W tym przykładzie tworzymy nowy obiekt Book o tytule Autostopem przez Galaktykę Douglasa Adamsa i nową anonimową implementację interfejsu Edition , która zwraca nazwę Science Fiction po wywołaniu metody getName() .

Co jeszcze może zrobić Records?

  • Wpisy mogą definiować konstruktory niestandardowe. Rekordy obsługują sparametryzowane konstruktory, które mogą wywołać konstruktora domyślnego z podanymi parametrami w ich treściach. Ponadto rekordy obsługują także konstruktory kompaktowe, które są podobne do konstruktorów domyślnych, ale mogą zawierać dodatkowe funkcje, takie jak kontrole w treści konstruktora.
  • Jak każda inna klasa w Javie, Record może definiować i używać metod instancji. Oznacza to, że możemy tworzyć i wywoływać metody specyficzne dla klasy nagrywania.
  • W Record definiowanie zmiennych instancji jako członków klasy jest niedozwolone, ponieważ można je określić jedynie jako parametry konstruktora. Rekordy obsługują jednak pola statyczne i metody statyczne, których można używać do przechowywania danych wspólnych dla wszystkich instancji klasy rekordów i uzyskiwania do nich dostępu.

Czy rekord może implementować interfejsy?

Tak, pisanie w Javie może implementować interfejsy. Na przykład poniższy kod ilustruje tę koncepcję:
public interface Printable {
   void print();
}
public record Person(String name, int age) implements Printable {
   public void print() {
      System.out.println("Name: " + name + ", Age: " + age);
   }
}
Tutaj zdefiniowaliśmy interfejs do druku z pojedynczą metodą print() . Zdefiniowaliśmy także wpis Person , który implementuje interfejs Printable . Rekord Osoba ma dwa pola: imię i nazwisko oraz wiek i zastępuje metodę drukowania interfejsu Printable , aby wydrukować wartości tych pól. Możemy utworzyć instancję wpisu Person i wywołać jej metodę drukowania w następujący sposób:
Person person = new Person("John", 30);
person.print();
Spowoduje to wyświetlenie na konsoli nazwy: John, Age: 30 . Jak pokazano w przykładzie, rekordy Java mogą implementować interfejsy tak samo jak zwykłe klasy. Może to być przydatne przy dodawaniu zachowania do wpisu lub upewnianiu się, że wpis jest zgodny z umową zdefiniowaną przez interfejs.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION