2017-06-12 3 views
0

Ich habe eine Basis BusinessObject abstrakte Klasse, die vergleichbare durch Vergleich ihrer langen ID Felder implementiert. Stellen Sie sich vor, ich verlängere es mit, sagen wir, Person und dann erweitere ich Person mit Worker. So haben wir:Java Vergleichbare Vererbung

Businessobject < Person < Worker

So, jetzt außer Kraft gesetzt ich die compareTo (Businessobject) in Business-Objekt in Person (vergleiche die Namen) und Arbeiter (vergleicht die Jobnamen, dann Person Namen).

jetzt etwas tun, wie ich:

List<BusinessObject> collection = new ArrayList<>(); 
collection.add(new Worker(1L, "Steve", "janitor")); 
collection.add(new Worker(2L, "Mark", "plumber")); 
collection.add(new Person(3L, "Dave")); 

Collections.sort(collection); 
System.out.println(collection); 

Durch die Protokollierung kann ich die Anrufe sehen, die gemacht werden:

  1. Worker.compareTo()
  2. Person.compareTo()

Also bedeutet es Sortiermethoden gemischt, die offensichtlich nicht gut ist. Also, was ist der richtige Weg, mit Vererbung Vergleichbar zu implementieren, so dass das Verfahren auf der gattungsgemäßen Art der Sammlung genannt abhängt:

  1. wenn Kollektion ist eine Liste dann immer BusinessObject.compareTo() verwendet
  2. wenn Sammlung eine Liste dann verwenden Sie immer Person.compareTo()
  3. wenn Sammlung eine Liste ist dann immer Worker.compareTo verwenden()

Hier ist mein Code:

public abstract class BusinessObject implements HasId, HasText, Comparable<BusinessObject> { 
    protected @Nullable Long id; 

    public BusinessObject(@Nullable Long id) { 
     this.id = id; 
    } 

    @Override 
    public @Nullable Long getId() { 
     return id; 
    } 

    public void setId(long id) { 
     this.id = id; 
    } 

    @Override 
    public int compareTo(@NonNull BusinessObject o) { 
     System.out.println("compareTo BusinessObject"); 
     return Long.compare(id, o.id); 
    } 

    @Override 
    public String toString() { 
     return String.format("BusinessObject#%d", id); 
    } 
} 

public class Person extends BusinessObject { 
    protected final String name; 

    public Person(@Nullable Long id, String name) { 
     super(id); 
     this.name = name; 
    } 

    @NonNull 
    @Override 
    public String getText(Context context) { 
     return null; 
    } 

    @Override 
    public String toString() { 
     return String.format("Person#%d (%s)", id, name); 
    } 

    @Override 
    public int compareTo(@NonNull BusinessObject o) { 
     if (o instanceof Person) { 
      System.out.println("compareTo Person"); 
      Person po = (Person) o; 
      return name.compareTo(po.name); 
     } 
     else { 
      return super.compareTo(o); 
     } 
    } 
} 

public class Worker extends Person { 
    protected final String job; 

    public Worker(@Nullable Long id, String name, String job) { 
     super(id, name); 
     this.job = job; 
    } 

    @Override 
    public String toString() { 
     return String.format("Worker#%d (%s-%s)", id, name, job); 
    } 

    @Override 
    public int compareTo(@NonNull BusinessObject o) { 
     if (o instanceof Worker) { 
      System.out.println("compareTo Worker"); 
      Worker wo = (Worker) o; 
      return String.format("%s%s", name, job).compareTo(String.format("%s%s", wo.name, wo.job)); 
     } 
     else { 
      return super.compareTo(o); 
     } 
    } 
} 
+1

Bitte fügen Sie Ihre Person und Code-Worker – solomkinmv

+2

Der offensichtliche Weg ist nicht zwei verschiedene Arten von Objekten in einer einzigen Liste zu mischen. Wenn Sie sie mischen, sind Sie verpflichtet, diese Art von Ergebnissen zu bekommen, wie Sie Äpfel und Apfelsaft vergleichen. –

+0

Bitte klären Sie, wenn Sammlung ist eine Liste dann verwenden Sie immer BusinessObject.compareTo() Wenn Sammlung ist eine Liste dann immer verwenden Sie Person.compareTo() Wenn Sammlung ist eine Liste dann immer verwenden Worker.compareTo() ". – htpvl

Antwort

0

Ich denke, es ist einfach nicht möglich zu tun, was Sie tun möchten. Der generische Typ hat und kann keine Auswirkungen auf die Methoden haben, die von den Elementen verwendet werden.

Aber Sie können möglicherweise Ihr Problem lösen. Verwenden Sie die compareTo-Methoden nicht, sondern implementieren Sie Comparators nach Bedarf.

Es gibt eine Version von Collections.sort(), die einen Komparator als Parameter verwendet.

Offensichtlich möchten Sie die Elemente mit verschiedenen Sortierkriterien abhängig von der Liste, die Sie verwenden, sortieren. Also ich denke, der Comparator wäre die beste Lösung.

3

Ihr Design ist von Natur aus fehlerhaft.

Zuerst haben Sie BusinessObject, die Comparable<BusinessObject> implementiert. Dies bedeutet, dass Geschäftsobjekte eine natürliche Reihenfolge haben, die durch ihre ID bestimmt wird.

Dann überschreiben Sie die Methode in Person, um den Namen zu vergleichen. Was Sie hier sagen, ist: "Nun, wenn beide Geschäftsobjekte, die verglichen werden, Personen sind, dann ist die natürliche Ordnung für sie anders".

Das ergibt keinen Sinn. Entweder ist das Sortieren nach ID für Geschäftsobjekte normal oder nicht.Aber es kann nicht für einige von ihnen natürlich sein, aber nicht für andere.

Formaler ausgedrückt, bricht Ihre Überschreibung die Transitivitätsanforderung von Comparable. Stellen Sie sich vor, Sie haben eine Liste mit drei Geschäftsobjekten: Arbeiter Alice (ID 3), Arbeiter Bob (ID 1) und Firma ACME (ID 2). Die Transitivität sagt, dass wenn x < y && y < z dann x < z auch wahr sein muss. Ihre Vergleichs-Methode gibt jedoch Bob < ACME (durch IDs vergleichen) und ACME < Alice (durch IDs vergleichen), aber das transitive Bob < Alice ist falsch (vergleiche namentlich). Sie haben die Anforderung in der compareTo Methode dokumentiert verletzt:

Der Implementierer muss auch dafür sorgen, dass die Beziehung transitiv ist: (x.compareTo (y)> 0 & & y.compareTo (z)> 0) bedeutet, x .compareTo (z)> 0.

Fazit: Comparable ist nicht für die offene Vererbung gedacht. Tu das nicht. Verwenden Sie stattdessen explizite Comparator s für Ihre Sammlungen, wenn sie sinnvoll sind.

Als Randbemerkung, und dies kann nur in Ihren Beispielen zu verkürzten Code fällig, aber Sie sollten immer equals mit compareTo (iexequals (y konsistent sein außer Kraft setzen) impliziert x.compareTo (y) == 0 und und umgekehrt). Alles andere bedeutet, dass sich Ihre Objekte auf eine nicht intuitive Weise verhalten. (Und dann müssen Sie hashCode außer Kraft zu setzen ihren eigenen Vertrag WRT zu erfüllen equals. Zwingende equals aber nicht hashCode ist eine wunderbare Art und Weise einzuführen wirklich subtile Bugs in Java-Programme.)

+0

Sie haben recht, ich habe es nach ein paar weiteren Tests festgestellt. Ich lasse die Tools Comparable in BusinessObject fallen. Ich sortiere je nach Situation mit einem Ad-hoc-Comparator. Ich danke Ihnen für Ihre Erklärung. –