2016-08-29 23 views
6

Mam kilka wyliczeń, które można znaleźć przez int. Odbywa się to metodą statyczną w wyliczeniu. Na przykład:Metody statyczne w interfejsach nie działają, jak uzyskać określoną wartość wyliczeniową z kilku wyliczeń?

enum Foo { 
A, B, C, D, ... ; 
    public static Foo fromInt(int i) { 
    switch(i) { 
    case 15: return A; 
    case 42: return B; 
    ... 
} 
} 
enum Bar { 
BLA, BOO, BEE, ... ; 
    public static Bar fromInt(int i) { 
    switch(i) { 
    case 78: return BLA; 
    case 22: return BOO; 
    ... 
} 
} 
... 

Teraz w niektórych kodu Mam ogólny typ T który jest gwarancją jednego z tych teksty stałe i mam całkowitą i. Jak mogę wywołać metodę fromInt i pobrać wystąpienie wyliczenia według wartości i?

Próbowałem stworzyć interfejs za pomocą statycznej metody fromInt i pozwolić, aby enum zaimplementował go, ale statyczne metody nie działają w interfejsach.

nie mogę używać Java 8.

+0

Co z odbiciem? –

+0

Masz więc gdzieś zmienną 'Class ', prawda? Czy możesz podać swój kod tego rodzaju? – sp00m

+2

Czy masz pewność, że zestawy wartości 'int' w' Foo' i 'Bar' są rozłączne? – bradimus

Odpowiedz

2

krótka odpowiedź nie można

Długa odpowiedź można też przekazać Class tego wyliczenia do metody i wywołać fromInt przez odbicie lub utworzyć interfejs fabrycznego i wstaw to dla każdego wyliczenia, a następnie przeprowadź właściwą instancję do swojej metody.

0

Rozszerzanie @talex's answer:

Odbicie:

public class YourEnumFactory { 

    public static <T extends Enum<T>> T get(Class<T> type,int i) { 
    try { 
     return (T) type.getDeclaredMethod("fromInt", int.class).invoke(null, i); 
    } catch (Exception e) { 
     throw new IllegalStateException("Could not find instance of '" + type + "' given '" + i + "'", e); 
    } 
    } 

} 

Fabryki:

public class YourEnumFactory { 

    private interface Factory<T> { 
    T fromInt(int i); 
    } 

    private static final Map<Class<?>, Factory<?>> FACTORIES; 
    static { 
    Map<Class<?>, Factory<?>> factories = new HashMap<>(); 
    factories.put(Foo.class, new Factory<Foo>() { 

     @Override 
     public Foo fromInt(int i) { 
     return Foo.fromInt(i); 
     } 

    }); 
    factories.put(Bar.class, new Factory<Bar>() { 

     @Override 
     public Bar fromInt(int i) { 
     return Bar.fromInt(i); 
     } 

    }); 
    FACTORIES = Collections.unmodifiableMap(factories); 
    } 

    public static <T extends Enum<T>> T get(Class<T> type, int i) { 
    Factory<T> factory = (Factory<T>) FACTORIES.get(type); 
    if (factory != null) { 
     return factory.fromInt(i); 
    } else { 
     throw new InvalidParameterException("No factory for type '" + type + "'"); 
    } 
    } 

} 

Jak zawsze staram się uniknąć refleksji, chciałbym osobiście wybrać to drugie rozwiązanie.

0

Nie można zrobić to tak, ale chciałbym zrobić to tak:

interface HasId() { 
    public int getId(); 
} 

public class MyRegistry { 
    private static Map<Class<?>, Map<Integer, Object>> REGISTRY = new HashMap<>(); 
    public static <T extends HasId> T fromInt(int id, Class<T> clazz) { 
     return clazz.cast(REGISTRY.getOrDefault(clazz, Collections.emptyMap()).get(id)); 
    } 
    public static void register(HasId obj) { 
     REGISTRY.putIfAbsent(obj.getClass(), new HashMap<>()).put(obj.getId, obj); 
    } 
} 

enum Foo implements HasId { 
    A(15), B(42), C(86), D(99); 
    private final int id; 
    Foo(int id) { 
     this.id = id; 
     MyRegistry.register(this); 
    } 
    public int getId() { 
     return id; 
    } 
} 
// ditto for other enums or non enums as you like 

Następnie użyć:

Foo foo = MyRegistry.fromInt(15, Foo.class); // A 

Zastrzeżenie: Kod nie może skompilować lub działać tak jak został zablokowany na moim telefonie (ale jest spora szansa, że ​​zadziała)

+0

Zauważ, że 'putIfAbsent' nie działa tak, jak oczekujesz (nie jest to podobne do' computeIfAbsent'). 'putIfAbsent' zawsze zwraca starą wartość, tj.' null', jeśli nie było wcześniejszego odwzorowania, a poprzednia (wciąż obecna) wartość była inna. Co więcej, nie pomaga w korzystaniu z mapy wątków, gdy jest dostępny bez wątków "Set" jest dostępny bez żadnej ochrony. – Holger

+0

Ponadto kod opiera się na inicjalizacji klasy rzeczywistego typu 'enum', który nie jest gwarantowany. Z drugiej strony byłoby łatwo pozwolić, aby 'fromInt' powtórzył wartości wyliczeniowe, eliminując potrzebę rejestracji. – Holger

+0

@Holger Czy inicjalizacja nie jest gwarantowana, ponieważ literówka klasy została przekazana do połączenia? – Bohemian