Static Method in Interface with Generic signature

笑着哭i 提交于 2020-07-17 10:20:16

问题


As of Java 8 you can have default or static methods implemented in Interfaces as the below

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

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

I would like to declare the above with the static method having a signature that uses bounds defined by the implementing classes since the method's implementation should be the same for all,with the only thing different should be the generics declared, as such:

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>

which breaks because T and ID are not static and cant be referenced from a static context.

So, how should the above be modified to compile successfully and if thats not possible, how the above should be implemented to achieve the desired purpose while avoiding code duplication within implementing classes .


回答1:


Since there is no relationship between static methods and the class’ type parameters, which describe how instances are parameterized, you have to make the static method generic on its own. The tricky part is to get the declarations right to describe all needed constraints. And as this answer already explained, you need to a a Class parameter, as otherwise, the implementation has no chance to get hands on the actual type arguments:

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();
}

Note that the type parameters of the static method are independent from the class’ type parameter. You may consider giving them different names for clarity.

So now, given you enum Statuses implements DbValuesEnumIface<Integer,Statuses> example, you can use the method like Statuses status = DbValuesEnumIface.fromId(42, Statuses.class);


Note that for default methods, it is possible to access the actual type, as a method providing the enum type will be provided by the implementation. You only have to declare the presence of the method within the 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();
}

However, the obvious disadvantage is that you need a target instance to invoke the method, i.e. Statuses status = Statuses.SOME_CONSTANT.fromId(42);




回答2:


There is no easy way as far as I can tell, first you need to change your method to default, you can read more here of why you can't use generics in a static context.

But even if you change it to default things are still not going to work, since you need to pass an instance or class type of the enum to that method, something like this:

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

Now you are hitting another problem, inside fromId - the only thing that you know is that T extends an enum - not your enum may be, thus getId (which seems that your enums have) are simply not known by the compiler.

I don't know an easy way to make this work besides declaring an interface, like :

interface IID {
    public int getId();
} 

making your enum implement it:

static enum My implements IID {
    A {

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

    };
}

and change the declaration to:

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



回答3:


You can change from static to default and it will compile successfully.

default EvaluationStatuses fromId(Integer id)



来源:https://stackoverflow.com/questions/47430424/static-method-in-interface-with-generic-signature

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!