4 ms·
> A method that returns Optional<T> may return null. projects that do this drive me bananas If I had the emotional energy, I'd open a JEP for a new @java.lang
by mdaniel 1y ago
> A method that returns Optional<T> may return null.
projects that do this drive me bananas
If I had the emotional energy, I'd open a JEP for a new @java.lang.NonNullReference and any type annotated with it would be a compiler error to assign null to it
public interface Alpha {}
@java.lang.NonNullReference
public interface Beta {}
Alpha a = null; // ok
Beta b = null; // compiler error
javac will tolerate this
Beta b;
if (Random.randBoolean()) {
b = getBeta();
} else {
b = newBeta();
}
but I would need to squint at the language specification to see if dead code elimination is a nicety or a formality
Beta b;
if (true) {
b = getBeta();
} else {
b = null; // I believe this will be elided and thus technically legal
}
- Spivak 1y agoI question the wisdom of even having Optional<T> in a language with nulls. It would raise some eyebrows if a function in Python returned an Optional type object rather than T | None. You have to do a check either way unless you're doing some cute monad-y stuff.
- catlover76 1y agoI think I get what you mean, but it's confusing ofc, because Optional[T] is the older type annotation in Python for T | None But like, when it comes to unwrappable optional/result thingies, there are libs in Python to mimic that kind of behavior from Rust and functional languages.
- singron 1y agoMaybe this is cute monady stuff, but there isn't an equivalent to Optional<Optional<T>> with only null/None. You usually don't directly write that, but you might incidentally instantiate that type when composing generic code, or a container/function won't allow nulls.
- charcircuit 1y agoIn what context would you not want to treat Optional.of(null) and null as the same? It shouldn't be a big deal.
- wiml 1y agoOften people use optional or nullable types as a convenient approximation to an Either type.
- charcircuit 1y agoI still don't see why it would be a problem merging then down even when used like an either. If there is no value then there is no value.
- unnah 1y agoHow about a situation where the inner Optional<T> is acquired from another system or database, and the outer Optional<Optional<T>> is a local cache of the value. If the outer Optional is null, then you need to query the other system. If the outer Optional is filled and the inner Optional is null, then you know that the other system explicitly has no value for the data item, and can skip the query. Seems like using nested optionals would be natural here, although of course alternative representations are possible.
- bippihippi1 1y agowhat is the advantage of using that over 2 variables? the cost is extra mental load, what does it buy?
- kelnos 1y agoIn JSON/REST API bindings, where a deserializer maps JSON to language-native object/struct type, I'll often need to know the difference between: {} and { "foo": null } and { "foo": 42 } So I'll represent that (in e.g. Rust) as: struct Whatever { foo: Option<Option<u32>>, } None means not present, Some(None) means present but null, and Some(Some(42)) means present with a value. I'll often use this in PATCH endpoints, where not-present means to leave the current value alone, null means to unset it, and a value means to set to that value.
- crooked-v 1y agoThere's a lot of quality-of-life stuff enabled by it in Java, since the base language's equivalents to Optional.empty(), Optional.ofNullable(...).orElse(...), etc are painfully verbose by comparison.
- hibikir 1y agoIt works quite well in Scala, which still tolerates nulls due to being in the JVM and having Java interop. Realistically nothing in the language is going to return null, so the only time you might have to care is when you call Java classes, and all of the Java standard library comes scalaified into having no nulls. And yes, there are enough monadic behavior in the standard library to make Option and Either quite useful, instead of just sum types. Java really suffers with optional because the language has such love for backwards compatibility that it's extremely unlikely that nulls would even be removed from the standard library in the first place. The fact that the ecosystem relies on ugly auto wiring hacks instead of mandating explicit constructors doesn't help either.
- eastbound 1y ago> because the language has such love for backwards compatibility I still remember when Java 9 introduced modules. And I’m currently pulling my hair because Java 21 renamed all javax.* into jakarta.* because Javax was a trademark of Oracle, and all libs now require a “-jakartax” version for JDK 21. But somehow I still have to deal with nulls everywhere and erased-at-runtime generics because Java loves backwards compatibility so much. The simple fact all libs released a “-jakartax” proves the entire ecosystem is fully maintained (plus CVEs means unmaintained libs aren’t allowed in production), so they could very well release a -jdk25 version with non-null types.
- codr7 1y agoYou're far from alone, it does make it a tiny bit easier to see which functions are expected to return null, but that's about it and messing around with it always feels like wasted effort.
- natdempk 1y agoPython has Optional[T] defined as T | None in the stdlib https://docs.python.org/3/library/typing.html#typing.Optional https://docs.python.org/3/library/typing.html#typing.Optiona...
- joe_fishfish 1y agoIn Kotlin this would already be a compile error, no need for another annotation.
- mdaniel 1y agoYeah, sure, and thankfully everyone has already switched all their teams to superior languages Anyway, I believe what you're referring to is the "?" syntax that annotates types in Kotlin but doesn't help the resulting bytecode, which means that every single library ever would need to convert to kotlin to benefit fun doit() : java.io.InputStream? { return null } kotlinc test.kt javap -c test.class public final java.io.InputStream doit(); Code: 0: aconst_null 1: areturn So even they didn't have the courtesy of marking the result of a known Optional result as Optional<java.io.InputStream> when interfacing with the existing Java ecosystem