Do tej pory tworząc kod z użyciem typów generycznych zawsze borykałem się z problemem - jak dobrać się do klasy na podstawie któregoś parametru. Rozwiązanie znalazłem przeglądając w internecie kod jakieś przykładowej aplikacji. Ot znowu kilka linii które uporządkują kod i zmniejszą ilość powtórzeń. :-)
Dbanie o porządek w kodzie to obowiązek każdego programisty. Stąd podział aplikacji na warstwy, wyodrębnianie wspólnego kodu do typów abstrakcyjnych i tak dalej. Są jednak miejsca, których do tej pory nie potrafiłem uporządkować dobrze - były to wszelkiego rodzaju DAO, które zawsze wyglądały podobnie. Interfejs, klasa abstrakcyjna, pochodna wywołująca chroniony konstruktor. Wszystko po to by w klasie abstrakcyjnej móc zdefiniować metodę readById. Oto przykład takiego kodu:
[java]// Ogólny interfejs dostępu do danych
// T to typ encji, K to typ klucza w danej encji.
public interface GenericDAO<T, K> {
void save(T instance);
void create(T newInstance);
void update(T existingInstance);
boolean delete(T instance);
T readById(K id);
List
// Przekazanie User.class do AbstractDAO by te mogło // użyć go do odpytywania bazy danych public class UserJpaDAO extends AbstractDAO<User, Long> implements UserDAO { public UserJpaDAO() { super(User.class); } } [/java] W tym wypadku typ generyczny zamiast uprościć życie nieco je komplikuje - w ostatniej klasie musimy pisać dwa razy User - raz jako parametr typu, dwa przekazując go do konstruktora klasy abstrakcyjnej. Logicznie rzecz biorąc. Znany jest typ generyczny zatem to nazwa klasy też powinna być do wykrycia. I tak rzeczywiście jest. Od JDK 1.5 istnieje interfejs ParameterizedType, który zawiera metodę getActualTypeArguments, Wystarczy dobrać się do tego obiektu i będzie z górki. Oto jak ja to rozwiązałem: [java] // Copyright (C) 2009 Code-House // All rights reserved package org.code_house.manager.dataaccess.jpa.util;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type;
/\\ \* Klasa pomocnicza do operowania na typach generycznych. \* \* @author Łukasz Dywicki luke@code-house.org \* \* $Id$ */ public class GenericTypeUtil {
/\\ \* Konstruktor prywatny, na wypadek gdyby ktoś chciał stworzyć instancję \* utila. :) */ private GenericTypeUtil() {}
/\\ \* Pobranie argumentów dla danego typu na bazie klasy. */ public static Class[] getGenericTypes(Class type) { Type superclass = type.getGenericSuperclass(); if (superclass instanceof ParameterizedType) { ParameterizedType genericTypeClass = (ParameterizedType) superclass; return cast(genericTypeClass.getActualTypeArguments()); } return new Class[0]; }
// Rzutowanie w dół z Type do Class. public static Class[] cast(Type[] types) { Class[] classTypes = new Class[types.length]; for (int i = 0, j = types.length; i < j; i++) { classTypes[i] = (Class) types[i]; } return classTypes; } } [/java]
Co zwróci nam wywołanie GenericTypeUtil.getGenericTypes(UserJpaDAO.class)? To czego potrzebujemy! :) [plain] [class org.code_house.manager.domain.User, class java.lang.Long] [/plain] Czy znaliście ten sposób? A może tylko ja byłem nieświadom tego rozwiązania?