Alternative Darstellung eines Codesnippets

Diskutiere Alternative Darstellung eines Codesnippets im Allgemeine Java-Themen Bereich.
Zrebna

Zrebna

Hi!

Ich frage mich wie man dieses Code-snippet innerhalb einer überschriebenen
equals()-Methode auch mit einer if-Kaskade äquivalent darstellen kann?

Code mit verkürzter Syntax:

Java:
 @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Invocation)) return false;

        Invocation that = (Invocation) o;

        if (getSelf() != null ? !getSelf().equals(that.getSelf()) : that.getSelf() != null) return false;
        if (getMethod() != null ? !getMethod().equals(that.getMethod()) : that.getMethod() != null) return false;
        
        return Arrays.deepEquals(getArgs(), that.getArgs());
    }
Mein Vorschlag für eine if-Kaskaden-Schreibweise:
Java:
@Override
    public boolean equals(Object o) {
       if(this == o)
           return true;
       if(!(o instanceof Invocation))
           return false;

       Invocation other = (Invocation) o;

       if(getSelf() == null && other.getSelf() == null) {
           return true;
       }
       else if (getSelf() != null && getSelf().equals(other.getSelf())) {
           return true;
       }
       else if(!getSelf().equals(other.getSelf())) {
           return false;
       }
       if(getMethod() == null && other.getMethod() == null) {
           return true;
       }
       else if(getMethod() != null) {
           return getMethod().equals(other.getMethod());
       }
        // use Arrays.deepEquals to compare Object[]
       return Arrays.deepEquals(getArgs(), other.getArgs());

    }
Kommt das so hin bzw. mach der 2te Code genau das selbe, wie der erste Code?

Lg,
Zrebna
 
J

JustNobody

Also generell ist der übliche Weg, das einfach mit Unit Tests abzudecken. Dann kannst Du Code umschreiben und hast immer die Kontrolle, dass du es nicht kaputt gemacht hast.

Auf den ersten Blick sieht es erst einmal ok aus, aber beim drüber lesen übersieht man schnell mal etwas.

Und als Anregung: wenn Arrays.deepEquals benutzt werden darf, dann doch bestimmt auch Objects.equals:

Das erspart dann diese Vergleichsarie mit Null Prüfung..
 
J

JustNobody

Ach so: nein, der Code scheint falsch zu sein. Ein return true kannst du nicht bringen, ehe du alles getestet hast.

Das Pattern ist immer:
Wenn ein Element nicht gleich ist, dann return false.
Nur beim letzten Element kommt dann auch die Option, true zurück zu geben!

Somit gehst du bei dem Vergleich eines Elementes bei Gleichheit zum nächsten Element weiter.
 
Zrebna

Zrebna

Hi!

Also eher so?
Java:
@Override
    public boolean equals(Object o) {
       if(this == o)
           return true;
       if(o == null)
           return false;
       if(!(o instanceof Invocation))
           return false;

       Invocation other = (Invocation) o;

       if(!getSelf().equals(other.getSelf())) {
           return false;
       }
       if(!getMethod().equals(other.getMethod()))
           return false;
      
        // use Arrays.deepEquals to compare Object[]
       return Arrays.deepEquals(getArgs(), other.getArgs());
    }
Wieso ist der Kollege (verkürzter Code im Eingsngspost) von dem Schema "zu Erst alles auf mögliche False und am Ende true returnen"
abgewichen?

offtopic:
Kennt Jemand gute Tutorials bgzl. Domain Specific Languages in Java? Schwierig da etwas hilfreiches zu finden...
 
J

JustNobody

Da fehlt jetzt der Null check.

Das pattern ist:
Ist das Element null?
Ja: ==> prüfe das Element in anderer Instanz auf Null. Gib falls zurück, falls es nicht null ist.
Nein: ==> prüfe ob Elemente ungleich sind und gib false zurück falls die Element nicht gleich sind.

Da du dies ja mehrfach brauchst, kannst du es in eine Methode auslagern. Dann hättest du eine Methode wie Objects.equals ... da kannst du dir ja auch einmal den Source Code von ansehen:

Dann siehst du die kompakte Version des oben beschriebenen. Um die zu bekommen kannst du überlegen, in welchen Fällen true zurück gegeben werden soll ...
 
J

JustNobody

Wenn wir bei alternativen Lösungen sind, dann werfe ich noch Lombok ins Rennen....

Warum Code schreiben, der einfach nur Tipparbeit ist? Zu einem Feld Getter/Setter schreiben, Konstruktoren, die einfach nur für Felder Werte entgegen nimmt und diese dann in die Felder einträgt... und natürlich equals und hashcode nicht zu vergessen.... Oder toString um was sinnvolles im log zu sehen ....

Daher mein Favorit: www.projectlombok.org
 
Zrebna

Zrebna

Danke!
Ich gucke mir den SourceCode an.

Bzgl. diesem Code hier - passt es nun? (Habe den Anfang geändert - zuerst Check auf Null, dann auf Gleichheit (falls beide nicht Null sind).

Java:
  @Override
    public boolean equals(Object o) {
       if(this == o)
           return true;
       if(this == null) {
           if(o != null)
               return false;
       }
       if(!(o instanceof Invocation))
           return false;

       Invocation other = (Invocation) o;

       if(!getSelf().equals(other.getSelf())) {
           return false;
       }
       if(!getMethod().equals(other.getMethod()))
           return false;

        // use Arrays.deepEquals to compare Object[]
       return Arrays.deepEquals(getArgs(), other.getArgs());
    }
Ansonsten verstehe ich bzgl. den letzten paar Posts eher nur Bahnhof - ich bin ein Java-Anfänger und habe die Sprache
erst im Rahmen meines Studiums kennen gelernt - daher sind Inhalte der letzten Postings für mich noch viel zu advanced und
wohl auch im Rahmen meines Studiums, da dies noch nicht relevant ist.

Ansonsten noch eine Frage bzgl., wie man meine equals-Methode im Rahmen von JUnit testen könnte:
Quasi unter 'test' eine Klasse erstellen und dort zwei Objekte der zu testenden Klasse initialisieren und dann eben testen?
Ich hab schon JUnit-testing mittels einfachen Beispielen kennnengelernt (ka, z.B. einfache Methode zur Summenberechnung), aber noch nicht
im Rahmen nicht primitiver Datentypen, wie hier.

So wäre mein Anfang:

Java:
import de.oth.mocker.Invocation;
import org.junit.jupiter.api.Test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


public class MockerTest {

    Class<?> clazz = Class.forName("de.oth.mocker.Invocation");
    Method methodStr = clazz.getDeclaredMethod("toString", String.class);
    Object[] object = (Object[]) clazz.getDeclaredConstructor(String.class, Method.class, Object[].class).newInstance("Hello", methodStr, "World");

    public MockerTest() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    }

    Invocation invocationObject = new Invocation("Hello", methodStr, object);
    @Test
    public void equalsTest() {
     // need antoher object to test against
    }
}
Das fühlt sich bis dato richtungsweise nicht richtig an...


Offtopic:
Gibt es eigentlich hier im Forum auch irrgendwo Nachhilfe-Anzeigen/Gesuche?
Ist das ein Ding hier, oder wird mir am Ende nichts anderes übrig bleiben, als mal Fiverr anzusteuern? xD

Trotzdem danke für den Input, aber wie gesagt:
Die letzten paar Pots sind zu weit weg von meinem jetzigem Stand un dem meiner Kommilitonen...

Lg,
Zrebna
 
Zuletzt bearbeitet:
L

LimDul

Bezüglch des Test-Codes - was zur Hölle machst du da? :)

Java:
@Test
public void testEquals_BeideNonNull() {
  MyClass object1 = new MyClass("Wert", 1);
 MyClass object2 = new MyClass("Wert", 1);

assertEquals(object1, object2);
}
Reflection brauchst du nicht, einfach plain Java Code schreiben.
 
J

JustNobody

Also ein paar Punkte, die mir auffallen:

a) Check auf null:
Code:
       if(this == null) {
           if(o != null)
               return false;
       }
Das ist so verdreht. Bei einem Aufruf someObject.equals(someOtherObject) kommt eine NPE, wenn someObject null ist. Eine Prüfung von this==null ist somit nicht notwendig. this kann nicht null sein!
Und du willst ja auch eher prüfen: Ist das andere Objekt null. Also wäre da lediglich der Code: if (o == null) return false; notwendig.

b) Du bekommst NPEs!
Code:
if(!getSelf().equals(other.getSelf())) {
           return false;
       }
Wenn getSelf() null zurück gibt, dann hast Du hier eine NPE. Die Prüfung ist also

Code:
if (getSelf != null) {
       // getSelf ist nicht null, daher Prüfung, ob es nicht gleich ist.
       if(!getSelf().equals(other.getSelf())) {
           return false;
       }
} else {
       // getSelf ist null, daher ist es nicht gleich, wenn other.getSelf nicht null ist.
       if (other.getSelf() != null) return false;
}
c) Zum Unit Test hat @LimDul schon einen wichtigen Punkt geschrieben. Was da noch wichtig ist, ist, dass Du Dir alle Kominationen aussuchst und testest. Also einfach den Code durchgehen und sicher stellen, dass Du in alle Elemente rein läufst.
- test mit sich selbst (if(this == o))
- test mit null (if (o == null))
- test mit etwas, das keine Instanz von Invocation ist
- test bei getSelf: in beidem null, dann jeweils in einem wird null zurück gegeben, dann beide nicht null aber einmal gleich und einmal nicht gleich ...
- test mit nächstem Element aus Invocation ...

So stellst Du sicher, dass alle Pfade durchlaufen werden ....
 
Zrebna

Zrebna

Bezüglch des Test-Codes - was zur Hölle machst du da? :)

Java:
@Test
public void testEquals_BeideNonNull() {
  MyClass object1 = new MyClass("Wert", 1);
MyClass object2 = new MyClass("Wert", 1);

assertEquals(object1, object2);
}
Reflection brauchst du nicht, einfach plain Java Code schreiben.

Ich weiß, wie bei "normalen" Klassen Tests funktionieren - also z.B. bei Klassenobjekten, wie in deinem Beispiel mit
gängigen Attributen, wie String und Integer, z.B.

In meinem Fall sind ja dies die folgenden 3 Attirbute:
private final String self;
private final Method method;
private final Object[] args;

Außer bei dei dem erstem Feld, weiß ich nicht, wie man das testen soll.

PS: Was ist das bei meinem Test-Code mache - diese Frage ist berechtig, denn das weiß ich selber nicht so recht D:
 
Zrebna

Zrebna

Also ein paar Punkte, die mir auffallen:

a) Check auf null:
Code:
       if(this == null) {
           if(o != null)
               return false;
       }
Das ist so verdreht. Bei einem Aufruf someObject.equals(someOtherObject) kommt eine NPE, wenn someObject null ist. Eine Prüfung von this==null ist somit nicht notwendig. this kann nicht null sein!
Und du willst ja auch eher prüfen: Ist das andere Objekt null. Also wäre da lediglich der Code: if (o == null) return false; notwendig.

b) Du bekommst NPEs!
Code:
if(!getSelf().equals(other.getSelf())) {
           return false;
       }
Wenn getSelf() null zurück gibt, dann hast Du hier eine NPE. Die Prüfung ist also

Code:
if (getSelf != null) {
       // getSelf ist nicht null, daher Prüfung, ob es nicht gleich ist.
       if(!getSelf().equals(other.getSelf())) {
           return false;
       }
} else {
       // getSelf ist null, daher ist es nicht gleich, wenn other.getSelf nicht null ist.
       if (other.getSelf() != null) return false;
}
c) Zum Unit Test hat @LimDul schon einen wichtigen Punkt geschrieben. Was da noch wichtig ist, ist, dass Du Dir alle Kominationen aussuchst und testest. Also einfach den Code durchgehen und sicher stellen, dass Du in alle Elemente rein läufst.
- test mit sich selbst (if(this == o))
- test mit null (if (o == null))
- test mit etwas, das keine Instanz von Invocation ist
- test bei getSelf: in beidem null, dann jeweils in einem wird null zurück gegeben, dann beide nicht null aber einmal gleich und einmal nicht gleich ...
- test mit nächstem Element aus Invocation ...

So stellst Du sicher, dass alle Pfade durchlaufen werden ....

Top vielen Dank - hat sehr geholfen!:)

Ich gucke mir das alles noch einmal in Ruhe an^^
 
Zrebna

Zrebna

Update:
Jetzt scheint es erstmal zu passen:
Java:
  @Override
    public boolean equals(Object o) {
       if(o == null)
           return false;

       if(!(o instanceof Invocation))
           return false;

       Invocation other = (Invocation) o;

        if(this.getSelf() != null) {
            if(!this.getSelf().equals(other.getSelf()))
                return false;
        } else {
            if(other.getSelf() != null) {
                return false;
            }
        }

        if(this.getMethod() != null) {
            if(!this.getMethod().equals(other.getMethod()))
                return false;
        } else {
            if(other.getMethod() != null)
                return false;
        }
        // use Arrays.deepEquals to compare Object[]
       return Arrays.deepEquals(getArgs(), other.getArgs());
    }
Es kommt auch beim Testen das raus, was raus kommen soll (habe ein paar vorgeschlagene Testcases getestet)
Java:
    @Test
    public void testEquals_BeideNonNull() {
        Invocation object1 = new Invocation("Hello", null, null);
        Invocation object2 = new Invocation("Hello", null, null);
       // Invocation object3 = null;
        boolean result = object1.equals(object2);
        Assert.assertEquals("Objects are not equal", true, result);
      // Assert.assertEquals(object1, object2);

    }
Die Frage von vorher (2 Posts zuvor) bleibt aber irgendwie bestehen:
Offensichtlich habe ich mich um mein Problem nicht zu wissen, wie man hier Methoden- und Object[]-Typen beim Testing initialisiert gedürckt,
indem ich mir etliche Testcase gespart habe, bei denen diese beiden Attribute eben nicht auf null gesetzt sind.
Wie würde es aussehen, wenn ich bei diesen Attributen eben non-null Werte initialisieren will und dafür testen möchte?
 
mrBrown

mrBrown

Wie würde es aussehen, wenn ich bei diesen Attributen eben non-null Werte initialisieren will und dafür testen möchte?
Das Method-Objekt musst du dir in dem Fall natürlich schon mit Reflection holen, ähnlich wie du das weiter oben gemacht hast.
Du brauchst aber wirklich nur getMethod/getDeclaredMethod und nimmst dafür am besten bekannte Klassenobjekte (also ohne Class.forName).
Das Objekt-Array soll (soweit ich verstehe) die Argumente für diese Methode darstellen, du musst dir also nur irgendwelche Beispiel-Argumente für die Methode suchen und damit das Array initialisieren.
 
Zrebna

Zrebna

Ach so, ok nun wird es klarer - danke!
Bevor ich es versuche - was wäre denn ein bekanntes Klassenobjekt - sowas wie ArrayList?
Also meinst du etwas "universelles" oder soll ich einfach im Nicht-test-package eine Testklasse erstellen mit einer Methode incl. Beispiel-Parameter und dann auf diese von meiner Testklasse aus zugreifen.

So hast du es nicht gmeint, nehm ich an:

Testklasse:

Java:
public class Greeting {

    private final String str;

    public Greeting(String str) {
        this.str = str;
    }
    
    // method
    public String sayGreeting(String str) {
        return str;
    }
}
Test:
Java:
  @Test
    public void testEquals_BeideNonNull() throws ClassNotFoundException, NoSuchMethodException {
        Class<?> clazz = Class.forName("de.oth.mocker.Greeting");

     //   Invocation object1 = new Invocation("Hello", null, null);
      //  Invocation object2 = new Invocation("Hello", null, null);
       // Invocation object3 = null;
        Invocation object1 = new Invocation("Hello", clazz.getDeclaredMethod("sayGreeting", String.class), clazz.getDeclaredMethod("sayGreeting", String.class).getTypeParameters());
        Invocation object2 = new Invocation("Hello", clazz.getDeclaredMethod("sayGreeting", String.class), clazz.getDeclaredMethod("sayGreeting", String.class).getTypeParameters());

        boolean result = object1.equals(object2);
        Assert.assertEquals("Objects are not equal", true, result);
      // Assert.assertEquals(object1, object2);

    }
 
Zuletzt bearbeitet:
mrBrown

mrBrown

Bevor ich es versuche - was wäre denn ein bekanntes Klassenobjekt - sowas wie ArrayList?
Also meinst du etwas "universelles" oder soll ich einfach im Nicht-test-package eine Testklasse erstellen mit einer Methode incl. Beispiel-Parameter und dann auf diese von meiner Testklasse aus zugreifen.
Irgendetwas, was du direkt mit Klassenname.class auflösen kannst :)

Ich würde eher eine eigene Klasse erstellen (die wirst du vermutlich für weitere Tests auch noch brauchen), die gehört dann aber in die Test-Packages, nicht zum normalen Code :)
 
Zrebna

Zrebna

Irgendetwas, was du direkt mit Klassenname.class auflösen kannst :)

Ich würde eher eine eigene Klasse erstellen (die wirst du vermutlich für weitere Tests auch noch brauchen), die gehört dann aber in die Test-Packages, nicht zum normalen Code :)

Ah, du warst schneller (hab obigen Post editiert) - meinst du doch, wie im Post drüber?
 
Thema: 

Alternative Darstellung eines Codesnippets

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben