| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
See also the ClassGraph API overview.
ClassGraph parses classfile-internal type signature and type descriptor strings (like "Ljava/lang/String;") into type signature objects ClassTypeSignature, MethodTypeSignature, and TypeSignature.
The hierarchy of type signature classes reflects the structure of Java's internal type signature system.
The following methods can be used to get a TypeSignature (getTypeSignatureOrTypeDescriptor() is preferable to calling either getTypeSignature() or getTypeDescriptor(), since it will use the type signature, including generic information, if available, otherwise it will fall back to using the type descriptor, without generic information):
For example, to check the result type of a method:
try (ScanResult scanResult = new ClassGraph().enableNonSystemModules().enableClasspath()
.enableAllInfo()
.acceptPackages(packageName).scan()) {
ClassInfo classInfo = scanResult.getClassInfo(className);
MethodInfoList methodInfoList = classInfo.getMethodInfo(methodName);
MethodInfo methodInfo = methodInfoList.get(0); // First method named {methodName}
MethodTypeSignature methodTypeSignature = methodInfo
.getTypeSignatureOrTypeDescriptor();
TypeSignature resultTypeSignature = methodTypeSignature.getResultType();
if (resultTypeSignature instanceof ArrayTypeSignature arrayTypeSignature) {
System.out.println("Method " + methodInfo.getName() + " returns a "
+ arrayTypeSignature.getNumDimensions() + "-dimensional array of "
+ arrayTypeSignature.getElementTypeSignature());
} else if (resultTypeSignature instanceof BaseTypeSignature baseTypeSignature) {
System.out.println("Method " + methodInfo.getName() + " returns "
+ baseTypeSignature.getTypeName());
} else if (resultTypeSignature instanceof ClassRefTypeSignature classRefTypeSignature) {
System.out.println("Method " + methodInfo.getName() + " returns "
+ classRefTypeSignature.getFullyQualifiedClassName());
} else if (resultTypeSignature instanceof TypeVariableSignature typeVariableSignature) {
// Attempt to resolve type variable
TypeParameter typeParameter = typeVariableSignature.resolve();
System.out.println("Method " + methodInfo.getName()
+ " returns type variable " + typeVariableSignature.getName()
+ ", which resolves to " + typeParameter);
}
}You probably have to cast a value of TypeSignature returned by the ClassGraph API into the correct subclass type (e.g. ClassRefTypeSignature) to get any useful information. For example:
public class TestReadingTypeArgs {
static class A<X> {
}
static abstract class B {
abstract A<Integer> a();
}
public static void main(String[] args) {
try (ScanResult scanResult = new ClassGraph().enableNonSystemModules().enableClasspath()
.acceptPackages(TestReadingTypeArgs.class.getPackage().getName())
.enableAllInfo().scan()) {
ClassInfo bClass = scanResult.getClassInfo(B.class.getName());
MethodInfo aMethodInfo = bClass.getMethodInfo("a").get(0);
MethodTypeSignature aType = aMethodInfo.getTypeSignatureOrTypeDescriptor();
TypeSignature aResultType = aType.getResultType();
ClassRefTypeSignature aResultTypeConcrete = (ClassRefTypeSignature) aResultType;
String aTypeBaseClassName = aResultTypeConcrete.getBaseClassName();
List<TypeArgument> aTypeArgs = aResultTypeConcrete.getTypeArguments();
TypeArgument aTypeArg0 = aTypeArgs.get(0);
String aTypeArg0BaseClassName =
((ClassRefTypeSignature) aTypeArg0.getTypeSignature()).getBaseClassName();
System.out.println("Method a() returns type " + aTypeBaseClassName
+ " with argument " + aTypeArg0BaseClassName);
}
}
}This prints:
Method a() returns type TestReadingTypeArgs$A with argument java.lang.Integer
ClassGraph does not substitute type arguments into type parameters automatically, so you may get a TypeVariableSignature for the type signature of a field, method, method parameter, etc., where you were expecting a concrete type. This is not an omission: a type variable has no single concrete value. Given
interface Base<T> {
T getT();
}
class DerivedA implements Base<String> { public String getT() { return ""; } }
class DerivedB implements Base<Integer> { public Integer getT() { return 0; } }the result type of Base#getT() is genuinely just T. It is String only when viewed through DerivedA, and Integer only when viewed through DerivedB.
Use this method to substitute the type arguments that a particular subtype supplies:
ClassInfo base = scanResult.getClassInfo("Base");
ClassInfo derivedA = scanResult.getClassInfo("DerivedA");
TypeSignature resultType = base.getMethodInfo("getT").get(0)
.getTypeSignatureOrTypeDescriptor().getResultType();
resultType.toString(); // "T"
resultType.resolveTypeVariables(derivedA).toString(); // "java.lang.String"contextClass is the subtype you are viewing the declaring class through -- it is the answer to "what type does this method return when called on a DerivedA?". It is not an enclosing class, and it is required rather than inferred because a MethodInfo or FieldInfo belongs to the class that declares it and is shared by every subclass that inherits it, so it does not itself record which subtype you reached it through.
Because the method is defined on TypeSignature, it applies equally to method result types, method parameter types and field types:
// Method parameter type
methodInfo.getParameterInfo().get(0).getTypeSignatureOrTypeDescriptor()
.resolveTypeVariables(contextClass);
// Field type
fieldInfo.getTypeSignatureOrTypeDescriptor().resolveTypeVariables(contextClass);Resolution walks up the superclass and superinterface chain of the context class, composing the type arguments each level supplies for the level above it, so bindings passed through intermediate classes are resolved too:
class Mid<U> implements Base<U> { }
class Derived extends Mid<Integer> { } // Base's T resolves to IntegerType variables are substituted inside type arguments at any depth (Map<A, List<B>>) and inside array element types (T[][]). The original type signature is never modified -- a new one is returned, or the same object if nothing was substituted.
A type variable is deliberately left unchanged when it cannot be resolved:
TypeVariableSignature#resolve() resolves a type variable to its declaration (its TypeParameter), by looking first at the containing method and then at the containing class. That gives you the variable's bounds, not a substituted type argument.
If you are willing to load the classes, you can instead use the reflection API together with a library like gentyref / GeAnTyRef, which can find the concrete and generic type signature of a loaded class.
A type signature for a generic class, returned by ClassInfo#getTypeSignature().
A type signature for a method, returned by MethodInfo#getTypeSignature() or MethodInfo#getTypeSignatureOrTypeDescriptor().
A type signature or type descriptor, representing the type of a field, the return type of a method, the type of a method parameter, etc. Subclasses are ReferenceTypeSignature and BaseTypeSignature.
All subclasses of TypeSignature include the following methods:
The following two methods are available on every type signature class on this page, including ClassTypeSignature, MethodTypeSignature, TypeArgument and TypeParameter, which are not subclasses of TypeSignature:
A type signature for a base type (a primitive type or void).
A type signature for a reference type. Subclasses are ClassRefOrTypeVariableSignature and ArrayTypeSignature.
A type signature for an array type.
A type signature for a class reference or a type variable. Subclasses are ClassRefTypeSignature and TypeVariableSignature.
A type signature for a Class reference.
A type signature for a type variable.
A (possibly-wildcarded) generic type argument.
A generic type parameter.
| Back | FazBrowse Home | New Git URL |