JavaRush /Java Blog /Random-TL /Coffee break #128. Gabay sa Java Records

Coffee break #128. Gabay sa Java Records

Nai-publish sa grupo
Pinagmulan: abhinavpandey.dev Sa tutorial na ito, tatalakayin namin ang mga pangunahing kaalaman sa paggamit ng Mga Tala sa Java. Ang mga rekord ay ipinakilala sa Java 14 bilang isang paraan upang alisin ang boilerplate code sa paligid ng paglikha ng mga bagay na Halaga habang sinasamantala ang mga hindi nababagong bagay. Coffee break #128.  Gabay sa Java Records - 1

1. Pangunahing konsepto

Bago tayo pumasok sa mismong mga entri, tingnan natin ang problemang niresolba nila. Para magawa ito, kailangan nating tandaan kung paano nilikha ang mga value object bago ang Java 14.

1.1. Mga bagay na may halaga

Ang mga value na bagay ay isang mahalagang bahagi ng mga aplikasyon ng Java. Nag-iimbak sila ng data na kailangang ilipat sa pagitan ng mga layer ng application. Ang isang value object ay naglalaman ng mga field, constructor, at mga pamamaraan para sa pag-access sa mga field na iyon. Nasa ibaba ang isang halimbawa ng value object:
public class Contact {
    private final String name;
    private final String email;

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

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }
}

1.2. Pagkakapantay-pantay sa pagitan ng mga bagay na Halaga

Ang mga value object ay maaari ding magbigay ng paraan upang ihambing ang mga ito para sa pagkakapantay-pantay. Bilang default, inihahambing ng Java ang pagkakapantay-pantay ng mga bagay sa pamamagitan ng paghahambing ng kanilang memory address. Gayunpaman, sa ilang mga kaso, ang mga bagay na naglalaman ng parehong data ay maaaring ituring na pantay. Upang ipatupad ito, maaari nating i-override ang mga katumbas at .hashCode na pamamaraan . Ipatupad natin ang mga ito para sa klase ng Contact :
public class Contact {

    // ...

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Contact contact = (Contact) o;
        return Object.equals(email, contact.email) &&
                Objects.equals(name, contact.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, email);
    }
}

1.3. Kawalang pagbabago ng mga bagay na Halaga

Dapat na hindi nababago ang mga value object. Nangangahulugan ito na dapat nating limitahan ang mga paraan na maaari nating baguhin ang mga patlang ng isang bagay. Ito ay ipinapayong para sa mga sumusunod na kadahilanan:
  • Upang maiwasan ang panganib ng aksidenteng pagbabago ng halaga ng field.
  • Upang matiyak na ang mga pantay na bagay ay mananatiling pareho sa buong buhay nila.
Dahil ang klase ng Contact ay hindi na nababago, kami ngayon:
  1. ginawang pribado at pinal ang mga patlang .
  2. nagbigay lang ng getter para sa bawat field (walang setter ).

1.4. Pagrerehistro ng mga bagay na Halaga

Kadalasan kailangan nating irehistro ang mga halaga na nakapaloob sa mga bagay. Ginagawa ito sa pamamagitan ng pagbibigay ng toString method . Sa tuwing ang isang bagay ay nakarehistro o naka-print, ang toString method ay tinatawag na . Ang pinakamadaling paraan dito ay ang pag-print ng halaga ng bawat field. Narito ang isang halimbawa:
public class Contact {
    // ...
    @Override
    public String toString() {
        return "Contact[" +
                "name='" + name + '\'' +
                ", email=" + email +
                ']';
    }
}

2. Bawasan ang mga template na may Records

Dahil ang karamihan sa mga bagay na may halaga ay may parehong mga pangangailangan at pag-andar, mainam na gawing simple ang proseso ng paglikha ng mga ito. Tingnan natin kung paano nakakatulong ang mga recording na makamit ito.

2.1. Pag-convert ng klase ng Tao sa Record

Gumawa tayo ng isang entry sa klase ng Contact na may parehong functionality tulad ng tinukoy na klase ng Contact sa itaas.
public record Contact(String name, String email) {}
Ginagamit ang record na keyword upang lumikha ng klase ng Record . Ang mga rekord ay maaaring iproseso ng tumatawag sa parehong paraan tulad ng isang klase. Halimbawa, upang lumikha ng bagong instance ng entry, maaari naming gamitin ang bagong keyword .
Contact contact = new Contact("John Doe", "johnrocks@gmail.com");

2.2. Default na Gawi

Binawasan namin ang code sa isang linya. Ilista natin kung ano ang kasama dito:
  1. Ang mga field ng pangalan at email ay pribado at pinal bilang default.

  2. Tinutukoy ng code ang isang "canonical constructor" na kumukuha ng mga field bilang mga parameter.

  3. Naa-access ang mga field sa pamamagitan ng mga getter-like na pamamaraan - name() at email() . Walang setter para sa mga patlang, kaya ang data sa bagay ay nagiging hindi nababago.

  4. Ipinatupad ang paraan ng toString upang i-print ang mga patlang tulad ng ginawa namin para sa klase ng Contact .

  5. Ipinatupad ang mga katumbas at .hashCode na pamamaraan . Kasama sa mga ito ang lahat ng field, tulad ng klase ng Contact .

2.3 Tagabuo ng kanonikal

Kinukuha ng default na constructor ang lahat ng mga field bilang mga parameter ng input at itinatakda ang mga ito sa mga field. Halimbawa, ang default na Canonical Constructor ay ipinapakita sa ibaba:
public Contact(String name, String email) {
    this.name = name;
    this.email = email;
}
Kung tutukuyin natin ang isang constructor na may parehong lagda sa recording class, ito ang gagamitin sa halip na ang canonical constructor.

3. Paggawa gamit ang mga talaan

Maaari naming baguhin ang pag-uugali ng entry sa maraming paraan. Tingnan natin ang ilang mga kaso ng paggamit at kung paano makamit ang mga ito.

3.1. Ino-override ang mga default na pagpapatupad

Maaaring baguhin ang anumang default na pagpapatupad sa pamamagitan ng pag-override dito. Halimbawa, kung gusto nating baguhin ang gawi ng toString method , maaari natin itong i-override sa pagitan ng mga curly braces {} .
public record Contact(String name, String email) {
    @Override
    public String toString() {
        return "Contact[" +
                "name is '" + name + '\'' +
                ", email is" + email +
                ']';
    }
}
Katulad nito, maaari nating i-override ang equals at hashCode na mga pamamaraan .

3.2. Mga compact construction kit

Minsan gusto naming gumawa ang mga constructor ng higit pa sa pagsisimula ng mga field. Upang gawin ito, maaari naming idagdag ang mga kinakailangang operasyon sa aming entry sa Compact Constructor. Tinatawag itong compact dahil hindi nito kailangang tukuyin ang field initialization o listahan ng parameter.
public record Contact(String name, String email) {
    public Contact {
        if(!email.contains("@")) {
            throw new IllegalArgumentException("Invalid email");
        }
    }
}
Tandaan na walang listahan ng mga parameter, at ang pagsisimula ng pangalan at email ay nangyayari sa background bago isagawa ang pagsusuri.

3.3. Pagdaragdag ng mga Konstruktor

Maaari kang magdagdag ng maramihang mga konstruktor sa isang tala. Tingnan natin ang ilang halimbawa at limitasyon. Una, magdagdag tayo ng mga bagong wastong konstruktor:
public record Contact(String name, String email) {
    public Contact(String email) {
        this("John Doe", email);
    }

    // replaces the default constructor
    public Contact(String name, String email) {
        this.name = name;
        this.email = email;
    }
}
Sa unang kaso, ang default na tagabuo ay ina-access gamit ang keyword na ito . Ino-override ng pangalawang constructor ang default na constructor dahil mayroon itong parehong listahan ng parameter. Sa kasong ito, ang entry mismo ay hindi gagawa ng default na constructor. Mayroong ilang mga paghihigpit sa mga konstruktor.

1. Ang default na constructor ay dapat palaging tinatawag mula sa anumang iba pang constructor.

Halimbawa, ang code sa ibaba ay hindi mag-compile:
public record Contact(String name, String email) {
    public Contact(String name) {
        this.name = "John Doe";
        this.email = null;
    }
}
Tinitiyak ng panuntunang ito na palaging sinisimulan ang mga field. Ginagarantiyahan din na ang mga operasyong tinukoy sa compact constructor ay palaging isinasagawa.

2. Hindi posibleng i-override ang default na constructor kung ang isang compact constructor ay tinukoy.

Kapag tinukoy ang isang compact constructor, awtomatikong nagagawa ang isang default na constructor na may initialization at compact constructor logic. Sa kasong ito, hindi kami papayagan ng compiler na tukuyin ang isang constructor na may parehong mga argumento bilang default na constructor. Halimbawa, sa code na ito ang compilation ay hindi mangyayari:
public record Contact(String name, String email) {
    public Contact {
        if(!email.contains("@")) {
            throw new IllegalArgumentException("Invalid email");
        }
    }
    public Contact(String name, String email) {
        this.name = name;
        this.email = email;
    }
}

3.4. Pagpapatupad ng mga Interface

Tulad ng anumang klase, maaari naming ipatupad ang mga interface sa mga talaan.
public record Contact(String name, String email) implements Comparable<Contact> {
    @Override
    public int compareTo(Contact o) {
        return name.compareTo(o.name);
    }
}
Mahalagang paalaala. Upang matiyak ang kumpletong kawalan ng pagbabago, ang mga talaan ay hindi maaaring mamana. Ang mga entry ay pinal at hindi maaaring palawakin. Hindi rin sila maaaring mag-extend ng ibang klase.

3.5. Pagdaragdag ng mga Paraan

Bilang karagdagan sa mga konstruktor, na nag-o-override sa mga pamamaraan at pagpapatupad ng interface, maaari rin kaming magdagdag ng anumang mga pamamaraan na gusto namin. Halimbawa:
public record Contact(String name, String email) {
    String printName() {
        return "My name is:" + this.name;
    }
}
Maaari din kaming magdagdag ng mga static na pamamaraan. Halimbawa, kung gusto naming magkaroon ng static na paraan na nagbabalik ng regular na expression kung saan maaari naming suriin ang email, maaari naming tukuyin ito tulad ng ipinapakita sa ibaba:
public record Contact(String name, String email) {
    static Pattern emailRegex() {
        return Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);
    }
}

3.6. Pagdaragdag ng mga patlang

Hindi kami makakapagdagdag ng mga field ng instance sa isang record. Gayunpaman, maaari kaming magdagdag ng mga static na field.
public record Contact(String name, String email) {
    private static final Pattern EMAIL_REGEX_PATTERN = Pattern
            .compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);

    static Pattern emailRegex() {
        return EMAIL_REGEX_PATTERN;
    }
}
Tandaan na walang mga implicit na paghihigpit sa mga static na field. Kung kinakailangan, maaaring available ang mga ito sa publiko at hindi pangwakas.

Konklusyon

Ang mga tala ay isang mahusay na paraan upang tukuyin ang mga klase ng data. Ang mga ito ay mas maginhawa at makapangyarihan kaysa sa JavaBeans/POJO na diskarte. Dahil madaling ipatupad ang mga ito, dapat na mas gusto ang mga ito kaysa sa iba pang paraan ng paglikha ng mga value object.
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION