com.truthbean.common.mini.util.ClassHelper Maven / Gradle / Ivy
/**
* Copyright (c) 2021 TruthBean(Rogar·Q)
* Debbie is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package com.truthbean.common.mini.util;
import com.truthbean.common.mini.CommonConstants;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
/**
* 类相关的工具类
*
* @author TruthBean/Rogar·Q
* @since 0.0.1
* Created on 2018-01-08 22:07
* @version 0.0.1
*/
public class ClassHelper {
/**
* Constructor for ClassHelper.
*/
protected ClassHelper() {
}
/**
* Suffix for array class names: "[]"
*/
private static final String ARRAY_SUFFIX = "[]";
/**
* Prefix for internal array class names: "[L"
*/
private static final String INTERNAL_ARRAY_PREFIX = "[L";
/**
* Map with primitive type name as key and corresponding primitive type as
* value, for example: "int" -> "int.class".
*/
private static final Map> PRIMITIVE_TYPE_NAME_MAP = new HashMap<>(16);
/**
* Map with primitive wrapper type as key and corresponding primitive type
* as value, for example: Integer.class -> int.class.
*/
private static final Map, Class>> PRIMITIVE_WRAPPER_TYPE_MAP = new HashMap<>(8);
private static final Map> CLASS_MAP = new HashMap<>(17);
private static final Map, Class>> BASE_TYPE_MAP = new HashMap<>(8);
static {
PRIMITIVE_WRAPPER_TYPE_MAP.put(Boolean.class, boolean.class);
PRIMITIVE_WRAPPER_TYPE_MAP.put(Byte.class, byte.class);
PRIMITIVE_WRAPPER_TYPE_MAP.put(Character.class, char.class);
PRIMITIVE_WRAPPER_TYPE_MAP.put(Double.class, double.class);
PRIMITIVE_WRAPPER_TYPE_MAP.put(Float.class, float.class);
PRIMITIVE_WRAPPER_TYPE_MAP.put(Integer.class, int.class);
PRIMITIVE_WRAPPER_TYPE_MAP.put(Long.class, long.class);
PRIMITIVE_WRAPPER_TYPE_MAP.put(Short.class, short.class);
Set> primitiveTypeNames = new HashSet<>(16);
primitiveTypeNames.addAll(PRIMITIVE_WRAPPER_TYPE_MAP.values());
primitiveTypeNames.addAll(Arrays
.asList(boolean[].class, byte[].class, char[].class, double[].class, float[].class, int[].class,
long[].class, short[].class));
for (Class> primitiveClass : primitiveTypeNames) {
PRIMITIVE_TYPE_NAME_MAP.put(primitiveClass.getName(), primitiveClass);
}
CLASS_MAP.put(Integer.class.getName(), Integer.class);
CLASS_MAP.put(int.class.getName(), int.class);
CLASS_MAP.put(Short.class.getName(), Short.class);
CLASS_MAP.put(short.class.getName(), short.class);
CLASS_MAP.put(long.class.getName(), long.class);
CLASS_MAP.put(Long.class.getName(), Long.class);
CLASS_MAP.put(float.class.getName(), Float.class);
CLASS_MAP.put(Float.class.getName(), float.class);
CLASS_MAP.put(Double.class.getName(), Double.class);
CLASS_MAP.put(double.class.getName(), double.class);
CLASS_MAP.put(Boolean.class.getName(), Boolean.class);
CLASS_MAP.put(boolean.class.getName(), boolean.class);
CLASS_MAP.put(Character.class.getName(), Character.class);
CLASS_MAP.put(char.class.getName(), char.class);
CLASS_MAP.put(Byte.class.getName(), Byte.class);
CLASS_MAP.put(byte.class.getName(), byte.class);
CLASS_MAP.put(Object.class.getName(), Object.class);
BASE_TYPE_MAP.put(int.class, Integer.class);
BASE_TYPE_MAP.put(short.class, Short.class);
BASE_TYPE_MAP.put(long.class, Long.class);
BASE_TYPE_MAP.put(float.class, Float.class);
BASE_TYPE_MAP.put(double.class, Double.class);
BASE_TYPE_MAP.put(boolean.class, Boolean.class);
BASE_TYPE_MAP.put(char.class, Character.class);
BASE_TYPE_MAP.put(byte.class, Byte.class);
}
/**
* forName by current thread class loader
*
* @param name class name
* @return class
* @throws java.lang.ClassNotFoundException if the class not found
*/
public static Class> forNameWithThreadContextClassLoader(String name)
throws ClassNotFoundException {
return forName(name, Thread.currentThread().getContextClassLoader());
}
/**
* forNameWithCallerClassLoader.
*
* @param name a {@link java.lang.String} object.
* @param caller a {@link java.lang.Class} object.
* @return a {@link java.lang.Class} object.
* @throws java.lang.ClassNotFoundException if any.
*/
public static Class> forNameWithCallerClassLoader(String name, Class> caller)
throws ClassNotFoundException {
return forName(name, caller.getClassLoader());
}
/**
* getCallerClassLoader.
*
* @param caller a {@link java.lang.Class} object.
* @return a {@link java.lang.ClassLoader} object.
*/
public static ClassLoader getCallerClassLoader(Class> caller) {
return caller.getClassLoader();
}
/**
* getClassLoader.
*
* @param clazz a {@link java.lang.Class} object.
* @return a {@link java.lang.ClassLoader} object.
*/
public static ClassLoader getClassLoader(Class> clazz) {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = clazz.getClassLoader();
if (cl == null) {
try {
cl = ClassLoader.getPlatformClassLoader();
} catch (Throwable ignored1) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
cl = ClassLoader.getSystemClassLoader();
} catch (Throwable ignored2) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
}
return cl;
}
/**
* getDefaultClassLoader.
*
* @return a {@link java.lang.ClassLoader} object.
*/
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = ClassHelper.class.getClassLoader();
if (cl == null) {
try {
cl = ClassLoader.getPlatformClassLoader();
} catch (Throwable ignored1) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
cl = ClassLoader.getSystemClassLoader();
} catch (Throwable ignored2) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
}
return cl;
}
/**
* classForName.
*
* @param className a {@link java.lang.String} object.
* @return a {@link java.lang.Class} object.
* @throws java.lang.ClassNotFoundException if any.
*/
public static Class> classForName(String className) throws ClassNotFoundException {
ClassLoader classLoader = getDefaultClassLoader();
return classLoader.loadClass(className);
}
/**
* Replacement for Class.forName()
that also returns Class
* instances for primitives (like "int") and array class names (like
* "String[]").
*
* @param name the name of the Class
* @param classLoader the class loader to use (may be null
,
* which indicates the default class loader)
* @return Class instance for the supplied name
* @throws java.lang.ClassNotFoundException if the class was not found
* @throws java.lang.LinkageError if the class file could not be loaded
* @see Class#forName(String, boolean, ClassLoader)
*/
public static Class> forName(String name, ClassLoader classLoader)
throws ClassNotFoundException, LinkageError {
Class> clazz = resolvePrimitiveClassName(name);
if (clazz != null) {
return clazz;
}
// "java.lang.String[]" style arrays
if (name.endsWith(ARRAY_SUFFIX)) {
String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
Class> elementClass = forName(elementClassName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
// "[Ljava.lang.String;" style arrays
int internalArrayMarker = name.indexOf(INTERNAL_ARRAY_PREFIX);
if (internalArrayMarker != -1 && name.endsWith(CommonConstants.COLON)) {
String elementClassName = null;
if (internalArrayMarker == 0) {
elementClassName = name
.substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1);
} else if (name.startsWith(CommonConstants.LEFT_CURLY_BRACKET)) {
elementClassName = name.substring(1);
}
Class> elementClass = forName(elementClassName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = getClassLoader(ClassHelper.class);
}
return classLoaderToUse.loadClass(name);
}
/**
* Resolve the given class name as primitive class, if appropriate,
* according to the JVM's naming rules for primitive classes.
*
* Also supports the JVM's internal class names for primitive arrays. Does
* not support the "[]" suffix notation for primitive arrays; this is
* only supported by {@link #forName}.
*
* @param name the name of the potentially primitive class
* @return the primitive class, or null
if the name does not
* denote a primitive class or primitive array class
*/
public static Class> resolvePrimitiveClassName(String name) {
Class> result = null;
// Most class names will be quite long, considering that they
// SHOULD sit in a package, so a length check is worthwhile.
if (name != null && name.length() <= CommonConstants.PRIMITIVE_CLASS_NUMBER) {
// Could be a primitive - likely.
result = PRIMITIVE_TYPE_NAME_MAP.get(name);
}
return result;
}
/**
*
toShortString.
*
* @param obj a {@link java.lang.Object} object.
* @return a {@link java.lang.String} object.
*/
public static String toShortString(Object obj) {
if (obj == null) {
return "null";
}
return obj.getClass().getSimpleName() + "@" + System.identityHashCode(obj);
}
/**
* getWrapperClass.
*
* @param baseType a {@link java.lang.Class} object.
* @return a {@link java.lang.Class} object.
*/
public static Class> getWrapperClass(Class> baseType) {
for (Map.Entry, Class>> classClassEntry : BASE_TYPE_MAP.entrySet()) {
if (classClassEntry.getKey() == baseType) {
return classClassEntry.getValue();
}
}
return baseType;
}
/**
* getClass.
*
* @param className a {@link java.lang.String} object.
* @return a {@link java.lang.Class} object.
*/
public static Class> getClass(String className) {
return CLASS_MAP.get(className);
}
/**
* isInt.
*
* @param clazz a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isInt(Class> clazz) {
return clazz == Integer.class || clazz == int.class;
}
/**
* isShort.
*
* @param clazz a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isShort(Class> clazz) {
return clazz == Short.class || clazz == short.class;
}
/**
* isLong.
*
* @param clazz a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isLong(Class> clazz) {
return clazz == Long.class || clazz == long.class;
}
/**
* isFloat.
*
* @param clazz a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isFloat(Class> clazz) {
return clazz == float.class || clazz == Float.class;
}
/**
* isDouble.
*
* @param clazz a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isDouble(Class> clazz) {
return clazz == Double.class || clazz == double.class;
}
/**
* isBoolean.
*
* @param clazz a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isBoolean(Class> clazz) {
return clazz == Boolean.class || clazz == boolean.class;
}
/**
* isChar.
*
* @param clazz a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isChar(Class> clazz) {
return clazz == Character.class || clazz == char.class;
}
/**
* isByte.
*
* @param clazz a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isByte(Class> clazz) {
return clazz == Byte.class || clazz == byte.class;
}
/**
* isObject.
*
* @param clazz a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isObject(Class> clazz) {
return clazz == Object.class;
}
/**
* isBaseType.
*
* @param type a {@link java.lang.reflect.Type} object.
* @return a boolean.
*/
public static boolean isBaseType(java.lang.reflect.Type type) {
if (type instanceof Class) {
Class> clazz = (Class>) type;
return isBoolean(clazz) || isDouble(clazz) || isFloat(clazz)
|| isInt(clazz) || isLong(clazz) || isShort(clazz)
|| isChar(clazz) || isByte(clazz)
|| clazz == String.class || isObject(clazz);
}
return false;
}
/**
* isRawBaseType.
*
* @param clazz a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isRawBaseType(Class> clazz) {
return boolean.class == clazz || double.class == clazz || float.class == clazz || int.class == clazz
|| long.class == clazz || short.class == clazz || char.class == clazz || byte.class == clazz;
}
/**
* unwarp.
*
* @param value a {@link java.lang.Object} object.
* @return a {@link java.lang.Object} object.
*/
public static Object unwarp(Object value) {
if (value instanceof Integer) {
try {
return ((Integer) value).intValue();
} catch (NumberFormatException e) {
logger.log(System.Logger.Level.ERROR, "", e);
return null;
}
}
if (value instanceof Short) {
try {
return ((Short) value).shortValue();
} catch (NumberFormatException e) {
logger.log(System.Logger.Level.ERROR, "", e);
return null;
}
}
if (value instanceof Long) {
try {
return ((Long) value).longValue();
} catch (NumberFormatException e) {
logger.log(System.Logger.Level.ERROR, "", e);
return null;
}
}
if (value instanceof Float) {
try {
return ((Float) value).floatValue();
} catch (NumberFormatException e) {
logger.log(System.Logger.Level.ERROR, "", e);
return null;
}
}
if (value instanceof Double) {
try {
return ((Double) value).doubleValue();
} catch (NumberFormatException e) {
logger.log(System.Logger.Level.ERROR, "", e);
return null;
}
}
if (value instanceof Boolean) {
try {
return ((Boolean) value).booleanValue();
} catch (Exception e) {
logger.log(System.Logger.Level.ERROR, "", e);
return null;
}
}
if (value instanceof Byte) {
try {
return ((Byte) value).byteValue();
} catch (NumberFormatException e) {
logger.log(System.Logger.Level.ERROR, "", e);
return null;
}
}
if (value instanceof Character) {
try {
return ((Character) value).charValue();
} catch (Exception e) {
logger.log(System.Logger.Level.ERROR, "", e);
return null;
}
}
return null;
}
/**
* isSameType.
*
* @param aType a {@link java.lang.Class} object.
* @param bType a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isSameType(Class> aType, Class> bType) {
return (isInt(aType) && isInt(bType)
|| (isShort(aType) && isShort(bType))
|| (isLong(aType) && isLong(bType))
|| (isFloat(aType)) && isFloat(bType)
|| (isDouble(aType) && isDouble(bType))
|| (isBoolean(aType) && isBoolean(bType))
|| (isChar(aType) && isChar(bType))
|| (isByte(aType)) && isByte(bType)
|| (aType == bType));
}
/**
* isArrayType.
*
* @param clazz a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isArrayType(Class> clazz) {
return clazz == Set.class || clazz == Map.class || clazz == List.class;
}
/**
* isArrayType.
*
* @param type a {@link java.lang.reflect.Type} object.
* @return a boolean.
*/
public static boolean isArrayType(java.lang.reflect.Type type) {
if (type instanceof Class) {
Class> clazz = (Class>) type;
return clazz == Set.class || clazz == Map.class || clazz == List.class;
} else if (type instanceof ParameterizedType) {
java.lang.reflect.Type rawType = ((ParameterizedType) type).getRawType();
return rawType == Set.class || rawType == Map.class || rawType == List.class;
}
return false;
}
/**
* isTimeType.
*
* @param type a {@link java.lang.reflect.Type} object.
* @return a boolean.
*/
public static boolean isTimeType(java.lang.reflect.Type type) {
if (type instanceof Class) {
Class> clazz = (Class>) type;
return clazz == Date.class || clazz == Calendar.class
|| Date.class.isAssignableFrom(clazz)
|| (clazz.getPackageName().startsWith("java.time"));
}
return false;
}
/**
* isAbstractOrInterface.
*
* @param type a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean isAbstractOrInterface(Class> type) {
return Modifier.isAbstract(type.getModifiers()) || type.isInterface();
}
/**
* hasDefaultConstructor.
*
* @param type a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean hasDefaultConstructor(Class> type) {
Constructor>[] constructors = type.getConstructors();
for (Constructor> constructor : constructors) {
if (constructor.getParameterCount() == 0) {
return true;
}
}
return false;
}
/**
* getExceptions.
*
* @param exceptionTypes an array of {@link java.lang.Class} objects.
* @return an array of {@link java.lang.String} objects.
*/
public static String[] getExceptions(Class>[] exceptionTypes) {
if (exceptionTypes != null && exceptionTypes.length > 0) {
String[] exceptions = new String[exceptionTypes.length];
for (int i = 0; i < exceptionTypes.length; i++) {
Class> exceptionType = exceptionTypes[i];
exceptions[i] = exceptionType.getName().replace(".", "/");
}
return exceptions;
}
return null;
}
/**
* isOrValueOf.
*
* @param clazz a {@link java.lang.Class} object.
* @param target a {@link java.lang.String} object.
* @return a boolean.
*/
public static boolean isOrValueOf(Class> clazz, String target) {
if (target.getClass().equals(clazz)) {
return true;
} else {
if (NumericUtils.isInteger(target)) {
return clazz == Integer.class || clazz == int.class
|| clazz == Short.class || clazz == short.class
|| clazz == Long.class || clazz == long.class
|| clazz == BigInteger.class;
}
if (NumericUtils.isDecimal(target)) {
return clazz == Float.class || clazz == float.class
|| clazz == Double.class || clazz == double.class
|| clazz == BigDecimal.class;
}
return (CommonConstants.TRUE.equalsIgnoreCase(target.strip())
|| CommonConstants.FALSE.equalsIgnoreCase(target.trim()))
&& (clazz == Boolean.class || clazz == boolean.class);
}
}
/**
* filterAnnotation.
*
* @param annotationType a {@link java.lang.Class} object.
* @return a boolean.
*/
public static boolean filterAnnotation(Class extends Annotation> annotationType) {
return !Target.class.equals(annotationType) && !Retention.class.equals(annotationType) &&
!Repeatable.class.equals(annotationType) && !Inherited.class.equals(annotationType) &&
!Documented.class.equals(annotationType) && !Deprecated.class.equals(annotationType) &&
!Native.class.equals(annotationType) && !SafeVarargs.class.equals(annotationType) &&
!FunctionalInterface.class.equals(annotationType) &&
!SuppressWarnings.class.equals(annotationType) && !Override.class.equals(annotationType);
}
/**
* getSuperGenericType.
*
* @param clazz a {@link java.lang.Class} object.
* @param a T object.
* @return a {@link java.lang.Class} object.
*/
@SuppressWarnings("unchecked")
public static Class getSuperGenericType(Class> clazz) {
return (Class) getSuperClassGenricType(clazz, 0);
}
/**
* 通过反射, 获得定义 Class 时声明的父类的泛型参数的类型
* 如:public EmployeeDao extends BaseDao<Employee, String>
*
* @param clazz class
* @param index index
* @return class
*/
public static Class> getSuperClassGenricType(Class> clazz, int index) {
Type genType = clazz.getGenericSuperclass();
if (!(genType instanceof ParameterizedType)) {
return Object.class;
}
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
if (index >= params.length || index < 0) {
return Object.class;
}
if (!(params[index] instanceof Class)) {
return Object.class;
}
return (Class>) params[index];
}
private static final System.Logger logger = System.getLogger(ClassHelper.class.getName());
}