Java: Überprüfung auf Null in verschachtelten Klassen vermeiden (Deep Null-Prüfung)

Lesezeit: 7 Minuten

Benutzer-Avatar
lappall

Stellen Sie sich vor, ich habe eine Klassenfamilie. Es enthält eine Personenliste. Jede (Klassen-)Person enthält eine (Klassen-)Adresse. Jede (Klassen-)Adresse enthält eine (Klassen-)Postleitzahl. Jede “Zwischenklasse” kann null sein.

Gibt es also eine einfache Möglichkeit, zu PostalCode zu gelangen, ohne bei jedem Schritt auf null prüfen zu müssen? dh gibt es eine Möglichkeit, den folgenden Daisy-Chaining-Code zu vermeiden? Ich weiß, dass es keine “native” Java-Lösung gibt, aber ich hatte gehofft, ob jemand eine Bibliothek oder so etwas kennt. (geprüft Commons & Guava und nichts gesehen)

if(family != null) {
    if(family.getPeople() != null) {
        if(family.people.get(0) != null) {
            if(people.get(0).getAddress() != null) {
                if(people.get(0).getAddress().getPostalCode() != null) {
                    //FINALLY MADE IT TO DO SOMETHING!!!
                }
            }
        }
    }
}

Nein, die Struktur kann nicht geändert werden. Es stammt von einem Dienst, über den ich keine Kontrolle habe.

Nein, ich kann Groovy und seinen praktischen “Elvis”-Operator nicht verwenden.

Nein, ich möchte lieber nicht auf Java 8 warten 😀

Ich kann nicht glauben, dass ich der erste Entwickler bin, der es satt hat, solchen Code zu schreiben, aber ich konnte keine Lösung finden.

  • Tut mir leid, du steckst fest. Einige Leute verwenden den trinären Bedingungsoperator, um ihn etwas weniger dicht zu machen, aber es ist immer noch derselbe Bytecode, nur schwerer zu lesen.

    – Paul Tomblin

    30. April 2012 um 22:32 Uhr

  • Ich kann nicht glauben, dass ich der erste Entwickler bin, der es satt hat, solchen Code zu schreiben„Nun, bist du nicht.

    – Benutzer1329572

    30. April 2012 um 22:34 Uhr

  • Trotz all der Antworten, die Ihnen sagen, dass Sie Nullprüfungen ignorieren und einfach versuchen sollen, a zu fangen NullPointerException, tu es nicht! Ihr Code mag ein Dorn im Auge sein, aber das Auslösen einer Ausnahme ist eine teure Operation, die Sie immer vermeiden möchten, wenn Sie können.

    – Jeffrey

    30. April 2012 um 22:47 Uhr

  • Berücksichtigen Sie auch all die guten Dinge, die Sie in “else”-Klauseln tun können, wenn Sie sie einfügen – Fehlermeldungen, alternative Codepfade usw. Angesichts all dessen wird es nicht so schlecht aussehen.

    – Mazaneicha

    30. April 2012 um 23:03 Uhr


  • Wenn Sie Brototype nur für Java portieren könnten … github.com/letsgetrandy/brototype

    – jonS90

    8. Oktober 2014 um 18:25 Uhr


Sie können verwenden für:

product.getLatestVersion().getProductData().getTradeItem().getInformationProviderOfTradeItem().getGln();

optionales Äquivalent:

Optional.ofNullable(product).map(
            Product::getLatestVersion
        ).map(
            ProductVersion::getProductData
        ).map(
            ProductData::getTradeItem
        ).map(
            TradeItemType::getInformationProviderOfTradeItem
        ).map(
            PartyInRoleType::getGln
        ).orElse(null);

  • Es erfordert API 24 und höher 🙁

    – Sandkasten

    10. April 2019 um 11:25 Uhr

  • Wenn Sie eine .map-Implementierung sehen, verwendet sie Objects.requireNonNull(mapper); und diese Methode löst nullpointerexception aus, wenn der Mapper null wird.

    – Anirudh

    27. Februar 2020 um 10:18 Uhr

  • @Anirudh der Mapper ist in diesem Fall die Methodenreferenz und ist niemals null. Der vom Mapper zurückgegebene Wert kann null sein, und in diesem Fall gibt die Map Optional.empty() zurück.

    – Andrea Polci

    22. Oktober 2020 um 11:45 Uhr

  • Dies sollte die akzeptierte Antwort sein.

    – Taher

    8. November 2021 um 10:25 Uhr

Ihr Code verhält sich genauso wie

if(family != null &&
  family.getPeople() != null &&
  family.people.get(0) != null && 
  family.people.get(0).getAddress() != null &&
  family.people.get(0).getAddress().getPostalCode() != null) { 
       //My Code
}

Dank an Kurzschlussauswertungdies ist auch sicher, da die zweite Bedingung nicht ausgewertet wird, wenn die erste falsch ist, die dritte nicht ausgewertet wird, wenn die zweite falsch ist, …. und Sie erhalten kein NPE, weil dies der Fall ist.

Falls Sie Java8 verwenden, können Sie Folgendes verwenden:

resolve(() -> people.get(0).getAddress().getPostalCode());
    .ifPresent(System.out::println);

:
public static <T> Optional<T> resolve(Supplier<T> resolver) {
    try {
        T result = resolver.get();
        return Optional.ofNullable(result);
    }
    catch (NullPointerException e) {
        return Optional.empty();
    }
}

REF: Vermeiden Sie Nullprüfungen

  • Diese Lösung beruht auf dem Fangen der NPE, die sehr schlecht abschneiden wird.

    – mojoken

    22. September 2017 um 1:33 Uhr

  • Ich bin dafür. Kann jemand erklären, warum es schlecht ist? Liegt es daran, dass die Getter aufgrund eines anderen Fehlers intern durch eine NPE gehen und maskiert werden?

    – daltonfury42

    16. Juni 2021 um 5:20 Uhr


Benutzer-Avatar
Paul Tomblin

Das Beste, was Sie erreichen können, ist, die Abkürzungsregeln in Bedingungen zu nutzen:

if(family != null && family.getPeople() != null && family.people.get(0) != null  && family.people.get(0).getAddress() != null && family.people.get(0).getAddress().getPostalCode() != null) {
                    //FINALLY MADE IT TO DO SOMETHING!!!

}

Übrigens ist es eine schreckliche Idee, eine Ausnahme abzufangen, anstatt die Bedingung im Voraus zu testen.

Ich persönlich bevorzuge etwas Ähnliches wie:

nullSafeLogic(() -> family.people.get(0).getAddress().getPostalCode(), x -> doSomethingWithX(x))

public static <T, U> void nullSafeLogic(Supplier<T> supplier, Function<T,U> function) {
    try {
        function.apply(supplier.get());
    } catch (NullPointerException n) {
        return null;
    }
}

oder so ähnlich

nullSafeGetter(() -> family.people.get(0).getAddress().getPostalCode())

public static <T> T nullSafeGetter(Supplier<T> supplier) {
    try {
        return supplier.get();
    } catch (NullPointerException n) {
        return null;
    }
}

Das Beste daran ist, dass die statischen Methoden mit jeder Funktion wiederverwendbar sind 🙂

Anstatt null zu verwenden, könnten Sie eine Version des Entwurfsmusters „Nullobjekt“ verwenden. Zum Beispiel:

public class Family {
    private final PersonList people;
    public Family(PersonList people) {
        this.people = people;
    }

    public PersonList getPeople() {
        if (people == null) {
            return PersonList.NULL;
        }
        return people;
    }

    public boolean isNull() {
        return false;
    }

    public static Family NULL = new Family(PersonList.NULL) {
        @Override
        public boolean isNull() {
            return true;
        }
    };
}


import java.util.ArrayList;

public class PersonList extends ArrayList<Person> {
    @Override
    public Person get(int index) {
        Person person = null;
        try {
            person = super.get(index);
        } catch (ArrayIndexOutOfBoundsException e) {
            return Person.NULL;
        }
        if (person == null) {
            return Person.NULL;
        } else {
            return person;
        }
    }
    //... more List methods go here ...

    public boolean isNull() {
        return false;
    }

    public static PersonList NULL = new PersonList() {
        @Override
        public boolean isNull() {
            return true;
        }
    };
}

public class Person {
    private Address address;

    public Person(Address address) {
        this.address = address;
    }

    public Address getAddress() {
        if (address == null) {
            return Address.NULL;
        }
        return address;
    }
    public boolean isNull() {
        return false;
    }

    public static Person NULL = new Person(Address.NULL) {
        @Override
        public boolean isNull() {
            return true;
        }
    };
}

etc etc etc

Dann kann Ihre if-Anweisung werden:

if (!family.getPeople().get(0).getAddress().getPostalCode.isNull()) {...}

Es ist suboptimal, weil:

  • Sie stecken fest, NULL-Objekte für jede Klasse zu erstellen,
  • Es ist schwierig, diese Objekte generisch zu machen, also müssen Sie eine Null-Objekt-Version jeder Liste, Karte usw. erstellen, die Sie verwenden möchten, und
  • Es gibt möglicherweise einige lustige Probleme mit der Unterklassenbildung und der Verwendung von NULL.

Aber wenn du deine wirklich hasst == nulls, das ist ein Ausweg.

Benutzer-Avatar
Gemeinschaft

Obwohl dieser Beitrag fast fünf Jahre alt ist, habe ich vielleicht eine andere Lösung für die uralte Frage, wie man damit umgeht NullPointerExceptions.

Kurzgesagt:

end: {
   List<People> people = family.getPeople();            if(people == null || people.isEmpty()) break end;
   People person = people.get(0);                       if(person == null) break end;
   Address address = person.getAddress();               if(address == null) break end;
   PostalCode postalCode = address.getPostalCode();     if(postalCode == null) break end;

   System.out.println("Do stuff");
}

Da noch viel Legacy-Code verwendet wird, verwenden Sie Java 8 und Optional ist nicht immer eine Option.

Wann immer es um tief verschachtelte Klassen geht (JAXB, SOAP, JSON, was auch immer …) und Gesetz der Demeter nicht angewendet wird, müssen Sie im Grunde alles überprüfen und sehen, ob möglicherweise NPEs lauern.

Meine vorgeschlagene Lösung strebt nach Lesbarkeit und sollte nicht verwendet werden, wenn nicht mindestens 3 oder mehr verschachtelte Klassen beteiligt sind (wenn ich verschachtelt sage, meine ich nicht Verschachtelte Klassen im formellen Kontext). Da Code mehr gelesen als geschrieben wird, macht ein kurzer Blick auf den linken Teil des Codes seine Bedeutung klarer als die Verwendung tief verschachtelter if-else-Anweisungen.

Wenn Sie den Else-Teil benötigen, können Sie dieses Muster verwenden:

boolean prematureEnd = true;

end: {
   List<People> people = family.getPeople();            if(people == null || people.isEmpty()) break end;
   People person = people.get(0);                       if(person == null) break end;
   Address address = person.getAddress();               if(address == null) break end;
   PostalCode postalCode = address.getPostalCode();     if(postalCode == null) break end;

   System.out.println("Do stuff");
   prematureEnd = false;
}

if(prematureEnd) {
    System.out.println("The else part");
}

Bestimmte IDEs brechen diese Formatierung, es sei denn, Sie weisen sie an, dies nicht zu tun (siehe diese Frage).

Ihre Bedingungen müssen invertiert werden – Sie teilen dem Code mit, wann er unterbrochen werden soll, nicht wann er fortgesetzt werden soll.

Eine weitere Sache – Ihr Code ist immer noch anfällig für Brüche. Sie müssen verwenden if(family.getPeople() != null && !family.getPeople().isEmpty()) als erste Zeile in Ihrem Code, andernfalls löst eine leere Liste eine NPE aus.

1381570cookie-checkJava: Überprüfung auf Null in verschachtelten Klassen vermeiden (Deep Null-Prüfung)

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy