Java tutorial
/* * Copyright (c) 2007 NTT DATA Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jp.terasoluna.fw.util; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * <code>Generics</code>???? */ public class GenericsUtil { /** * */ private static final Log log = LogFactory.getLog(GenericsUtil.class); /** * ????? * <p> * <h5>??????</h5> ?????????????? ?????? ?????????? ?<code>WildCardType</code>? * ????????????? ??????????? * <ul> * <li>????(???????) <code><pre> * public class Descendant extends Generic<String, Integer> { * ... * } * </pre></code> ???????[<code>String</code>, <code>Integer</code>]? ??????</li> * <li>????(????????) <code><pre> * public class Descendant extends Generic<String[], Integer> { * ... * } * </pre></code> ???????[<code>String[]</code>, <code>Integer</code>]? ??????</li> * <li>????(?????????) <code><pre> * public class Descendant * extends Generic<String[], Map<String, Object>> { * ... * } * </pre></code> ???????[<code>String[]</code>, <code>Map</code>]? ??????</li> * <li>??????(???????) <code><pre> * public class Descendant<P, Q> extends Generic<S, T> { * ... * } * </pre></code></li> * <li>??????(???????) <code><pre> * public class Descendant<P extends String, Q super Bean> * extends Generic<S, T> { * ... * } * </pre></code></li> * <li>??????(?????) <code><pre> * Generic<String, Integer> descendant = * new Generic<String, Integer>(); * </pre></code></li> * </ul> * </p> * <p> * <h5>??</h5> <code>genericType</code>?<code>descendantClass</code>?? ?????????????? * <ul> * <li>? <code><pre> * public class Child<S, T> extends Generic<S, T> { * ... * } * * public class GrandChild<S, T> extends Child<S, T> { * ... * } * * public class Descendant extends GrandChild<String, Integer> { * ... * } * </pre></code> ???????[<code>String</code>, <code>Integer</code>]? ??????</li> * </ul> * </p> * <p> * <h5>?????</h5> <code>genericType</code>?<code>descendantClass</code>??? ??????????? * <code>genercClass</code>????????? * <ul> * <li>???? <code><pre> * public class Generic<S, T> { * ... * } * * public class Child<A, T, B, S> extends Generic<S, T> { * ... * } * * public class Descendant * extends Generic<Boolean, Integer, Double, String> { * ... * } * </pre></code> ???????[<code>String</code>, <code>Integer</code>]? ??????</li> * </ul> * </p> * @param <T> ??? * @param genericClass ?? * @param descendantClass <code>genericsClass</code>?? ??? * @return ??<code>Class</code>?? ?<code>genercClass</code>????? * @throws IllegalArgumentException <code>genericClass</code>? <code>null</code>?? <code>descendantClass</code>? * <code>null</code>?? * @throws IllegalStateException <code>descendantClass</code>??? ????????? <code>genercClass</code> * ????? ????? */ @SuppressWarnings("rawtypes") public static <T> Class[] resolveParameterizedClass(Class<T> genericClass, Class<? extends T> descendantClass) throws IllegalArgumentException, IllegalStateException { if (genericClass == null) { throw new IllegalArgumentException("Argument 'genericsClass' (" + Class.class.getName() + ") is null"); } if (descendantClass == null) { throw new IllegalArgumentException("Argument 'descendantClass'(" + Class.class.getName() + ") is null"); } List<ParameterizedType> ancestorTypeList = getAncestorTypeList(genericClass, descendantClass); ParameterizedType parameterizedType = ancestorTypeList.get(ancestorTypeList.size() - 1); // parameterizedType??? Type ?? // AbstractBLogic<P, R>??P?R Type[] actualTypes = parameterizedType.getActualTypeArguments(); // ?????? Class[] actualClasses = new Class[actualTypes.length]; for (int i = 0; i < actualTypes.length; i++) { // actualTypes[i]i? // ancestorList???? actualClasses[i] = resolveTypeVariable(actualTypes[i], ancestorTypeList); } return actualClasses; } /** * ???? * <p> * <h5>??????</h5> ?????????????? ?????? ?????????? ?<code>WildCardType</code>? * ????????????? ??????????? * <ul> * <li>????(???????) <code><pre> * public class Descendant extends Generic<String, Integer> { * ... * } * </pre></code> ???????<code>String</code>???<code>Integer</code>? ????</li> * <li>????(????????) <code><pre> * public class Descendant extends Generic<String[], Integer> { * ... * } * </pre></code> ???????<code>String[]</code>???<code>Integer</code>? ????</li> * <li>????(?????????) <code><pre> * public class Descendant * extends Generic<String[], Map<String, Object>> { * ... * } * </pre></code> ???????<code>String[]</code>???<code>Map</code> ?????</li> * <li>??????(???????) <code><pre> * public class Descendant<P, Q> extends Generic<S, T> { * ... * } * </pre></code></li> * <li>??????(???????) <code><pre> * public class Descendant<P extends String, Q super Bean> * extends Generic<S, T> { * ... * } * </pre></code></li> * <li>??????(?????) <code><pre> * Generic<String, Integer> descendant = * new Generic<String, Integer>(); * </pre></code></li> * </ul> * </p> * <p> * <h5>??</h5> <code>genericType</code>?<code>descendantClass</code>?? ?????????????? * <ul> * <li>? <code><pre> * public class Child<S, T> extends Generic<S, T> { * ... * } * * public class GrandChild<S, T> extends Child<S, T> { * ... * } * * public class Descendant extends GrandChild<String, Integer> { * ... * } * </pre></code> ???????<code>String</code>???<code>Integer</code> ?????</li> * </ul> * </p> * <p> * <h5>?????</h5> <code>genericType</code>?<code>descendantClass</code>??? ??????????? * <code>genercClass</code>????????? * <ul> * <li>???? <code><pre> * public class Generic<S, T> { * ... * } * * public class Child<A, T, B, S> extends Generic<S, T> { * ... * } * * public class Descendant * extends Generic<Boolean, Integer, Double, String> { * ... * } * </pre></code> ???????<code>String</code>???<code>Integer</code> ?????</li> * </ul> * </p> * @param <T> ??? * @param genericClass ?? * @param descendantClass <code>genericsClass</code>?? ??? * @param index ????? * @return ??<code>Class</code> * @throws IllegalArgumentException <code>genericClass</code>? <code>null</code>?? <code>descendantClass</code>? * <code>null</code>?? <code>index</code>?<code>0</code>???????? ???? * @throws IllegalStateException <code>descendantClass</code>??? ????????? <code>genercClass</code> * ????? ????? */ public static <T> Class<? extends Object> resolveParameterizedClass(Class<T> genericClass, Class<? extends T> descendantClass, int index) throws IllegalArgumentException, IllegalStateException { if (genericClass == null) { throw new IllegalArgumentException("Argument 'genericsClass' (" + Class.class.getName() + ") is null"); } if (descendantClass == null) { throw new IllegalArgumentException("Argument 'descendantClass'(" + Class.class.getName() + ") is null"); } List<ParameterizedType> ancestorTypeList = getAncestorTypeList(genericClass, descendantClass); ParameterizedType parameterizedType = ancestorTypeList.get(ancestorTypeList.size() - 1); // parameterizedType??? Type ?? // AbstractBLogic<P, R>??P?R Type[] actualTypes = parameterizedType.getActualTypeArguments(); // ?????? if (index < 0 || index >= actualTypes.length) { throw new IllegalArgumentException("Argument 'index'(" + Integer.toString(index) + ") is out of bounds of" + " generics parameters"); } // actualTypes[index]index? // ancestorList???? return resolveTypeVariable(actualTypes[index], ancestorTypeList); } /** * ??????? <code>ParameterizedType</code>??? * @param <T> ??? * @param genericClass ?? * @param descendantClass <code>genericsClass</code>?? ??? * @return ??????? <code>ParameterizedType</code>? * @throws IllegalStateException <code>descendantClass</code>? ??????????? <code>genercClass</code> * ????? ????? */ protected static <T> List<ParameterizedType> getAncestorTypeList(Class<T> genericClass, Class<? extends T> descendantClass) throws IllegalStateException { List<ParameterizedType> ancestorTypeList = new ArrayList<ParameterizedType>(); Class<?> clazz = descendantClass; boolean isInterface = genericClass.isInterface(); while (clazz != null) { Type type = clazz.getGenericSuperclass(); if (checkParameterizedType(type, genericClass, ancestorTypeList)) { break; } // ?????? // ?????? if (!isInterface) { clazz = clazz.getSuperclass(); continue; } if (checkInterfaceAncestors(genericClass, ancestorTypeList, clazz)) { break; } // ??????????? // ???????? // ????????????? // ?????????? // ???Generics?API????????????? // ????????? clazz = clazz.getSuperclass(); } // ????? // AbstractBLogic<P, R> if (ancestorTypeList.isEmpty()) { throw new IllegalStateException( "Argument 'genericClass'(" + genericClass.getName() + ") does not declare type parameter"); } // ??????????????? // ?????????? // ???Generics?API????????????? // ????????? ParameterizedType targetType = ancestorTypeList.get(ancestorTypeList.size() - 1); if (!targetType.getRawType().equals(genericClass)) { throw new IllegalStateException("Class(" + descendantClass.getName() + ") is not concrete class of Class(" + genericClass.getName() + ")"); } return ancestorTypeList; } /** * ?????? <code>ParameterizedType</code>??? * @param <T> ??? * @param genericClass ?? * @param ancestorTypeList <code>ParameterizedType</code> ? * @param clazz ? * @return ?????????<code>true</code> ????????<code>false</code> */ protected static <T> boolean checkInterfaceAncestors(Class<T> genericClass, List<ParameterizedType> ancestorTypeList, Class<?> clazz) { boolean genericTypeFound = false; Type[] interfaceTypes = clazz.getGenericInterfaces(); for (Type interfaceType : interfaceTypes) { genericTypeFound = checkParameterizedType(interfaceType, genericClass, ancestorTypeList); if (genericTypeFound) { return true; } @SuppressWarnings("rawtypes") Class[] interfaces = clazz.getInterfaces(); for (Class<?> interfaceClass : interfaces) { if (checkInterfaceAncestors(genericClass, ancestorTypeList, interfaceClass)) { return true; } } } return false; } /** * <code>Type</code>???<code>ParameterizedType</code> ???????????? * @param <T> ??? * @param type ? * @param genericClass ?? * @param ancestorTypeList <code>ParameterizedType</code> ? * @return <code>type</code>???????? */ protected static <T> boolean checkParameterizedType(Type type, Class<T> genericClass, List<ParameterizedType> ancestorTypeList) { if (!(type instanceof ParameterizedType)) { return false; } // ParameterizedType?????ParameterizedType // ?? ParameterizedType parameterlizedType = (ParameterizedType) type; // ?Generics????ParameterizedType? // ???????? // ??????????????????? // ?????????? // ???Generics?API????????????? // ????????? if (!genericClass.isAssignableFrom((Class<?>) parameterlizedType.getRawType())) { return false; } ancestorTypeList.add(parameterlizedType); // #getRawType???Type?? if (parameterlizedType.getRawType().equals(genericClass)) { return true; } return false; } /** * ??<code>Type</code>?? * @param type ????<code>Type</code> * @param ancestorTypeList <code>type</code>??? ??????<code>ParameterizedType</code>? * @return ? * @throws IllegalStateException <code>type</code>? <code>Class</code>???? <code>TypeVariable</code>????? * <code>type</code>?? ????????? <code>type</code>???<code>Class</code>???? * (??)? */ protected static Class<? extends Object> resolveTypeVariable(Type type, List<ParameterizedType> ancestorTypeList) throws IllegalStateException { if (isNotTypeVariable(type)) { return getRawClass(type); } // TypeVariable:? TypeVariable<?> targetType = (TypeVariable<?>) type; Type actualType = null; for (int i = ancestorTypeList.size() - 1; i >= 0; i--) { ParameterizedType ancestorType = ancestorTypeList.get(i); // ????? GenericDeclaration declaration = targetType.getGenericDeclaration(); if (!(declaration instanceof Class)) { throw new IllegalStateException("TypeVariable(" + targetType.getName() + " is not declared at Class " + "(ie. is declared at Method or Constructor)"); } // ?Generics???????? Class<?> declaredClass = (Class<?>) declaration; if (declaredClass != ancestorType.getRawType()) { continue; } // ???????? // ConcreteAbstractBLogic<R,P> extends AbstractBLogic<P,R> // ????????type???? Type[] parameterTypes = declaredClass.getTypeParameters(); int index = ArrayUtils.indexOf(parameterTypes, targetType); if (index == -1) { // ??????????????? // ?????????? // ???Generics?API????????????? // ????????? throw new IllegalStateException("Class(" + declaredClass.getName() + ") does not declare TypeValidable(" + targetType.getName() + ")"); } actualType = ancestorType.getActualTypeArguments()[index]; if (log.isDebugEnabled()) { log.debug("resolved " + targetType.getName() + " -> " + actualType); } if (isNotTypeVariable(actualType)) { return getRawClass(actualType); } targetType = (TypeVariable<?>) actualType; } throw new IllegalStateException( "Concrete type of Type(" + type + ") was not found in ancestorList(" + ancestorTypeList + ")"); } /** * <code>type</code>?<code>Class</code> ????<code>TypeVariable</code>?? * @param type <code>Type</code> * @return <code>type</code>? <code>Class, ParameterizedType, GenericArrayType</code>?? <code>true</code> * <code>type</code>?<code>TypeVariable</code>?? <code>false</code> * @throws IllegalStateException <code>type</code>? <code>Class</code>?<code>ParameterizedType</code>? * <code>GenericArrayType</code>?<code>TypeVariable</code>? ?????? */ protected static boolean isNotTypeVariable(Type type) throws IllegalStateException { if (type instanceof Class) { return true; } else if (type instanceof TypeVariable) { return false; } else if (type instanceof ParameterizedType) { return true; } else if (type instanceof GenericArrayType) { return true; } throw new IllegalStateException("Type(" + type + ") is not instance of " + TypeVariable.class.getName() + ", " + ParameterizedType.class.getName() + ", " + GenericArrayType.class.getName() + " nor " + Class.class.getName()); } /** * <code>type</code>???? * @param type <code>Type</code> * @return <code>Class</code> * @throws IllegalStateException <code>type</code>? <code>Class</code>?<code>ParameterizedType</code>? * <code>GenericArrayType</code>??????? */ @SuppressWarnings("unchecked") protected static Class<? extends Object> getRawClass(Type type) throws IllegalStateException { if (type instanceof Class) { return (Class<? extends Object>) type; } else if (type instanceof ParameterizedType) { return getRawClass(((ParameterizedType) type).getRawType()); } else if (type instanceof GenericArrayType) { Type componentType = ((GenericArrayType) type).getGenericComponentType(); Class<? extends Object> componentClass = getRawClass(componentType); return Array.newInstance(componentClass, 0).getClass(); } throw new IllegalStateException("Type(" + type + ") is not instance of " + ParameterizedType.class.getName() + ", " + GenericArrayType.class.getName() + " nor " + Class.class.getName()); } }