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 getAll(); } // Implementacja oparta o mechanizmy Springa public abstract class AbstractDAO<T extends BaseEntity, K> extends JpaDaoSupport implements GenericDAO<T, K> { private Class typeClass; protected AbstractDAO(Class typeClass) { this.typeClass = typeClass; } // pobranie obiektu na bazie id public T readById(K id) { return getJpaTemplate().find(typeClass, id); } }

// 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?