#Null handling like in C#

1 messages · Page 1 of 1 (latest)

quiet violet
#

Basically in c# you have the ? operator which like returns null early so you can just chain possible null values without having NPE's when one of them is null. Now I know java doesn't have this. But is there some workaround with like a method or something or do i need to chain a ton of if statements?

lime elbowBOT
#

<@&987246399047479336> please have a look, thanks.

#

Here is an AI assisted attempt to answer your question 🤖. Maybe it helps! In any case, a human is on the way 👍. To continue talking to the AI, you can use </chatgpt:1108714622413963314>.

#
TJ-Bot
Basically in c# you have the ? operator which like returns null early so you can just chain possible null values without having NPE's when one of them is null. Now I know java doesn't have this. But is there some workaround with like a method or something

In Java, there is no built-in operator like the ? operator in C# for handling null values. However, you can use methods like Optional.ofNullable() or Objects.requireNonNullElse() to handle null values safely and prevent NullPointerExceptions. These methods allow you to provide default values or perform actions if a value is null.

Useful links:

cyan ridge
#

but yeah for most things: just use ifs

#
var n = Optional.ofNullable(s)
  .map(Thing::name)
  .map(o -> o.length())
  .orElse(null);
#

like this can work - sometimes

quiet violet
#

Oh

#

One of the things that C# does way better. Like why does java not have that yet

cyan ridge
#

there is:

quiet violet
#

But java has the best enums ever so I guess it cancels out

cyan ridge
#
  1. Representing null in types
#

so like String? as a nullable string

quiet violet
#

Te

#

Ye

cyan ridge
#

and also "non-null" strings (String!)

#

they are actively working on that

#
  1. "null chaining"
quiet violet
#

There is also shorter null checks

cyan ridge
#

which might feel like an obvious feature, but there is more nuance than you'd expect

quiet violet
#

?: I think

#

Or :?

#

One of the 2

cyan ridge
#

whatever the case: null types come first

#

my guess is that ?., ?:, etc. might not come ever or for a long time: they aren't as obvious as some people think

quiet violet
#

Like if you get a null reference exception in C# you are probably just using a ! To ignore everything cuz like

cyan ridge
#

the main reason is that they make writing code easier - ?. is easier to type than a whole if ... == null) check

quiet violet
#

IT just doesn’t allow it rly

cyan ridge
#

?. is a very small visual signal for an important branch in control flow

quiet violet
#

Ive never had issues with that

cyan ridge
# quiet violet Not rly

just know the language designers are aware of all the features people want and are working on the first one

cyan ridge
# quiet violet Ive never had issues with that
  return {
    statusCode: 200,
    responseBody: {
      id: organization_id,
      displayName: result?.identity_organization_by_pk?.display_name,
      profileImageUrl: result?.identity_organization_by_pk?.profile_image_url,
      email: result?.identity_organization_by_pk?.mainEmail?.[0]?.value,
      agency: result?.identity_organization_by_pk?.agency && {
        displayName: result.identity_organization_by_pk.agency.display_name,
        profileImageUrl:
          result.identity_organization_by_pk.agency.profile_image_url,
      },
      lumanuId: result?.identity_organization_by_pk?.lumanuId?.[0]?.value,
    },
  };
#

consider this typescript

#

notice how the ?.s "fade into the background" when you read the code?

#

i'm not saying there aren't positives, but its not like it has no negatives

#

if you want nullable types in java today there is this

storm merlin
quiet violet
storm merlin
#

bc its not just about adding it but adding it the right way

#

yes

#

if u follow recent talks by brian and friends ull see it being a topic

quiet violet
#

what about extension methods

#

those are great too

#

kotlin has them

#

so why java not yk

storm merlin
#

extension methods is one of the features that, if misused, results in terrible code

#

so it makes sense for an expert-lang like kotlin

#

but not for java

cyan ridge
#

similarly there are interviews with the language designers where they explain why

#

but tl;dr; is that extension methods are a net negative language feature

#

you are allowed to like them, but they also sit on the "make code harder to read, easier to write" side of things

#

(and are a baby version of a more powerful feature called implicits/type classes)

#

(I wrote about this here, but seek out the interviews with the language designers if you want to know why they don't want them)

quiet violet
#

honestly its not even that hard to work around it but they are just nice to have

harsh plover
#

If you really need that many nulls, your data is probably badly mapped

#

Like, say you have an object you get from somewhere, the more modern approach is to have your accessors return Optional. If your way of getting said records does the same, you're basically just flatmapping optionals together, which is imho the proper approach.

cyan ridge
#

Most time you spend developing is just reading and tweaking code

cyan ridge
harsh plover
neat glacier
#

they want an evlis operator system

quiet violet
#

if the first doesnt exist i get an NPE though

neat glacier
#

thats a composite structure, the composite pattern

quiet violet
#

but please

#

tell me there is a better solution

neat glacier
#

that doesnt make any difference here

quiet violet
#

this feels horrible

#

there surely is a better way

cyan ridge
#

there is a better solution

quiet violet
#

thank god

#

this sucks

neat glacier
#

they want to worry about null handling. Optional is basically the only option

cyan ridge
#

and what is the data type here?

storm merlin
#

the solution is to approach it differently and not to try to keep the current structure @neat glacier

#

but for that one needs to understand the situation better

quiet violet
#

you need to understand im working with a mc codebase here so half the stuff i have to use are out of my control in the first place

#

but basically this

#

its a partdefinition with children of the same type

#

i need to get a head partdefinition

#

but it could be capitalized, it could be a child of a body, which could also be a child of root

#

but it depends on the model and i want to capture like 90% of cases

storm merlin
#

sounds like u have a graph and want to do graph exploration like bfs

cyan ridge
quiet violet
cyan ridge
#

Head
head
body -> Head
body -> head
Body -> Head
Body -> head
root -> body -> Head
root -> body -> head
root -> Body -> Head
root -> Body -> head
Root -> body -> Head
Root -> body -> head
Root -> Body -> Head
Root -> Body -> head

#

are these all the options

quiet violet
#

yes

#

they are

#

it feels like binary counting or something

#

its weird

#

or like ternary

#

nah ig there are 2 states

cyan ridge
#

okay so all we have is .getChild which either returns the child or null?

storm merlin
#

(permutation is the term ur looking for)

quiet violet
#

its just a map#get call

cyan ridge
#

what java version?

quiet violet
#

let me check

#

i think 21

#

yes

#

21

cyan ridge
#

ok a few minutes

quiet violet
#

👍

#

always looking to improve so if there is a better way i would love it lmao

#

this isnt the first time encountering this type of problem

cyan ridge
#
record Entry() {}
static class EntryMap {
    Entry getEntry(String s) {
        if (Math.random() < 0.1) {
            return new Entry();
        }
        else {
            return null;
        }
    }
}

void main() {
    var m = new EntryMap();

    var list = Arrays.stream("""
            Head
            head
            body -> Head
            body -> head
            Body -> Head
            Body -> head
            root -> body -> Head
            root -> body -> head
            root -> Body -> Head
            root -> Body -> head
            Root -> body -> Head
            Root -> body -> head
            Root -> Body -> Head
            Root -> Body -> head""".split("\n"))
            .map(row -> row.split("->"))
            .map(row -> Arrays.stream(row)
                    .map(String::strip)
                    .toList())
            .toList();

    IO.println(list);

    for (var path : list) {
        Entry e = null;
        for (var entry : path) {
            e = m.getEntry(entry);
            if (e == null) {
                break;
            }
        }

        if (e != null) {
            IO.println(path);
            IO.println(e);
        }
    }
}
#

@quiet violet just ad-hoc

quiet violet
#

ad hoc?

cyan ridge
#

but i'm sure its less painful than what you have going

cyan ridge
#

but i think you can see the idea

quiet violet
#

ye i can just make it a string array directly

#

wait how does this work

#

oh with an entrymap idk what that does

cyan ridge
#
static class Entry {
    Entry getEntry(String s) {
        if (Math.random() < 0.1) {
            return new Entry();
        }
        else {
            return null;
        }
    }
}
#

there

quiet violet
#

even with a hashmap or something how would that work

#

ah

cyan ridge
#

static class Entry {
    Entry getEntry(String s) {
        if (Math.random() < 0.1) {
            return new Entry();
        }
        else {
            return null;
        }
    }
}

void main() {
    var m = new Entry();

    var list = Arrays.stream("""
            Head
            head
            body -> Head
            body -> head
            Body -> Head
            Body -> head
            root -> body -> Head
            root -> body -> head
            root -> Body -> Head
            root -> Body -> head
            Root -> body -> Head
            Root -> body -> head
            Root -> Body -> Head
            Root -> Body -> head""".split("\n"))
            .map(row -> row.split("->"))
            .map(row -> Arrays.stream(row)
                    .map(String::strip)
                    .toList())
            .toList();

    IO.println(list);

    for (var path : list) {
        Entry e = m;
        for (var entry : path) {
            e = e.getEntry(entry);
            if (e == null) {
                break;
            }
        }

        if (e != null) {
            IO.println(path);
            IO.println(e);
        }
    }
}
#

can you tell i'm tired?

quiet violet
#

ok but if i understand correctly that first part makes a list like

[["Head"],["body", "Head"] ] etc but i dont get how that nests the children

#

like you just loop over it

#

oh well you check one layer deep

#

but not 2 layers

#

or im dumb one of the 2

cyan ridge
#
    Optional.ofNullable(m)
            .map(e -> e.getEntry("root"))
            .map(e -> e.getEntry("body"))
            .map(e -> e.getEntry("head"))
            .orElseGet(null);
#

here is another option that gives ?.

#

like this is a more verbose version of that

cyan ridge
#

e = e.getEntry(entry)

quiet violet
#

ye but isnt that only one layer deep

cyan ridge
#

then you get to the next layer

quiet violet
#

like lets take root body head

you first go into the root body head sequency
then you get an entry so you get root
you still have body and head
and then you exit the loop?

#

or you mean that i just add the other 2 layers

cyan ridge
#

sorry i am getting pulled back to work

quiet violet
#

okay

harsh plover
#

eeeh

#

Little unknown trick

#

new TreeMap<String, ?>(String.CASE_INSENSITIVE_ORDER)

#

There, case insensitive strings in your map

quiet violet
#

ok but. thats minecraft code

#

i cant change the map

harsh plover
#

ugh

quiet violet
#

yep

#

ill keep it in mind for the future though

harsh plover
#

it does have some issues if you start comparing entire maps to eachother though

#

but for lookups etc, it's great 😄

cyan ridge
quiet violet
#

ye i get that

cyan ridge
#

sorry still cant really come back to this yet

#

did you figure out what i was going for?

quiet violet
#

ye

#

i did

#

ill code it in

#

its def better than what i have rn

neat glacier
#

but they could be working on legacy code for all we know

#

you need to understand im working with a mc codebase here so half the stuff i have to use are out of my control in the first place

sounds like they dont have much power in changing the structure

quiet violet
neat glacier
#

but its more used for exposing nullable values, not for working with them

#

people still do null checks & such in modern Java code, just usually dont statically

#

Objects.nonNull

#

so instead of if(member != null), its if(Objects.nonNull(member))

#

the idea is: "if you are given null, use null. if you are exposing values that may be nullable, expose Optional instead"

#

that way instead of receiving a nullable value, they receive an optional value. "this value may not exist" winds up being clear as day

quiet violet
neat glacier
#

thats it

#

nonNull performs basically the same check

#

its just more descriptive

#

"more descriptive" 🙄

#

they do the same thing though

#

a benefit would be allowing the API to handle null checking. thats the main idea

#

if null checking ever changed, nonNull would account for it

#

you wouldnt have to change your null check or anything, it would just work

#

its silly, pretty dumb IMO, but it does have some reasoning

#

null is being rid of anyways. null was a mistake

#

the billion dollar mistake

#

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W).

#

can read more about it, just look up Tony Hoare

#

Sir Charles Antony Richard Hoare (; born 11 January 1934), also known as C. A. R. Hoare, is a British computer scientist who has made foundational contributions to programming languages, algorithms, operating systems, formal verification, and concurrent computing. His work earned him the Turing Award, usually regarded as the highest distinction ...

#

thats the guy who invented null

storm merlin
#
persons.stream()
  .map(person::getFriend)
  .filter(Objects::nonNull)
  .forEach(...);
#

versus

#
persons.stream()
  .map(person::getFriend)
  .filter(p -> p != null)
  .forEach(...);
#

i havent seenn it being used for normal ifs yet. but yeah. it was added together with other similar methods when method references were added with the stream api

harsh plover
#

That has a serious issue

#

If you're quickly glancing over the code, the first one has a distinctive shape, something, something orange

#

You know because of that different color something is happening there immediately, just because null is a keyword and is highlighted differently

harsh plover
neat glacier
storm merlin
#

not nonNull

#

i.e. "i want to use this thing. btw, throw an exception if its null"

#

versus "is this thing null or not?"

#

requireNonNull definitely has uses, its great

#

but nonNull is mostly for when u need a Predicate id say

quiet violet
storm merlin
quiet violet
#

true i guess

storm merlin
#

like, in this case its meaningless:
Objects.requireNonNull(person).getName()

#

but in this case its good:

quiet violet
#

i read its mostly for checking parameters

#

forgot where i read it but

#

somewheer

storm merlin
#
Person person = Objects.requireNonNull(persons.getFirst());
...
nameToPerson.put("john", person);
#

cause in this case u wouldnt necessarily get a NPE otherwise

quiet violet
#

ye true

storm merlin
#

and if, potentially many lines later only

quiet violet
#

ye

storm merlin
#

(a lot of these situations are covered by Optional as return type for methods already as well)

neat glacier
#

the idea was, if you want null handling similar to other languages, the only option at the moment is making use of APIs like this

storm merlin
#

while there are ofc uses like that out in the wild, its not really sth ull encounter commonly

neat glacier
#

depends on the situation, as with most things

#

the core idea is pretty simple: rely on some form of an interface, even if its just a method

#

the difference is "API vs Language Spec"

quaint turtle
neat glacier
#

theres a whole JEP explaining why they opted for default methods instead of extension methods

#

"the API designer should be in charge of scaling the interface, not the user"

thats basically the summary

#

this is probably one of the first JEPs where they bring up extensions

#

Project Lambda also includes virtual extension methods, which will address the long-standing limitation of not being able to add methods to widely-used interfaces because of source compatibility concerns.

#

very similar to how they implemented lambdas: very restricted, because they feel it would hurt productivity if it wasn't restricted

#

ah my bad, it wasnt a JEP that talked about it. it was a Brian Goetz lecture

#

but the idea still remains: Java opted for default methods as an alternative to extension methods