1 poin oleh GN⁺ 2024-08-04 | 1 komentar | Bagikan ke WhatsApp
  • Java sedang menyiapkan fitur bahasa preview yang menambahkan penanda nullness pada tipe: Foo! menolak null, sedangkan Foo? sengaja mengizinkannya
  • Foo tanpa penanda tetap memiliki nullness unspecified, sehingga kompatibel dengan kode lama; konversi antar tipe dengan nullness berbeda dapat disertai peringatan atau pemeriksaan runtime
  • Field dan array yang menolak null tidak dapat memakai nilai default null, sehingga field instans harus mendapat definite assignment sebelum pemanggilan super(...), dan array memerlukan nilai awal komponen
  • Jika null dipersempit menjadi Foo!, akan terjadi NullPointerException; pada jalur penyimpanan array dan field, kontaminasi dicegah dengan ArrayStoreException dan FieldStoreException
  • Ini menjadi dasar agar optimisasi seperti flattening value class Valhalla dapat memercayai eksklusi null; penerapan ke pustaka standar atau reinterpretasi otomatis kode lama saat ini bukan tujuan

Fitur preview untuk menyatakan nullness pada tipe Java

  • Menambahkan penanda nullness pada tipe Java untuk menyatakan apakah himpunan nilai tipe tersebut mencakup null
  • Foo! adalah tipe null-restricted, yang mengecualikan null dari himpunan nilainya
  • Foo? adalah tipe nullable, yang secara sengaja memasukkan null ke dalam himpunan nilainya
  • Foo tanpa penanda memiliki nullness unspecified; null dapat muncul, tetapi tidak diketahui apakah keberadaannya disengaja
  • Fitur ini adalah fitur preview yang diaktifkan dengan flag kompilasi dan runtime --enable-preview

Tujuan dan non-tujuan

  • Menyatakan apakah tipe referensi Java mengharapkan null, serta menyediakan peringatan dan pemeriksaan untuk konversi antar tipe dengan perbedaan nullness
  • Tetap kompatibel dengan kode Java yang ada, dan harus dapat diperkenalkan secara bertahap tanpa menimbulkan inkompatibilitas source maupun binary
  • Variabel bertipe yang menolak null harus diinisialisasi sebelum pertama kali dibaca, dan penolakan null dipaksakan saat runtime bahkan pada kelas yang dikompilasi terpisah
  • Menyediakan metadata dan jaminan integritas agar optimisasi runtime seperti flattening value class Valhalla dapat memercayai tipe null-restricted
  • Hal-hal berikut saat ini bukan tujuan
    • Tidak otomatis menafsirkan ulang kode lama
    • Tidak menjadikan semua kemungkinan null sebagai error kompilasi
    • Tidak menambahkan bentuk nullable pada tipe primitif seperti int
    • Tidak menerapkan ekstensi bahasa ke pustaka standar pada tahap saat ini

Mengapa diperlukan

  • Variabel String di Java dapat berisi referensi ke objek String atau null, tetapi tidak ada cara di tingkat bahasa untuk menyatakan mana yang dimaksud
  • Banyak program berasumsi tidak ada null, tetapi perlu upaya tambahan untuk menegakkan hal ini secara konsisten dalam spesifikasi Javadoc dan kode implementasi
  • Jika ekspektasi ini dilanggar, nilai null dapat mengalir di dalam kode implementasi lalu memicu exception di titik yang jauh dari bug asalnya
  • Jika developer menyatakan niat bahwa null ditolak atau diizinkan sebagai bagian dari tipe, feedback waktu kompilasi dan pemeriksaan runtime membantu menemukan null tak terduga lebih dini
  • Dalam Valhalla, variabel bertipe value class dapat dioptimalkan menjadi representasi flattened dari nilai, tetapi jika bit tambahan diperlukan untuk mengenkode null, penggunaan memori dapat meningkat atau optimisasi penyimpanan tidak dapat dilakukan
  • Dalam Amber, nullness kandidat pattern matching dapat memengaruhi penilaian exhaustiveness pada switch, dan nullness type pattern dapat memengaruhi apakah null ikut cocok

Sintaks penanda nullness dan struktur tipe

  • Nullness diperlakukan sebagai bagian intrinsik dari tipe; karena Foo? dan Foo memiliki nullness berbeda, keduanya adalah tipe yang berbeda
  • Baik tipe array maupun tipe komponen array dapat memiliki penanda nullness
    • Foo?[]! adalah tipe array yang array-nya sendiri null-restricted, sedangkan komponennya adalah Foo nullable
    • Pada array multidimensi, penanda dapat ditempatkan setelah setiap pasangan kurung siku, dan secara konvensi dibaca dari kiri ke kanan, dari bagian luar ke bagian dalam
  • Tipe berparameter dan argumen tipe juga dapat memiliki penanda nullness
    • Predicate!<Foo?> adalah Predicate null-restricted, dengan argumen tipe Foo nullable
  • Untuk menyatakan tipe null-restricted atau nullable, ! atau ? harus muncul secara eksplisit di source
  • Di masa mendatang, dapat dipertimbangkan cara menafsirkan semua tipe dalam kelas atau unit kompilasi sebagai null-restricted secara default dan memakai ? hanya sebagai pengecualian, tetapi detailnya merupakan pekerjaan terpisah

Aturan inisialisasi field dan array

  • Dalam Java yang ada, nilai default untuk field bertipe referensi dan komponen array adalah null, tetapi ini tidak sesuai sebagai nilai awal untuk field atau komponen array null-restricted
  • Field dan array null-restricted harus selalu diinisialisasi oleh program sebelum dibaca
  • Jika field instans null-restricted tidak memiliki initializer, field itu harus mendapat definite assignment sebelum pemanggilan super(...) eksplisit atau implisit di setiap konstruktor
    • Flexible Constructor Bodies JEP memungkinkan penulisan kode inisialisasi yang diperlukan di bagian awal konstruktor
    • Dalam early construction context ini, operasi yang berisiko mereferensikan this atau membaca field yang belum diinisialisasi tidak diizinkan
  • Jika field instans null-restricted memiliki initializer, initializer itu dijalankan pada awal setiap konstruktor sebelum pemanggilan super(...)
    • Konstruktor yang memanggil this(...) adalah kasus khusus yang tidak menjalankan initializer, seperti aturan yang ada
  • Field statis null-restricted harus mendapat definite assignment sebelum semua initializer statis dan blok inisialisasi kelas berakhir
    • Jika kelas lain mencoba membaca field tersebut selama inisialisasi kelas, pemeriksaan runtime mendeteksi pembacaan dini dan melempar exception
  • Array dengan tipe komponen null-restricted harus menyediakan nilai awal untuk setiap komponen dalam ekspresi pembuatan array
    • Semua nilai dapat dicantumkan dengan array initializer
    • Sintaks ringkas baru juga dimungkinkan, tetapi sintaksnya masih TBD

Nullness ekspresi dan konversi nullness

  • Compiler Java menentukan nullness semua ekspresi selama proses type checking
  • Nullness referensi variabel berasal dari deklarasi variabel, dan nullness pemanggilan metode berasal dari tipe return metode yang dirujuk
  • Literal null bersifat nullable
  • Sebagian besar ekspresi bertipe referensi lainnya bersifat null-restricted
    • Termasuk literal, konkatenasi string, this, pembuatan instans kelas, pembuatan array, method reference, dan ekspresi lambda
  • Konversi nullness diizinkan dalam konteks assignment, invocation, dan casting
  • Widening nullness conversion mencakup berikut ini
    • Foo!Foo?
    • Foo!Foo unspecified
    • Foo?Foo unspecified
    • Foo unspecified → Foo?
  • Narrowing nullness conversion mencakup berikut ini
    • Foo?Foo!
    • Foo unspecified → Foo!
  • Narrowing nullness conversion dilakukan otomatis oleh compiler, mirip dengan unboxing conversion, tetapi saat runtime dilakukan pemeriksaan dinamis yang dapat menyebabkan NullPointerException
  • Upaya mengonversi literal null secara langsung ke tipe null-restricted adalah error waktu kompilasi

Pemeriksaan runtime dan exception

  • Saat runtime, jika nilai null mengalami narrowing nullness conversion menjadi tipe null-restricted, NullPointerException terjadi
  • Narrowing nullness conversion yang tidak tampak eksplisit dalam source code juga dapat terjadi selama eksekusi runtime
  • Array yang dialokasikan dengan tipe komponen null-restricted menolak nilai null melalui pemeriksaan penyimpanan array biasa, meskipun di source code diperlakukan sebagai tipe yang kurang spesifik
    • Kegagalan konversi ini memicu ArrayStoreException
  • Jika field yang saat kompilasi belum null-restricted kemudian menjadi null-restricted melalui kompilasi terpisah, field store check baru menolak penyimpanan null
    • Kegagalan konversi ini memicu FieldStoreException
  • Pada pemanggilan metode dalam relasi overriding, konversi ke tipe pemanggilan parameter metode super dapat diikuti oleh konversi ke tipe parameter metode overriding
  • Nilai return metode juga dapat dikonversi ke tipe return metode yang dideklarasikan, lalu dikonversi ke tipe return yang diharapkan di titik pemanggilan

Generics, argumen tipe, dan overriding

  • Penggunaan type variable juga dapat diberi penanda nullness; T! adalah tipe null-restricted dan T? adalah tipe nullable
  • Tipe type variable null-restricted dan nullable menegaskan nullness tertentu di dalam kode generik
  • Tipe yang digunakan sebagai argumen tipe juga dapat menyatakan nullness, dan penanda nullness yang ditempelkan pada tipe type variable menimpa nullness yang ditegaskan pada argumen tipe
  • Di dalam implementasi erased dari API generik, pembatasan null tidak dapat dipaksakan
    • Namun cast implisit biasa yang terjadi pada batas API generik memaksakan argumen tipe null-restricted saat runtime
  • Demi interoperabilitas, nullness di dalam argumen tipe tidak dipaksakan secara kuat
    • Predicate<String!> dapat dikonversi menjadi Predicate<String> atau Predicate<String?>
    • unchecked nullness conversion seperti ini dapat menimbulkan peringatan
  • Perubahan nullness tipe komponen array juga diizinkan sebagai unchecked nullness conversion, dan dalam kondisi apa pemeriksaan runtime dilakukan masih TBD
  • Dalam penentuan kesamaan signature metode, nullness diabaikan
    • Sebuah metode dapat meng-override metode lain meskipun nullness parameter dan tipe return tidak cocok
    • Ketidaksesuaian seperti ini bisa umum terjadi jika API yang berbeda memperkenalkan penanda nullness secara independen
  • Nullness tidak memengaruhi applicability metode dan tidak dapat menyebabkan kegagalan inferensi argumen tipe, tetapi dapat memengaruhi nullness yang diinferensikan untuk tipe return metode generik
    • Detail algoritme inferensi masih TBD

Peringatan dan error compiler

  • Menjadikan suatu tipe null-restricted dapat menimbulkan error waktu kompilasi baru
    • Jika field atau array bertipe tersebut belum diinisialisasi
    • Jika ada upaya mengonversi literal null ke tipe tersebut
    • Membandingkan literal null dengan ekspresi bertipe null-restricted juga dapat menjadi error waktu kompilasi
  • Dalam situasi lain, analisis nullness bersifat pendukung dan tidak menimbulkan error waktu kompilasi
  • javac menyediakan peringatan untuk menghindari error runtime, dan IDE serta alat analisis lain dianjurkan mengikuti arah yang sama
  • Kemungkinan penyebab peringatan meliputi
    • Narrowing nullness conversion, termasuk yang berasal dari tipe ?
    • Penggunaan ekspresi bertipe ? untuk akses member atau operasi lain yang null-hostile
    • Nullness argumen tipe yang tidak konsisten dengan bound
    • Nullness parameter metode atau return yang tidak cocok dengan metode yang di-override
    • Unchecked conversion yang mengubah nullness tipe

File kelas, refleksi, dan perubahan pendukung

  • Sebagian besar penggunaan penanda null dihapus dari file class, dan konversi runtime pendamping direpresentasikan langsung dalam bytecode
  • Sintaks atribut Signature diperbarui agar mengizinkan ! dan ? di dalam tipe
  • Nullness tidak dienkode dalam descriptor metode dan field
  • Untuk mencegah kontaminasi field, atribut baru NullRestricted menandai bahwa field tidak mengizinkan nilai null
    • Field ini juga harus ditandai ACC_STRICT, dan harus diinisialisasi secara ketat
    • Verifier memastikan semua field instans dengan strict initialization sudah di-assign saat konstruktor melakukan pemanggilan super(...)
    • Setiap upaya menulis ke field memeriksa nilai null, dan jika ditemukan akan melempar FieldStoreException
  • Pembuatan array null-restricted tidak didukung oleh instruksi anewarray, dan harus dilakukan melalui pemanggilan Reflection API
  • Tidak ada literal Foo!.class atau Foo?.class, dan tidak ada instans java.lang.Class terkait
  • RuntimeType API baru mendeskripsikan himpunan tipe yang dipaksakan saat runtime dalam pemeriksaan penyimpanan array dan field, termasuk varian null-restricted dari semua tipe class dan interface
  • API Field mendukung pengambilan RuntimeType field, dan nilai ini dapat berbeda dari hasil getType
  • API Array mendukung varian newInstance yang menyatakan tipe komponen dengan RuntimeType
    • Varian ini juga mengizinkan pemberian nilai awal komponen array
    • Upaya membuat array null-restricted tanpa nilai awal ditolak
  • Deserialisasi tradisional tidak kompatibel dengan field dan array null-restricted, dan JEP terpisah akan menyediakan mekanisme serialisasi yang tidak mengekspos field dan array null-restricted yang belum diinisialisasi
  • Dokumentasi yang dihasilkan javadoc menyertakan penanda nullness
  • API java.lang.reflect.Type dan javax.lang.model mengenkode nullness dalam representasi tipe

Alternatif dan dependensi

  • Berbagai alat pengembangan di ekosistem Java telah mengimplementasikan pelacakan null waktu kompilasi sendiri, tetapi karena tidak mengubah bahasa Java, sintaksnya terutama terbatas pada annotation dan perilaku yang dapat dipengaruhi juga terbatas pada pemeriksaan waktu kompilasi
  • Bahasa lain melacak nullness dalam sistem tipe, dan banyak bahasa secara default bersifat null-restricted serta menganggap assignment ke tipe null-restricted tanpa pemeriksaan null eksplisit sebagai error
  • Di Java, fitur ini harus opsional dan dapat digunakan bertahap tanpa migrasi besar-besaran sekaligus
  • Pemaksaan nullness saat runtime juga dapat diimplementasikan dengan pemeriksaan eksplisit atau pemanggilan Objects.requireNonNull
    • Namun penerapan yang konsisten merepotkan, membutuhkan dokumentasi tambahan, dan mengurangi keterbacaan program
    • Tidak ada cara untuk menerapkannya langsung ke penyimpanan variabel seperti field dan array
  • Prasyaratnya adalah Flexible Constructor Bodies (Second Preview)
    • Konstruktor dapat menjalankan statement sebelum pemanggilan super(...) dan melakukan assignment ke field instans, sehingga memungkinkan persyaratan inisialisasi field null-restricted
  • Pekerjaan mendatang mencakup berikut ini
  • Kemungkinan peningkatan mendatang tambahan mencakup penerapan penanda nullness pada sebagian API standar, representasi null check yang ringkas dalam bytecode, pemaksaan tingkat rendah yang lebih kuat untuk parameter metode null-restricted, dan mekanisme bahasa yang secara implisit menegaskan semua tipe dalam konteks tertentu sebagai null-restricted

1 komentar

 
GN⁺ 2024-08-04
Komentar Hacker News
  • Menarik melihat bagaimana pendekatan ini berbeda dari cara yang diperkenalkan C# beberapa tahun lalu. Di C#, jika nullability diaktifkan pada proyek, semua variabel secara default dideklarasikan sebagai non-null kecuali secara eksplisit ditandai nullable, sedangkan dalam proposal ini variabel yang sudah ada pada dasarnya menjadi salah satu dari nullable implisit, nullable eksplisit, atau non-nullable eksplisit
    Kotlin juga bahasa JVM, tetapi seperti C# ia menganggap tipe sebagai non-null jika tidak dinyatakan sebaliknya, hanya saja Kotlin tidak punya beban kompatibilitas mundur. Kotlin juga menyediakan jalan keluar melalui lateinit var, yang memungkinkan tipe non-nullable dibiarkan kosong sampai diinisialisasi dari metode lain, dan akan melempar pengecualian khusus jika diakses sebelum inisialisasi
    Saya penasaran kenapa harus ada tiga pilihan. Rasanya cukup mempertahankan variabel tanpa anotasi sebagai nullable, lalu hanya variabel yang diberi anotasi yang menjadi non-null eksplisit. Jika tanpa penanda apa pun sudah otomatis nullable, saya tidak melihat alasan mengapa orang masih ingin mendeklarasikan nullable secara eksplisit
    Saya lebih suka pendekatan C#, tetapi pendekatan ini punya kelebihan karena bisa dipakai di codebase lama tanpa harus membereskan semua masalah nullability lebih dulu. Sebaliknya, C# langsung menampakkan masalah nullability, sedangkan proposal ini memungkinkan semuanya tetap tersembunyi seperti sekarang
    Bagian yang mengatakan “satu metode tetap bisa menimpa metode lain meskipun nullability parameter dan nilai kembalian tidak cocok” juga terasa aneh. Saat meng-override/mengimplementasikan callback, ini terlihat seperti senjata makan tuan ketika metode aslinya menetapkan pengembalian non-null tetapi implementasinya malah mengembalikan null

    • Kami sedang memigrasikan codebase C# lama di kantor ke nullable reference types, dan adanya penanda non-nullable yang eksplisit akan sangat membantu karena langsung menunjukkan bagian yang sudah ditinjau dan dianotasi
      Untuk sementara, karena keterbatasan waktu, kami memilih hanya mengaktifkan anotasi kecuali di beberapa titik penting, tetapi saya masih mempertahankan NotNullAttribute dan CanBeNullAttribute dari JetBrains sebagai penanda agar keputusan yang dibuat secara sadar bisa langsung terlihat. Yang kedua sebenarnya bisa dihapus karena nullable sudah punya penanda eksplisit, tetapi yang pertama bentrok nama dengan fitur bawaan C#
      Dalam konteks itu, tiga pilihan justru cukup diinginkan. Kalau kodenya sudah ratusan ribu baris, migrasi cepat dan mudah memang tidak realistis
      Di proyek internal lain, kami menaburkan #nullable enable di sekitar kode yang sedang disentuh agar cakupan nullability meluas secara bertahap. Kami juga mensyaratkan kode baru harus berada dalam konteks nullable. Ini juga cukup baik untuk menandai bagian yang sudah dianotasi, tetapi pendekatan seperti ini lebih mungkin dilakukan pada codebase dan tim yang jauh lebih kecil
    • Di Kotlin juga ada tipe yang nullability-nya tidak ditentukan, meski tidak bisa didefinisikan langsung oleh pengguna. Kotlin menyebutnya platform types, dan dalam error maupun diagnosis ditampilkan dengan tanda seru seperti String!
      Istilah “platform types” dan simbol tanda seru itu memang agak membingungkan, tetapi selain itu pendekatan Kotlin bekerja cukup baik. Karena Kotlin sejak awal sudah punya nullability, programmer tidak bisa secara langsung menetapkan platform type, tetapi tetap membutuhkannya agar kompatibel dengan platform dasar seperti JVM atau JavaScript yang nullability-nya tidak jelas
      Dalam pendekatan ini, nilai defaultnya tetap masuk akal. Secara default seharusnya selalu non-nullable, dan siapa pun yang berpikir sebaliknya berarti tidak belajar apa pun dari Tony Hoare. Pada saat yang sama, kompatibilitas mundur juga tetap terjaga. Kotlin punya jalan yang lebih mudah, sedangkan Java dan C# juga harus menjaga kompatibilitas dengan source code yang sudah ada
      Baik pendekatan Java maupun C# sama-sama tidak ideal. Pendekatan C# mengubah perilaku kode secara besar bergantung pada compiler flag, sedangkan pendekatan Java menjadikan opsi terburuk sebagai default
      Meski begitu, saya tetap lebih condong ke C#. Jika “mungkin nullable” dijadikan pilihan yang paling mudah, kebanyakan programmer akan memilih itu sebagai default. Ini terasa makin mungkin pada bahasa yang ramah perusahaan seperti Java. Linter dan peringatan compiler akan membantu dalam jangka panjang, tetapi sepertinya butuh bertahun-tahun sampai sebagian besar kode Java benar-benar dianotasi nullability dengan benar. Pengguna C# mungkin akan lebih menderita dalam jangka pendek, tetapi peluang mereka untuk jauh lebih cepat mencapai tujuan berupa nullability yang jelas juga lebih besar
      https://kotlinlang.org/docs/java-interop.html#null-safety-an...
      https://www.infoq.com/presentations/Null-References-The-Bill...
    • Saya setuju ini terasa aneh. Pendekatan yang saya harapkan adalah pada level source code, T? berarti nullable dan T! berarti non-nullable, lalu untuk T biasa pada source yang baru dikompilasi, nilai defaultnya ditetapkan lewat deklarasi mirip pragma per file sumber, per file package-info, atau secara global lewat compiler switch
      Setelah itu, pada rilis Java LTS berikutnya, default globalnya bisa diubah. Dengan begitu transisi proyek jadi mudah, pragma bisa disisipkan/diperbarui secara otomatis, dan jika perlu default lama pun tetap bisa dipertahankan
      Selain itu, kalau anotasi JSR-305 @Nonnull yang sudah ada dimanfaatkan di class file untuk menandai kemunculan tipe non-nullable, kompatibilitas dua arah dengan JDK lama juga sebenarnya bisa disediakan
    • Di bagian akhir ada seksi yang berbunyi “menyediakan mekanisme agar bahasa bisa menegaskan bahwa semua tipe dalam konteks tertentu secara implisit dibatasi null, meskipun programmer tidak menulis simbol ! secara eksplisit”
      Rasanya compiler flag atau semacam tag modul saja sudah cukup baik
    • Kemungkinan alasannya adalah karena Java jauh lebih menekankan kompatibilitas mundur kode dibanding bahasa lain
      Meski begitu, saya rasa nantinya akan ada compiler flag yang menganggap semua hal sebagai non-nullable secara otomatis kecuali dinyatakan lain
  • Kelihatannya bagus. Akhirnya ada cara untuk menghilangkan ribuan exception yang tidak perlu dan pemeriksaan null di tingkat bahasa. Namun, konversi otomatis yang mempersempit status null terasa kurang tepat
    Dari contoh proposal String? id(String! arg) { return arg; }, String s = null;, Object! o1 = s; // NPE, Object o2 = id(s); // NPE, Object o3 = (String!) s; // NPE, rasanya setidaknya dua kasus pertama seharusnya menjadi error kompilasi
    Yang terakhir memang ambigu karena eksplisit, tetapi lebih baik kalau di dalam if (s != null) compiler mengetahui tipe validnya sebagai String! dan mengizinkan String! ss = s;. Dengan begitu tidak ada kemungkinan error

    • Dari sudut pandang orang yang pernah memakai Java di industri, lebih baik pemeriksaan dinamis hanya terjadi saat diminta secara eksplisit dan sisanya semua ditangani secara statis. Bean Validation bekerja dengan baik karena meskipun objek bisa sementara berada dalam keadaan tidak valid, ia dianggap valid pada saat divalidasi secara eksplisit, atau saat framework memvalidasinya sebelum masuk ke kode saya
      Sebenarnya, dibanding cast pada kasus terakhir, saya lebih suka memakai Objects.requireNonNull(s) karena lebih eksplisit. Namun, akan bagus juga kalau ada sesuatu seperti Objects.unsafeForceNonNull(s) yang melewati pemeriksaan eksplisit tetapi hanya dikecualikan untuk kasus yang terhalang optimisasi. Jika ada method unsafe, kita bisa mengimplementasikan requireNonNull sendiri tanpa menambah analisis statis yang rumit
    • Tertulis bahwa “tujuannya bukan mewajibkan program untuk menangani secara eksplisit semua nilai null yang mungkin muncul, dan nilai null yang tidak ditangani bisa menjadi peringatan saat kompilasi tetapi bukan error”
      Sayangnya ini hanya akan terdeteksi saat runtime
    • Jika contoh pertama atau kedua menjadi error kompilasi, itu berarti kita harus memberi anotasi pada setiap titik penggunaan library. Sampai library tersebut bermigrasi ke null type, atau mungkin tidak pernah bermigrasi, hampir setiap baris akan penuh dengan cast. Itu tidak masuk akal
      Misalnya, library standar secara eksplisit menyatakan bahwa setidaknya untuk sementara waktu mereka tidak akan bermigrasi ke null type
    • Jika inti masalahnya adalah memasukkan null ke API, saya tidak yakin itu termasuk error bahasa. Saya paham itu diperlukan agar lebih ringkas dan rapi, tetapi pada akhirnya bukankah masalahnya ada pada developer yang menulis kode seperti itu?
    • Jika yang kedua bukan error kompilasi, maka kode legacy bisa memanggil fungsi yang menerima argumen non-null, tetapi saya tidak yakin itu hal yang baik. Sulit menilai sebelum benar-benar mencobanya
  • Sepertinya sangat perlu ada cara untuk menandai semua variabel sebagai non-null secara default pada level package, atau setidaknya file. Kalau tidak, demi keamanan akan muncul dorongan untuk menulis sintaks T! pada hampir semua variabel, dan itu hanya akan menambah banyak noise

    • Di bawah “kemungkinan perbaikan di masa depan” ada “penyediaan mekanisme dalam bahasa untuk menyatakan bahwa semua tipe dalam konteks tertentu secara implisit dibatasi null, sehingga programmer tidak perlu menulis tanda ! secara eksplisit”
    • Sepertinya tidak terlalu buruk. Di proyek kami sudah ada standar bahwa semua variabel dalam kode Java yang kami miliki bukan null. Jika butuh variabel nullable, harus diberi anotasi @Null
      Masalah hanya muncul di batas kode yang berinteraksi dengan library. Sintaks baru ini sepertinya juga akan mirip begitu
    • Jika linternya agresif, mungkin tipe yang tidak diberi penanda bisa dianggap non-null, dan penandaan nullability hanya diwajibkan di tempat yang memang perlu
  • Bagian “untuk saat ini bukan tujuan untuk menerapkan perbaikan bahasa ke library standar” cukup disayangkan
    Dari pengalaman terpaksa memakai PHP, harus melepas atau memasang kembali properti yang sudah dijamin sebelumnya pada data setiap kali berinteraksi dengan library standar yang sangat besar itu merepotkan
    Java juga perlu memasukkan ekspresivitas seperti ini lebih agresif ke library standar dan menjadikannya warga kelas satu

    • Salah satu masalah terbesar Java adalah karena perbaikannya dilakukan bertahap, kode yang baru sedikit diperbaiki akan menjadi kode legacy lagi pada fitur bertahap berikutnya. Kode legacy yang memakai Optional menjadi beban bagi proposal nullable/non-nullable yang eksplisit. Rasanya tipe record juga seharusnya bisa dibuat default non-nullable
    • Sepertinya Anda membaca “untuk saat ini” sebagai “tidak akan pernah”, padahal sebenarnya lebih ke memisahkan transisi itu karena pekerjaannya sendiri sangat besar
      Selain itu, membaginya menjadi dua tahap membuat fitur ini lebih mudah dirilis sebagai preview dan desainnya bisa dipastikan setelah menerima masukan. Kalau semua dipaksakan sekaligus, hampir tidak ada ruang untuk memperbaikinya secara iteratif di dunia nyata
    • Frasa kuncinya adalah “untuk saat ini”. Kalau fiturnya sudah mapan, saya kira itu akan menjadi target
    • Terpaksa? PHP modern sebenarnya cukup nyaman
  • Saya ingin fitur ini masuk ke Java. Opsi eksplisit di tingkat bahasa seperti T? benar-benar meningkatkan kualitas hidup developer di Kotlin dan TypeScript. Java punya alat seperti NullAway, tetapi merepotkan
    Dukungan di tingkat bahasa menurut saya jauh lebih baik daripada Optional/Maybe. Karena itu membuat kita fokus pada logika yang sebenarnya, alih-alih menaruh kode di rel map/flatMap
    https://github.com/uber/NullAway

    • Jadi ingin merusak kompatibilitas source lebih dari 30 tahun? Kenapa harus begitu
      Bukankah lebih baik pakai bahasa JVM lain saja?
  • Situsnya sedang down sekarang, jadi saya taruh tautan arsipnya: https://web.archive.org/web/20240802081039/https://bugs.open...

    • Tidak mengejutkan kalau ternyata diganti ke “jira” yang dicintai semua orang
  • “Tujuannya bukan menuntut program untuk secara eksplisit menangani semua kemungkinan nilai null yang dapat terjadi, dan nilai null yang tidak tertangani bisa menjadi peringatan saat kompilasi tetapi bukan error” adalah keputusan yang buruk.
    Java pada dasarnya adalah bahasa bertipe statis, jadi saya tidak paham mengapa masih menambahkan perilaku dinamis lain seperti ini. Saya berharap ada cara mudah untuk menaikkan peringatan semacam ini menjadi error

    • Itu tidak bisa dilakukan. Kalau iya, semua program Java yang sudah ada akan rusak. Jika dipaksakan, semua program harus ditulis ulang agar menangani respons null di semua tempat. Dengan penambahan fitur kali ini, penanganan null wajib hanya bisa diterapkan pada operasi yang terkait dengan tipe baru, jadi tidak ada yang rusak.
      Bahasa yang sistem tipenya memaksa penanganan null/nil di semua kasus memang jauh lebih baik. Tetapi Java saat ini bukan bahasa seperti itu. Meski begitu, ini tetap akan menjadi peningkatan besar
  • Sayang sekali pelajaran seperti ini dipelajari terlalu terlambat. Seharusnya secara default non-nullable, secara default immutable, dan secara default memakai cakupan tersempit.
    Dalam desain baru, terlalu sering yang dipilih adalah kemudahan langsung daripada “membiarkannya jatuh ke jalur yang aman”. Default yang aman butuh desain dan pengalaman pengguna yang jauh lebih cermat, tetapi hasil akhirnya hampir semua bahasa, platform, dan teknologi dipenuhi footgun. Teknik sipil dan teknik elektro punya standar, sedangkan perangkat lunak terus mempelajari ulang pelajaran yang sama di bahasa dan teknologi baru setiap sekitar 30 tahun

    • Sayangnya, Java tampaknya masih belum mempelajari aturan non-nullable secara default. Proposal ini memperkenalkan dua tipe baru, tetapi jika ditanya apakah tipe bawaan tanpa anotasi itu nullable, jawabannya tetap “entahlah”
    • Yang disayangkan adalah Java baru mempelajari hal seperti ini terlambat. “Kita” dan “Java” itu tidak sama.
      Java seperti desa di dunia ketiga yang baru belajar bahwa air harus direbus untuk membunuh bakteri
  • Sebagian besar yang saya kerjakan di Facebook adalah memakai Hack. Status null adalah elemen inti dari sistem tipe Hack, dan itu benar-benar menyelesaikan banyak sekali error yang tidak perlu.
    Tentu saja itu bukan berarti Anda sama sekali tidak akan pernah menerima null yang tak terduga. Fitur ini juga ditambahkan belakangan ke bahasa tersebut, jadi masih ada banyak tipe mixed warisan yang mencerminkan akar PHP-nya, yang pada dasarnya berarti apa saja bisa masuk.
    Pertama, saya penasaran bagaimana dengan array yang nullable. Contoh String![] menunjukkan kasus ketika objeknya bisa null, tetapi bagaimana dengan array itu sendiri? Di Java, String labels[] = null; sepenuhnya legal. Jadi apakah harus dideklarasikan seperti String![]! labels;?
    Di Hack, vec $foo berarti foo maupun elemennya tidak null, dan ?vec $foo berarti elemennya non-null tetapi foo sendiri bisa null. Sebenarnya hampir tidak ada alasan untuk memakai array null, jadi default-nya seharusnya tidak boleh null. Hanya saja, Java punya masalah bahwa semua kode warisan mengasumsikan kemungkinan null.
    Pada contoh proposal Object! o1 = s, Object o2 = id(s), Object o3 = (String!) s, saya merasa bukankah nomor 2 dan 3 seharusnya menjadi error kompilasi?
    Terakhir, dibanding casting di Java, saya lebih suka operator pemaksaan as di Hack. Misalnya, foo($b) menghasilkan error kompilasi, $b as A menghasilkan error runtime jika null, dan $a as ?B akan meng-cast jika itu B, jika tidak maka mengembalikan null.
    Pada akhirnya, pertanyaannya adalah apakah ini bisa diterapkan ke Java SDK, dan seperti apa bentuknya di kode warisan.
    https://docs.hhvm.com/hack/types/nullable-types

    • Mungkin saja cukup String![]? labels = null;
  • Hal-hal bagus dari Kotlin tampaknya sekarang semuanya masuk ke Java.
    Meski begitu, saya tetap ingin terus bekerja dengan Kotlin, di mana saya tidak perlu berurusan dengan hal-hal seperti Lombok. Record di Java memang bagus

    • Tetap saja, para programmer Java akan terus memakai idiom lama dan terikat pada codebase lama. Hal-hal seperti ini akan butuh waktu lama sampai dipakai luas, jadi programmer Kotlin tidak perlu khawatir
    • Java punya sejarah panjang mengambil inspirasi dari bahasa lain. Menyenangkan melihatnya terus berkembang dengan kecepatan yang cukup baik sambil sebagian besar tetap menjaga kompatibilitas dengan versi sebelumnya