Pilihan Java sebagai Jenis Pengembalian

1. Pengenalan

The Pilihan jenis diperkenalkan di Jawa 8. Ia menyediakan cara yang jelas dan jelas untuk menyampaikan mesej bahawa ada mungkin tidak nilai, tanpa menggunakan null .

Apabila mendapat jenis pengembalian Pilihan , kami mungkin akan memeriksa apakah nilainya hilang, menyebabkan NullPointerException lebih sedikit dalam aplikasi. Walau bagaimanapun, jenis Pilihan tidak sesuai di semua tempat.

Walaupun kami dapat menggunakannya di mana sahaja yang kami anggap sesuai, dalam tutorial ini, kami akan menumpukan pada beberapa amalan terbaik menggunakan Pilihan sebagai jenis pengembalian.

2. Pilihan sebagai Jenis Pengembalian

An Pilihan jenis boleh menjadi jenis pulangan bagi kebanyakan kaedah kecuali beberapa senario dibincangkan kemudian dalam tutorial.

Sebilangan besar masa, mengembalikan Pilihan adalah baik:

public static Optional findUserByName(String name) { User user = usersByName.get(name); Optional opt = Optional.ofNullable(user); return opt; }

Ini berguna kerana kita dapat menggunakan API Pilihan dalam kaedah panggilan:

public static void changeUserName(String oldFirstName, String newFirstName) { findUserByFirstName(oldFirstName).ifPresent(user -> user.setFirstName(newFirstName)); }

Ia juga sesuai untuk kaedah statik atau kaedah utiliti untuk mengembalikan nilai Pilihan . Walau bagaimanapun, terdapat banyak keadaan di mana kita tidak boleh mengembalikan jenis Pilihan .

3. Bila Tidak Boleh Kembali Pilihan

Kerana Pilihan adalah kelas pembungkus dan nilai, ada beberapa operasi yang tidak dapat dilakukan terhadap objek Pilihan . Berkali-kali, lebih baik mengembalikan jenis sebenar daripada jenis Pilihan .

Secara amnya, bagi pemula dalam POJO, lebih sesuai untuk mengembalikan jenis sebenar, bukan jenis Pilihan . Terutama, penting bagi Entity Beans, Model Data, dan DTO untuk memiliki pemula tradisional.

Kami akan mengkaji beberapa kes penggunaan penting di bawah.

3.1. Serialisasi

Cuba bayangkan kita mempunyai entiti sederhana:

public class Sock implements Serializable { Integer size; Optional pair; // ... getters and setters }

Ini sebenarnya tidak akan berfungsi sama sekali. Sekiranya kita mencuba dan membuat siri ini, kita akan mendapat NotSerializableException :

new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(new Sock()); 

Dan sesungguhnya, semasa membuat siri Opsional mungkin berfungsi dengan perpustakaan lain, ia pasti menambahkan kerumitan yang tidak perlu.

Mari kita lihat aplikasi lain dari ketidakcocokan serialisasi yang sama, kali ini dengan JSON.

3.2. JSON

Aplikasi moden menukar objek Java menjadi JSON sepanjang masa. Sekiranya penerima mendapat jenis Pilihan , kemungkinan besar kita akan melihat beberapa struktur data yang tidak dijangka di JSON akhir.

Katakan kita mempunyai kacang dengan harta pilihan:

private String firstName; public Optional getFirstName() { return Optional.ofNullable(firstName); } public void setFirstName(String firstName) { this.firstName = firstName; }

Oleh itu, jika kita menggunakan Jackson untuk membuat siri pilihan Opsional , kita akan mendapat:

{"firstName":{"present":true}} 

Tetapi, apa yang sebenarnya kita mahukan ialah:

{"firstName":"Baeldung"}

Jadi, Pilihan adalah menyakitkan untuk kes penggunaan siri. Seterusnya, mari lihat sepupu untuk membuat siri: menulis data ke pangkalan data.

3.3. JPA

Dalam JPA, penerima, pengatur, dan bidang harus memiliki nama dan juga jenis perjanjian. Sebagai contoh, medan FirstName jenis String harus dipasangkan dengan getter bernama getFirstName yang juga mengembalikan String.

Mengikuti konvensyen ini menjadikan beberapa perkara lebih mudah, termasuk penggunaan refleksi oleh perpustakaan seperti Hibernate, untuk memberi kita sokongan pemetaan Objek-Relasional yang hebat.

Mari kita lihat kes penggunaan yang sama dengan nama depan pilihan dalam POJO.

Namun, kali ini, ia akan menjadi entiti JPA:

@Entity public class UserOptionalField implements Serializable { @Id private long userId; private Optional firstName; // ... getters and setters }

Dan mari teruskan dan cuba bertahan:

UserOptionalField user = new UserOptionalField(); user.setUserId(1l); user.setFirstName(Optional.of("Baeldung")); entityManager.persist(user);

Malangnya, kami mengalami ralat:

Caused by: javax.persistence.PersistenceException: [PersistenceUnit: com.baeldung.optionalReturnType] Unable to build Hibernate SessionFactory at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1015) at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:941) at org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:56) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:79) at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54) at com.baeldung.optionalReturnType.PersistOptionalTypeExample.(PersistOptionalTypeExample.java:11) Caused by: org.hibernate.MappingException: Could not determine type for: java.util.Optional, at table: UserOptionalField, for columns: [org.hibernate.mapping.Column(firstName)]

Kami boleh mencuba menyimpang dari standard ini. Sebagai contoh, kami dapat menyimpan harta itu sebagai tali , tetapi menukar pengambil:

@Column(nullable = true) private String firstName; public Optional getFirstName() { return Optional.ofNullable(firstName); }

Nampaknya kita boleh mempunyai dua cara: mempunyai jenis pengembalian Pilihan untuk penerima dan bidang nama pertama yang boleh bertahan .

Namun, sekarang kita tidak konsisten dengan pengambil, pengatur, dan bidang kita, akan lebih sukar untuk memanfaatkan lalai JPA dan alat kod sumber IDE.

Sehingga JPA mendapat sokongan elegan dari jenis Pilihan , kita harus mematuhi kod tradisional. Lebih mudah dan lebih baik:

private String firstName; // ... traditional getter and setter

Mari kita lihat bagaimana ini mempengaruhi bahagian depan - periksa untuk mengetahui apakah masalah yang kita hadapi terdengar biasa.

3.4. Bahasa Ekspresi

Menyiapkan DTO untuk bahagian depan menghadirkan kesukaran yang serupa.

Sebagai contoh, mari kita bayangkan bahawa kita menggunakan templat JSP untuk membaca Nama pertama DTO UserOptional dari permintaan:

Oleh kerana itu adalah Pilihan , kita tidak akan melihat " Baeldung ". Sebagai gantinya, kita akan melihat perwakilan String dari jenis Pilihan :

Optional[Baeldung] 

Dan ini bukan masalah hanya dengan JSP. Sebarang bahasa templat, baik Velocity, Freemarker, atau yang lain, perlu menambahkan sokongan untuk ini. Sehingga itu, mari terus memastikan DTO kami tetap sederhana.

4. Kesimpulan

Dalam tutorial ini, kita telah belajar bagaimana kita dapat mengembalikan objek Pilihan , dan bagaimana menangani nilai pulangan seperti ini.

Sebaliknya, kami juga mengetahui bahawa terdapat banyak senario yang lebih baik kami tidak menggunakan jenis pengembalian pilihan untuk mendapatkan. Walaupun kita dapat menggunakan jenis Opsional sebagai petunjuk bahawa mungkin tidak ada nilai non-nol, kita harus berhati-hati untuk tidak menggunakan terlalu banyak jenis pengembalian Pilihan , terutama dalam mendapatkan kacang entiti atau DTO.

Kod sumber contoh dalam tutorial ini boleh didapati di GitHub.