Saya telah bekerja dengan tipe Opsional baru di Java 8 , dan saya menemukan apa yang tampak seperti operasi umum yang tidak didukung secara fungsional: sebuah "orElseOptional"
Perhatikan pola berikut:
Optional<Result> resultFromServiceA = serviceA(args);
if (resultFromServiceA.isPresent) return result;
else {
Optional<Result> resultFromServiceB = serviceB(args);
if (resultFromServiceB.isPresent) return resultFromServiceB;
else return serviceC(args);
}
Ada banyak bentuk dari pola ini, tetapi intinya adalah menginginkan "orElse" pada opsional yang mengambil fungsi yang menghasilkan opsional baru, yang dipanggil hanya jika yang sekarang tidak ada.
Implementasinya akan terlihat seperti ini:
public Optional<T> orElse(Supplier<Optional<? extends T>> otherSupplier) {
return value != null ? this : other.get();
}
Saya ingin tahu apakah ada alasan mengapa metode seperti itu tidak ada, jika saya hanya menggunakan Opsional dengan cara yang tidak disengaja, dan cara lain apa yang dilakukan orang untuk menangani kasus ini.
Saya harus mengatakan bahwa menurut saya solusi yang melibatkan kelas / metode utilitas khusus tidak elegan karena orang yang bekerja dengan kode saya belum tentu tahu bahwa mereka ada.
Juga, jika ada yang tahu, apakah metode seperti itu akan dimasukkan dalam JDK 9, dan di mana saya bisa mengusulkan metode seperti itu? Bagi saya, ini sepertinya kelalaian yang cukup mencolok ke API.
orElseGet()
apa yang dibutuhkan OP, hanya saja itu tidak menghasilkan sintaks kaskade yang bagus.Jawaban:
Ini adalah bagian dari JDK 9 dalam bentuk
or
, yang mengambilSupplier<Optional<T>>
. Contoh Anda kemudian akan menjadi:return serviceA(args) .or(() -> serviceB(args)) .or(() -> serviceC(args));
Untuk detailnya lihat Javadoc atau posting ini yang saya tulis.
sumber
ifPresent
. Tapi bagaimanapun, saya pikir namaifPresent
itu tidak bagus. Untuk semua metode lain tidak bantalan “yang lain” dalam nama (sepertimap
,filter
,flatMap
), itu tersirat bahwa mereka melakukan apa-apa jika tidak ada nilai hadir, jadi mengapa harusifPresent
...Optional<T> perform(Consumer<T> c)
metode untuk memungkinkan rangkaianperform(x).orElseDo(y)
(orElseDo
sebagai alternatif dari yang Anda usulkanifEmpty
, agar konsisten dalamelse
nama semua metode yang mungkin melakukan sesuatu untuk nilai yang tidak ada). Anda dapat meniru ituperform
di Java 9 melaluistream().peek(x).findFirst()
meskipun itu adalah penyalahgunaan API dan masih tidak ada cara untuk mengeksekusiRunnable
tanpa menentukan aConsumer
pada saat yang sama…Pendekatan "coba layanan" terbersih yang diberikan API saat ini adalah:
Aspek pentingnya bukanlah rangkaian operasi (konstan) yang harus Anda tulis satu kali tetapi betapa mudahnya menambahkan layanan lain (atau mengubah daftar layanan secara umum). Di sini, menambahkan atau menghapus satu saja
()->serviceX(args)
sudah cukup.Karena evaluasi streaming yang lambat, tidak ada layanan yang akan dipanggil jika layanan sebelumnya menampilkan non-kosong
Optional
.sumber
.map(Optional::get)
dengan.findFirst()
akan membuatnya lebih mudah untuk "membaca", misalnya.filter(Optional::isPresent).findFirst().map(Optional::get)
dapat "membaca" seperti "menemukan elemen pertama dalam aliran yang Opsional :: isPresent benar dan kemudian meratakannya dengan menerapkan Opsional :: get"?Ini tidak cantik, tapi ini akan berhasil:
return serviceA(args) .map(Optional::of).orElseGet(() -> serviceB(args)) .map(Optional::of).orElseGet(() -> serviceC(args)) .map(Optional::of).orElseGet(() -> serviceD(args));
.map(func).orElseGet(sup)
adalah pola yang cukup berguna untuk digunakan denganOptional
. Artinya "Jika iniOptional
mengandung nilaiv
, beri akufunc(v)
, jika tidak beri akusup.get()
".Dalam hal ini, kami menelepon
serviceA(args)
dan mendapatkanOptional<Result>
. Jika ituOptional
mengandung nilaiv
, kami ingin mendapatkannyaOptional.of(v)
, tetapi jika kosong, kami ingin mendapatkannyaserviceB(args)
. Bilas-ulangi dengan lebih banyak alternatif.Kegunaan lain dari pola ini adalah
.map(Stream::of).orElseGet(Stream::empty)
.map(Collections::singleton).orElseGet(Collections::emptySet)
sumber
() -> {}
tidak mengembalikanOptional
. Apa yang ingin Anda capai?or(Supplier<Optional<T>>)
Java 9 yang akan datang.map()
di kosongOptional
akan menghasilkan yang kosongOptional
.Mungkin inilah yang Anda cari: Dapatkan nilai dari satu Opsional atau lainnya
Jika tidak, Anda mungkin ingin melihatnya
Optional.orElseGet
. Berikut adalah contoh dari apa yang menurut saya Anda cari:result = Optional.ofNullable(serviceA().orElseGet( () -> serviceB().orElseGet( () -> serviceC().orElse(null))));
sumber
ofNullable
adalah hal paling keren yang pernah saya lihat.Dengan asumsi Anda masih menggunakan JDK8, ada beberapa opsi.
Opsi # 1: Buat metode pembantu Anda sendiri
Misalnya:
public class Optionals { static <T> Optional<T> or(Supplier<Optional<T>>... optionals) { return Arrays.stream(optionals) .map(Supplier::get) .filter(Optional::isPresent) .findFirst() .orElseGet(Optional::empty); } }
Sehingga Anda dapat melakukan:
return Optionals.or( ()-> serviceA(args), ()-> serviceB(args), ()-> serviceC(args), ()-> serviceD(args) );
Opsi # 2: Gunakan perpustakaan
Misalnya Opsional jambu google mendukung
or()
operasi yang benar (seperti JDK9), misalnya:return serviceA(args) .or(() -> serviceB(args)) .or(() -> serviceC(args)) .or(() -> serviceD(args));
(Di mana setiap layanan kembali
com.google.common.base.Optional
, bukanjava.util.Optional
).sumber
Optional<T>.or(Supplier<Optional<T>>)
di dokumen Guava. Apakah Anda punya link untuk itu?Ini adalah tampak seperti cocok untuk pencocokan pola dan antarmuka Option yang lebih tradisional dengan beberapa dan ada implementasi (seperti di Javaslang , FunctionalJava ) atau malas Mungkin implementasi di cyclops-bereaksi .Saya penulis perpustakaan ini.
Dengan cyclops-react, Anda juga dapat menggunakan pencocokan pola struktural pada tipe JDK. Untuk Opsional, Anda dapat mencocokkan kasus saat ini dan yang tidak ada melalui pola pengunjung . itu akan terlihat seperti ini -
import static com.aol.cyclops.Matchables.optional; optional(serviceA(args)).visit(some -> some , () -> optional(serviceB(args)).visit(some -> some, () -> serviceC(args)));
sumber