Pobieranie typu klucza z pustej EnumMap

Klasa java.util.EnumMap jest bardzo wydajną implementacją java.util.Map dla wyliczeń. Jest wydajna dlatego, że hasze dla kluczy są wartościami porządkowymi instancji danego wyliczenia. Dokładniej jej implementacja przechowuje wartości w tablicy obiektów a indeksy to wartości porządkowe wyliczeń. W metodzie get możemy znaleźć taki kod:

vals[((Enum)key).ordinal()]

służący do pobierania wartości z tablicy vals dla klucza key. Żeby miało to sens (aby uniknąć powtarzania się kluczy) jeden obiekt typu EnumMap musi mieć klucze będące obiektami tego samego wyliczenia. Mechanizm typów generycznych nie wystarczy do zapamiętania typu klucza bo informacje o typach generycznych są wymazywane podczas kompilacji. Obiekty EnumMap zapamiętują klasę typu klucza wewnątrz. EnumMap można skonstruować na trzy sposoby ale w każdym z konstruktorów musimy dostarczyć informacje o typie klucza. Albo podając obiekt typu Class, albo jest ona pobierana z innego obiektu EnumMap albo w przypadku konstrukcji z Map jest wymagane, żeby zawierała co najmniej jedno mapowanie z którego zostanie pobrany typ klucza. Z obiektu EnumMap posiadającego co najmniej jedno mapowanie typ klucza możemy pobrać w prosty sposób wywołując .keySet().iterator().next().getClass().

Problem pojawia się gdy mamy do czynienia z pustym obiektem typu EnumMap (na przykład parametrem metody, lub po serializacji). Nie posiada on metody umożliwiającej pobranie typu klucza a informacje o typach generycznych zostały wymazane podczas kompilacji. Jeśli nie mamy wyjścia jest jeden sposób. Nie jest on bezpieczny, jest uzależniony od implementacji EnumMap i czasem kod może nie mieć odpowiednich uprawnień. Myślę jednak, że w wyjątkowych przypadkach można spróbować. Oto przykładowy fragment kodu:

1
2
3
4
5
6
7
8
9
    enum MyEnum { A, B; };
    public static void main(String a[]) throws Exception {
        EnumMap map = new EnumMap (MyEnum.class);
        /// tutaj map może być na przykład zserializowany /////
        Field keyTypeField = EnumMap.class.getDeclaredField("keyType");
        keyTypeField.setAccessible(true);
        Class keyType = (Class) keyTypeField.get(map);
        System.out.println (keyType.getName());
    }

Jak widać można użyć refleksji, żeby odczytać wartość prywatnego pola przechowującego typ klucza. Przykład działa na Sun JRE

Zacheusz Siedlecki

Komentarze

Chcesz coś napisać?





*