magicmarcy.de | Equals richtig nutzen

Equals richtig nutzen

18. November 2022 - Lesezeit: 5 Minuten

In nahezu jedem Code ist es unerlässlich Vergleiche anzustellen. Dies ist nicht nur bei der Eingabe von Daten unumgänglich, sondern auch für diverse Bedingungen. Beispielsweise muss man bei einem Login per Email-Adresse ja auch vergleichen können, ob die Eingabe korrekt gewesen ist. Ein anderes Beispiel sind ReCaptchas oder Token-Vergleiche. Man wird nicht drum herum kommen, Vergleiche in seinem Code zu implementieren.

In Java gibt es nützliche Bilbliotheken wie zum Beispiel StringUtils. Damit lassen sich unter vielem anderem Vergleiche ziemlich einfach gegen null prüfen. Doch manchmal möchte man auch einfach keine anderen Bibliotheken nutzen oder muss aus anderen Gründen auf die ganz normale Syntax zurückgreifen. Die String-Klasse bietet uns von Haus aus ja bereits Methoden, die wir für diesen Zweck nutzen können.

Eigentlich – so will man meinen – kann man da ja gar nicht so viel verkehrt machen – es werden ja schließlich nur zwei Strings miteinander verglichen.

Doch weit gefehlt! Immer wieder sehe ich String-Vergleiche, die im schlechtesten Fall nämlich einfach zu einer Exception führen – gerade wenn es um Eingaben aus einer Oberfläche geht.

Ein einfaches Beispiel:

Nehmen wir an, auf unserer Webseite gibt es ein Feld für einen Benutzernamen und wir möchten prüfen, ob der Benutzername bereits in unserer Liste von Benutzernamen enthalten ist.

Wir haben also unsere Liste:

final List<String> usernames = Arrays.asList("Peter", "Paul", "Michael");

Und die Eingabe des Benutzers aus der Oberfläche:

final String userInput;

Also schreiben wir doch einfach eine Methode, die durch unsere Liste iteriert und uns ein true zurückliefert, wenn der Benutzername gefunden wurde:

private boolean usernameInList(final String userInput) {
  for (final String username : this.usernames) {
    if (userInput.equals(username)) {
      return true;
    }
  }
}

Das sieht doch gar nicht schlecht aus und auf den ersten Blick macht die Methode auch genau das was sie soll. Wir iterieren über unsere Liste und vergleichen unsere Benutzereingabe mit jedem Eintrag aus der Liste.

Alles safe, oder doch nicht?

Nun, eigentlich schon – bei unserer Liste können wir relativ sicher sein (wenn wir sie selbst erstellt haben), dass nur korrekte Angaben enthalten sind – also kein Eintrag leer oder null ist. Allerdings können wir das bei einer Benutzereingabe nicht sicher sagen. Was ist denn, wenn der User auf der Oberfläche einfach nichts eingibt?

Ein String, wenn er nicht initialisiert ist, ist immer null. Gibt der User auf der Oberfläche nichts ein (und wir machen auch keine weitere Prüfung) ändert sich an diesem Wert auch nichts userInput ist weiterhin null.

Versuchen wir nun, auf null die equals-Methode aufzurufen erhalten wir eine Exception:

java.lang.NullPointerException#
    at StringTest.simple_equals_test(StringTest.java:21)
  ...

Wir können also einfach – wenn wir nichts eingeben – unsere Anwendung zum Absturz bringen!

Die Lösung dafür ist so einfach wie naheliegend: Wir müssen einfach nur den Wert, von dem wir sicher sagen können, dass er niemals null sein kann an den Anfang stellen. Unsere neue Methode würde dann also wie folgt aussehen:

private boolean usernameInList(final String userInput) {
  for (final String username : this.usernames) {
    if (username.equals(userInput)) {
      return true;
    }
  }
}

Wenn nun userInput null ist, dann ist es einfach nur nicht gleich dem, womit wir es vergleichen wollen. Der entscheidende Unterschied ist aber, dass wir die equals-Methode an einem String aufrufen der immer einen Wert enthält und niemals null ist.

Auch das Initialisieren von userInput mit einem Leerstring, also "", würde uns in diesem Fall nicht weiterhelfen, da wir (ohne weitere Prüfung) diesen Leerstring sogar mit null aus der Eingabe überschreiben würden.

Um es ganz sicher zu machen, müsste man aber tatsächlich vorher einen String immer prüfen sodass wir sicherstellen können, dass er nicht null oder leer ist. Das gilt ganz besonders für Eingaben, die auf der Oberfläche kommen. Die Prüfung könnte also wie folgt aussehen:

private boolean isValid(final String input) {
  return input != null && !''.equals(input);
}

Ja, das ist tatsächlich nicht schön, würde unser Problem aber durchaus lösen wenn wir in unserer Schleife nun prüfen ob es eine valide Eingabe ist:

private boolean usernameInList(final String userInput) {
  if (isValid(userInput) {
    for (final String username : this.usernames) {
      if (isValid(username) {
        if (userInput.equals(username)) {
          return true;
        }
      }
    }
  }
}

Die Schleife führen wir also nur aus, wenn die User-Eingabe valide ist und der Vergleich dann im Anschluss auch nur, wenn der entsprechende Username aus der Liste ebenfalls valide ist.

Natürlich ließe sich das auch noch ein wenig reduzieren und zusammenfassen aber irgendwie ist das auch alles andere als übersichtlich. Daher würde uns hier die StringUtils-Bibliothek wirklich sehr helfen. Diese Bibliothek bringt uns nämlich für genau solche Fälle die entsprechenden Methoden mit. Es gibt eine null-sichere Variante der equals-Methode sowie weitere Prüfungen auf null oder leer mit. Die StringUtils stecken in Apache Commons Lang und können über die folgende Dependency (oder als lib) hinzugefügt werden:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

Damit könnte unsere Vergleichsmethode dann so aussehen:

private boolean usernameInList(final String userInput) {
  for (final String username : this.usernames) {
    if (StringUtils.equals(userInput, username)) {
      return true;
    }
  }
}

Schaut man sich die Implementierung an dann stellt man fest, dass hier alle möglichen Prüfungen für uns ausgeführt werden. Selbst wenn auf beiden Seite null stehen würde, funktioniert unsere Prüfung noch immer – in diesem Fall würde wir lediglich ein unerwartetes Ergebnis – nämlich true – erhalten.

Die StringUtils stellen noch eine Vielzahl weiterer Methoden zur Verfügung die ich in meinem Alltag kaum mehr missen möchte. Um ein paar wichtige und nützliche zu nennen:

  • StringUtils.equals(value1, value2)
  • StringUtils.compare(value1, value2)
  • StringUtils.contains(value1, searchString)
  • StringUtils.isBlank(value)
  • StringUtils.isNotBlank(value)
  • StringUtils.isNoneBlank(value1, value2, value3, usw)

Das sind nur einige der Methoden die uns mit dieser Bibliothek zur Verfügung stellt. Man muss sich nur mal vorstellen, dass man für all diese Anwendungsfälle je eine Methode selbst schreiben müsste um die Eingaben zu validieren.

Es wurden noch keine Kommentare verfasst, sei der erste!

Support

Wenn du mich und meine Arbeit unterstützen möchtest, freue ich mich sehr über eine kleine Aufmerksamkeit in Form eines Kaffees ;-)



Weitere Informationen, wie du mich und meine Arbeit unterstützen kannst, findest du hier.