Penggunaan yang tepat dari Opsional.ifPresent ()

99

Saya mencoba memahami ifPresent()metode OptionalAPI di Java 8.

Saya memiliki logika sederhana:

Optional<User> user=...
user.ifPresent(doSomethingWithUser(user.get()));

Tetapi ini menghasilkan kesalahan kompilasi:

ifPresent(java.util.functionError:(186, 74) java: 'void' type not allowed here)

Tentu saja saya bisa melakukan sesuatu seperti ini:

if(user.isPresent())
{
  doSomethingWithUser(user.get());
}

Tapi ini persis seperti nullcek yang berantakan .

Jika saya mengubah kodenya menjadi ini:

 user.ifPresent(new Consumer<User>() {
            @Override public void accept(User user) {
                doSomethingWithUser(user.get());
            }
        });

Kode menjadi semakin kotor, yang membuat saya berpikir untuk kembali ke nullcek lama .

Ada ide?

rayman
sumber

Jawaban:

160

Optional<User>.ifPresent()mengambil Consumer<? super User>argumen sebagai. Anda memberikan ekspresi yang tipenya kosong. Jadi itu tidak bisa dikompilasi.

Konsumen dimaksudkan untuk diimplementasikan sebagai ekspresi lambda:

Optional<User> user = ...
user.ifPresent(theUser -> doSomethingWithUser(theUser));

Atau bahkan lebih sederhana lagi, menggunakan referensi metode:

Optional<User> user = ...
user.ifPresent(this::doSomethingWithUser);

Ini pada dasarnya sama dengan

Optional<User> user = ...
user.ifPresent(new Consumer<User>() {
    @Override
    public void accept(User theUser) {
        doSomethingWithUser(theUser);
    }
});

Idenya adalah bahwa doSomethingWithUser()pemanggilan metode hanya akan dijalankan jika pengguna hadir. Kode Anda menjalankan panggilan metode secara langsung, dan mencoba meneruskan hasil kosongnya ke ifPresent().

JB Nizet
sumber
2
Kode itu semakin berantakan .. pemeriksaan nol akan jauh lebih bersih. bukankah menurut Anda khusus bahwa doSomethingWithUser bukanlah metode statis
rayman
4
Kode yang mana Yang harus Anda gunakan adalah yang kedua, yang memanggil metode instance (yaitu non-statis) doSomethingWithUser (). Saya tidak melihat bagaimana itu berantakan. Kode terakhir ada untuk menjelaskan kepada Anda persamaan lambda di dunia pra-lambda. Jangan gunakan itu.
JB Nizet
2
Ya, tetapi Anda mungkin terbiasa dengan kelas anonim dan dengan demikian memahami apa yang dilakukan lambda dengan melihat kelas anonim yang setara. Itulah intinya.
JB Nizet
1
Anda tidak perlu memodifikasi apa pun. Biarkan apa adanya, dan gunakan contoh kedua:user.ifPresent(this::doSomethingWithUser);
JB Nizet
11
@rayman Jika Anda memiliki fungsi yang mengembalikan, Optional<User>seringkali tidak perlu menyimpannya dalam variabel lokal. Cukup rantai panggilan metode:funcThatMightReturnUser().ifPresent(this::doSomethingWithUser);
Stuart Marks
21

Selain jawaban @ JBNizet, kasus penggunaan umum saya ifPresentadalah untuk menggabungkan .isPresent()dan.get() :

Cara lama:

Optional opt = getIntOptional();
if(opt.isPresent()) {
    Integer value = opt.get();
    // do something with value
}

Jalan baru:

Optional opt = getIntOptional();
opt.ifPresent(value -> {
    // do something with value
})

Ini, bagi saya, lebih intuitif.

cst1992
sumber
9

Mengapa menulis kode yang rumit padahal Anda bisa membuatnya sederhana?

Memang, jika Anda benar-benar akan menggunakan Optionalkelas, kode paling sederhana adalah yang telah Anda tulis ...

if (user.isPresent())
{
    doSomethingWithUser(user.get());
}

Kode ini memiliki keuntungan menjadi

  1. mudah dibaca
  2. mudah untuk di-debug (breakpoint)
  3. tidak rumit

Hanya karena Oracle telah menambahkan Optionalkelas di Java 8 tidak berarti bahwa kelas ini harus digunakan di semua situasi.

schlebe
sumber
1
Manfaat utama menggunakan ifPresent adalah menghilangkan kebutuhan Anda untuk memanggil get () secara manual. Memanggil get () secara manual rawan kesalahan, karena mudah untuk lupa memeriksa isPresent dulu, tetapi Anda tidak mungkin lupa jika menggunakan ifPresent
dustinroepsch
1
Ok dan setiap kali Anda akan menggunakan objek 'user' Anda harus memanggil .ifPresent (). Kode akan segera menjadi tidak dapat dibaca karena Anda akan membaca .ifPresent () terlalu banyak waktu!
schlebe
2
Untuk memperbaiki kesalahan ejaan di halaman profil Anda ( VB.Net , Netbeans , SqlServer , PostGresql , MySql , dan Linq, Anda dapat menggunakan layanan saya . Ada juga daftar kata yang sesuai .
Peter Mortensen
7

Gunakan flatMap. Jika ada nilai, flatMap mengembalikan Stream berurutan yang hanya berisi nilai itu, jika tidak, mengembalikan Stream kosong. Jadi tidak perlu digunakan ifPresent(). Contoh:

list.stream().map(data -> data.getSomeValue).map(this::getOptinalValue).flatMap(Optional::stream).collect(Collectors.toList());
Taras Melnyk
sumber
3
Opsional :: streaming membutuhkan java9
avmohan
7

Anda dapat menggunakan referensi metode seperti ini:

user.ifPresent(ClassNameWhereMethodIs::doSomethingWithUser);

Metode ifPresent()dapatkan Consumerobjek sebagai paremeter dan (dari JavaDoc ): "Jika ada nilai, panggil konsumen yang ditentukan dengan nilai tersebut." Nilai itu adalah variabel Anda user.

Atau jika metode ini doSomethingWithUserada di Userkelas dan tidak static, Anda dapat menggunakan referensi metode seperti ini:

user.ifPresent(this::doSomethingWithUser);
Aleksandr Podkutin
sumber
1
Tapi doSomethingWithUser bukanlah metode statis juga bukan kelas.
rayman
@rayman Ok, jika tidak statis Anda dapat melakukan seperti ini:user.ifPresent(new ClassNameWhereMethodIs()::doSomethingWithUser);
Aleksandr Podkutin
7
@AleksandrPodkutin Anda tidak boleh membuat instance baru dari kelas hanya untuk menjalankan satu metode, dari OP sepertinya metode tersebut berada di kelas yang sama dengan yang dipanggil, jadi dia harus menggunakanuser.ifPresent(this::doSomethingWithUser);
Marv
@Marv Saya tidak melihat bentuk penegasan OP bahwa itu di kelas yang sama. Tetapi jika Anda memiliki perasaan seperti itu, saya setuju bahwa dia harus menggunakan user.ifPresent(this::doSomethingWithUser);. Saya akan menambahkannya ke jawaban saya.
Aleksandr Podkutin