Frage Statische Methode in Schnittstelle mit generischer Signatur


Ab Java 8 können Sie Standard-oder statische Methoden in Interfaces wie unten implementiert haben

public interface DbValuesEnumIface<ID, T extends Enum<T>> {
   T fromId(ID id);

   ID getId();
   static String getDescriptionKey(){
      return "this is a test";
   }
}

Ich möchte das oben genannte mit der statischen Methode erklären, die eine Unterschrift hat, die Begrenzungen verwendet, die durch die implementierenden Klassen definiert werden, da die Implementierung der Methode für alle gleich sein sollte, mit dem einzigen Unterschied sollten die generics erklärt werden, als solche:

public interface DbValuesEnumIface<ID, T extends Enum<T>> {

   public static T fromId(ID id) {
        if (id == null) {
            return null;
        }
        for (T en : T.values()) {
            if (en.getId().equals(id)) {
                return en;
            }
        }
    }

    ID getId();

    String getDescriptionKey();
}
...
public enum Statuses implements DbValuesEnumIface<Integer,Statuses>

Das bricht, weil T und ID nicht statisch sind und aus einem statischen Kontext nicht referenziert werden können.

Also, wie sollte das Obige modifiziert werden, um erfolgreich zu kompilieren, und wenn das nicht möglich ist, wie sollte das oben genannte implementiert werden, um den gewünschten Zweck zu erreichen, während Code-Duplizierung innerhalb implementierender Klassen vermieden wird.


5
2017-11-22 08:57


Ursprung


Antworten:


Da gibt es keine Beziehung zwischen static Methoden und die Typparameter der Klasse, die beschreiben, wie Instanzen parametrisiert werden, müssen Sie die static Methode generisch für sich. Der schwierige Teil besteht darin, die Deklarationen richtig zu machen, um alle benötigten Einschränkungen zu beschreiben. Und wie diese Antwort schon erklärt, du musst ein a Class Parameter, da ansonsten die Implementierung keine Möglichkeit hat, die eigentlichen Typargumente zu bekommen:

public interface DbValuesEnumIface<ID, T extends Enum<T>> {

   public static
   <ID, T extends Enum<T>&DbValuesEnumIface<ID,T>> T fromId(ID id, Class<T> type) {
        if (id == null) {
            return null;
        }
        for (T en : type.getEnumConstants()) {
            if (en.getId().equals(id)) {
                return en;
            }
        }
        throw new NoSuchElementException();
    }

    ID getId();

    String getDescriptionKey();
}

Beachten Sie, dass die Typparameter des static Methode sind unabhängig vom Typentyp der Klasse. Sie können in Betracht ziehen, ihnen verschiedene Namen zu geben, um sie zu verdeutlichen.

So, jetzt, dir gegeben enum Statuses implements DbValuesEnumIface<Integer,Statuses> Beispiel, Sie können die Methode wie verwenden Statuses status = DbValuesEnumIface.fromId(42, Statuses.class);


Beachten Sie, dass für default Methoden ist es möglich, auf den eigentlichen Typ zuzugreifen, als a Methode zur Bereitstellung der enum Art wird von der Implementierung bereitgestellt werden. Sie müssen nur das Vorhandensein der Methode innerhalb der angeben interface:

public interface DbValuesEnumIface<ID, T extends Enum<T>&DbValuesEnumIface<ID,T>> {

    public default T fromId(ID id) {
        if (id == null) {
            return null;
        }
        for (T en : getDeclaringClass().getEnumConstants()) {
            if (en.getId().equals(id)) {
                return en;
            }
        }
        throw new NoSuchElementException();
    }
    Class<T> getDeclaringClass();//no needed to implement it, inherited by java.lang.Enum
    ID getId();
    String getDescriptionKey();
}

Der offensichtliche Nachteil besteht jedoch darin, dass Sie eine Zielinstanz benötigen, um die Methode aufzurufen, d.h. Statuses status = Statuses.SOME_CONSTANT.fromId(42);


4
2017-11-22 10:08



Es gibt keinen einfachen Weg, soweit ich das beurteilen kann, zuerst müssen Sie Ihre Methode ändern auf default, können Sie mehr lesen Hier warum Sie Generika nicht in einem statischen Kontext verwenden können.

Aber selbst wenn Sie es ändern zu default Dinge funktionieren immer noch nicht, da Sie eine Instanz oder einen Klassentyp der Enumeration an diese Methode übergeben müssen, etwa so:

public default T fromId(ID id, Class<T> t) {
        if (id == null) {
            return null;
        }
        for (T en : t.getEnumConstants()) {
            // dome something
        }
        return null;
}

Jetzt treffen Sie ein anderes Problem, drinnen fromId - Das einzige, was du weißt, ist das T erweitert ein enum - nicht dein enum kann so sein getId (was scheint, dass Ihre Enums haben) sind einfach nicht vom Compiler bekannt.

Ich kenne keine einfache Möglichkeit, dies zu tun, außer eine Schnittstelle zu deklarieren, wie:

interface IID {
    public int getId();
} 

mach dein enum füge es ein:

static enum My implements IID {
    A {

        @Override
        public int getId() {
            // TODO Auto-generated method stub
            return 0;
        }

    };
}

und ändern Sie die Deklaration zu:

public interface DbValuesEnumIface<ID, T extends Enum<My> & IID>

2
2017-11-22 09:32



Sie können von wechseln static zu default und es wird erfolgreich kompiliert.

default EvaluationStatuses fromId(Integer id)


1
2017-11-22 09:04