CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-github-classgraph--classgraph

The uber-fast, ultra-lightweight classpath and module scanner for JVM languages.

Pending
Overview
Eval results
Files

type-signatures.mddocs/

Type Signatures and Generics

ClassGraph provides comprehensive support for Java's type system, including full generic type information, type parameters, wildcards, and complex nested generic types. This system allows precise analysis of generic types without requiring class loading.

Type Signature Hierarchy

import io.github.classgraph.TypeSignature;
import io.github.classgraph.BaseTypeSignature;
import io.github.classgraph.ClassRefTypeSignature;
import io.github.classgraph.ArrayTypeSignature;
import io.github.classgraph.TypeVariableSignature;
import io.github.classgraph.ClassTypeSignature;
import io.github.classgraph.MethodTypeSignature;
import io.github.classgraph.TypeParameter;
import io.github.classgraph.TypeArgument;
import java.util.List;

TypeSignature - Base Class

All type signatures extend from the abstract TypeSignature base class:

TypeSignature typeSignature = fieldInfo.getTypeSignature();

// Type annotations (Java 8+)
AnnotationInfoList typeAnnotations = typeSignature.getTypeAnnotationInfo();

// Equality comparison ignoring type parameters
boolean equalIgnoringGenerics = typeSignature.equalsIgnoringTypeParams(otherSignature);

// String representation
String typeString = typeSignature.toString();

Primitive Types - BaseTypeSignature

Represents primitive Java types (int, long, boolean, etc.):

BaseTypeSignature primitiveType = (BaseTypeSignature) fieldInfo.getTypeSignature();

// Single character type code ('I' for int, 'J' for long, etc.)
char typeChar = primitiveType.getTypeSignatureChar();

// Human-readable type name
String typeName = primitiveType.getTypeStr();  // "int", "long", "boolean"

// Get primitive Class object
Class<?> primitiveClass = primitiveType.getType();  // int.class, long.class, etc.

Primitive Type Examples

// Analyzing primitive fields
FieldInfoList fields = classInfo.getDeclaredFieldInfo();
for (FieldInfo field : fields) {
    TypeSignature type = field.getTypeSignature();
    if (type instanceof BaseTypeSignature) {
        BaseTypeSignature primitive = (BaseTypeSignature) type;
        System.out.println("Field " + field.getName() + " is primitive type: " + primitive.getTypeStr());
    }
}

Class References - ClassRefTypeSignature

Represents references to classes and interfaces, including generic types:

ClassRefTypeSignature classRef = (ClassRefTypeSignature) fieldInfo.getTypeSignature();

// Basic class information
String baseClassName = classRef.getBaseClassName();              // "java.util.List"
String fullyQualifiedName = classRef.getFullyQualifiedClassName(); // "java.util.List"

// Generic type arguments
List<TypeArgument> typeArguments = classRef.getTypeArguments();

// Inner class information
List<String> suffixes = classRef.getSuffixes();                          // ["Inner", "Nested"]
List<List<TypeArgument>> suffixTypeArguments = classRef.getSuffixTypeArguments();

// Load referenced class
Class<?> referencedClass = classRef.loadClass(false);  // Don't initialize
Class<?> referencedClassThrow = classRef.loadClass();  // Throw on error

// Get ClassInfo for referenced class
ClassInfo referencedClassInfo = classRef.getClassInfo();

Generic Type Analysis

// Analyzing generic field: List<String>
FieldInfo listField = classInfo.getDeclaredFieldInfo("items");
if (listField.getTypeSignature() instanceof ClassRefTypeSignature) {
    ClassRefTypeSignature listType = (ClassRefTypeSignature) listField.getTypeSignature();
    
    System.out.println("Base type: " + listType.getBaseClassName());  // "java.util.List"
    
    List<TypeArgument> typeArgs = listType.getTypeArguments();
    if (!typeArgs.isEmpty()) {
        TypeArgument firstArg = typeArgs.get(0);
        System.out.println("First type argument: " + firstArg.getTypeSignature());  // "java.lang.String"
    }
}

// Complex generic type: Map<String, List<User>>
FieldInfo mapField = classInfo.getDeclaredFieldInfo("userGroups");
ClassRefTypeSignature mapType = (ClassRefTypeSignature) mapField.getTypeSignature();

List<TypeArgument> mapArgs = mapType.getTypeArguments();
if (mapArgs.size() == 2) {
    TypeSignature keyType = mapArgs.get(0).getTypeSignature();        // String
    TypeSignature valueType = mapArgs.get(1).getTypeSignature();      // List<User>
    
    if (valueType instanceof ClassRefTypeSignature) {
        ClassRefTypeSignature listValueType = (ClassRefTypeSignature) valueType;
        List<TypeArgument> listArgs = listValueType.getTypeArguments();
        if (!listArgs.isEmpty()) {
            TypeSignature userType = listArgs.get(0).getTypeSignature();  // User
            System.out.println("User type: " + userType);
        }
    }
}

Array Types - ArrayTypeSignature

Represents array types with full dimension information:

ArrayTypeSignature arrayType = (ArrayTypeSignature) fieldInfo.getTypeSignature();

// Full array type signature string
String arraySignature = arrayType.getTypeSignatureStr();    // "[Ljava/lang/String;"

// Element type information
TypeSignature elementType = arrayType.getElementTypeSignature();  // String
int dimensions = arrayType.getNumDimensions();                    // 1 for String[], 2 for String[][]

// Nested type for multi-dimensional arrays
TypeSignature nestedType = arrayType.getNestedType();

// Array class information
ArrayClassInfo arrayClassInfo = arrayType.getArrayClassInfo();

// Load element and array classes
Class<?> elementClass = arrayType.loadElementClass(false);
Class<?> elementClassThrow = arrayType.loadElementClass();
Class<?> arrayClass = arrayType.loadClass(false);
Class<?> arrayClassThrow = arrayType.loadClass();

Array Type Analysis

// Analyzing various array types
FieldInfoList fields = classInfo.getDeclaredFieldInfo();
for (FieldInfo field : fields) {
    TypeSignature type = field.getTypeSignature();
    if (type instanceof ArrayTypeSignature) {
        ArrayTypeSignature arrayType = (ArrayTypeSignature) type;
        
        System.out.println("Field: " + field.getName());
        System.out.println("  Dimensions: " + arrayType.getNumDimensions());
        System.out.println("  Element type: " + arrayType.getElementTypeSignature());
        System.out.println("  Full signature: " + arrayType.getTypeSignatureStr());
    }
}

// Example outputs:
// Field: names
//   Dimensions: 1
//   Element type: java.lang.String
//   Full signature: [Ljava/lang/String;

// Field: matrix  
//   Dimensions: 2
//   Element type: int
//   Full signature: [[I

Type Variables - TypeVariableSignature

Represents generic type variables (T, E, K, V, etc.):

TypeVariableSignature typeVar = (TypeVariableSignature) signature;

// Type variable name
String name = typeVar.getName();  // "T", "E", "K", "V", etc.

// Resolve to type parameter definition
TypeParameter typeParam = typeVar.resolve();

// String representation with bounds
String withBounds = typeVar.toStringWithTypeBound();  // "T extends Number"

Type Variable Examples

// Analyzing generic class: class Container<T extends Number>
ClassTypeSignature classSignature = classInfo.getTypeSignature();
if (classSignature != null) {
    List<TypeParameter> typeParams = classSignature.getTypeParameters();
    for (TypeParameter param : typeParams) {
        String paramName = param.getName();                        // "T"
        ReferenceTypeSignature classBound = param.getClassBound(); // Number
        List<ReferenceTypeSignature> interfaceBounds = param.getInterfaceBounds();
        
        System.out.println("Type parameter: " + paramName);
        if (classBound != null) {
            System.out.println("  Class bound: " + classBound);
        }
        for (ReferenceTypeSignature bound : interfaceBounds) {
            System.out.println("  Interface bound: " + bound);
        }
    }
}

// Analyzing generic method: <T extends Comparable<T>> T max(T a, T b)
MethodInfo genericMethod = classInfo.getDeclaredMethodInfo("max").get(0);
MethodTypeSignature methodSignature = genericMethod.getTypeSignature();
if (methodSignature != null) {
    List<TypeParameter> methodTypeParams = methodSignature.getTypeParameters();
    TypeSignature returnType = methodSignature.getResultType();
    List<TypeSignature> paramTypes = methodSignature.getParameterTypeSignatures();
}

Class Type Signatures - ClassTypeSignature

Represents complete class signatures including generics:

ClassTypeSignature classSignature = classInfo.getTypeSignature();

if (classSignature != null) {
    // Generic type parameters of the class
    List<TypeParameter> typeParameters = classSignature.getTypeParameters();
    
    // Superclass with generic information
    ClassRefTypeSignature superclassSignature = classSignature.getSuperclassSignature();
    
    // Implemented interfaces with generic information
    List<ClassRefTypeSignature> superinterfaceSignatures = classSignature.getSuperinterfaceSignatures();
}

Class Signature Analysis

// Analyzing: class MyList<T extends Number> extends AbstractList<T> implements Serializable
ClassTypeSignature signature = classInfo.getTypeSignature();

// Type parameters: <T extends Number>
List<TypeParameter> typeParams = signature.getTypeParameters();
if (!typeParams.isEmpty()) {
    TypeParameter tParam = typeParams.get(0);
    System.out.println("Type parameter: " + tParam.getName());                    // "T"
    System.out.println("Class bound: " + tParam.getClassBound());                // Number
}

// Superclass: AbstractList<T>
ClassRefTypeSignature superclass = signature.getSuperclassSignature();
if (superclass != null) {
    System.out.println("Superclass: " + superclass.getBaseClassName());         // AbstractList
    List<TypeArgument> superArgs = superclass.getTypeArguments();
    if (!superArgs.isEmpty()) {
        System.out.println("Super type arg: " + superArgs.get(0).getTypeSignature()); // T
    }
}

// Interfaces: Serializable
List<ClassRefTypeSignature> interfaces = signature.getSuperinterfaceSignatures();
for (ClassRefTypeSignature iface : interfaces) {
    System.out.println("Interface: " + iface.getBaseClassName());               // Serializable
}

Method Type Signatures - MethodTypeSignature

Represents complete method signatures including generics:

MethodTypeSignature methodSignature = methodInfo.getTypeSignature();

if (methodSignature != null) {
    // Method-level type parameters
    List<TypeParameter> typeParameters = methodSignature.getTypeParameters();
    
    // Parameter types
    List<TypeSignature> parameterTypes = methodSignature.getParameterTypeSignatures();
    
    // Return type
    TypeSignature returnType = methodSignature.getResultType();
    
    // Thrown exceptions
    List<ClassRefOrTypeVariableSignature> thrownSignatures = methodSignature.getThrowsSignatures();
    
    // Receiver type annotations (for inner class methods)
    AnnotationInfoList receiverAnnotations = methodSignature.getReceiverTypeAnnotationInfo();
}

Method Signature Analysis

// Analyzing: public <T extends Comparable<T>> List<T> sort(Collection<? extends T> items) throws IllegalArgumentException
MethodInfo sortMethod = classInfo.getDeclaredMethodInfo("sort").get(0);
MethodTypeSignature methodSig = sortMethod.getTypeSignature();

// Method type parameters: <T extends Comparable<T>>
List<TypeParameter> methodTypeParams = methodSig.getTypeParameters();
if (!methodTypeParams.isEmpty()) {
    TypeParameter tParam = methodTypeParams.get(0);
    System.out.println("Method type parameter: " + tParam.getName());           // "T"
    
    List<ReferenceTypeSignature> bounds = tParam.getInterfaceBounds();
    for (ReferenceTypeSignature bound : bounds) {
        System.out.println("  Bound: " + bound);                                // Comparable<T>
    }
}

// Return type: List<T>
TypeSignature returnType = methodSig.getResultType();
System.out.println("Return type: " + returnType);

// Parameter types: Collection<? extends T>
List<TypeSignature> paramTypes = methodSig.getParameterTypeSignatures();
for (TypeSignature paramType : paramTypes) {
    System.out.println("Parameter type: " + paramType);
}

// Thrown exceptions: IllegalArgumentException
List<ClassRefOrTypeVariableSignature> thrownTypes = methodSig.getThrowsSignatures();
for (ClassRefOrTypeVariableSignature thrown : thrownTypes) {
    System.out.println("Throws: " + thrown);
}

Type Arguments and Wildcards

TypeArgument Analysis

// Analyzing generic types with wildcards: List<? extends Number>
ClassRefTypeSignature listType = (ClassRefTypeSignature) fieldInfo.getTypeSignature();
List<TypeArgument> typeArgs = listType.getTypeArguments();

for (TypeArgument arg : typeArgs) {
    // Wildcard information
    Wildcard wildcard = arg.getWildcard();  // NONE, ANY, EXTENDS, or SUPER
    
    // Actual type signature  
    ReferenceTypeSignature typeSignature = arg.getTypeSignature();
    
    switch (wildcard) {
        case NONE:
            System.out.println("Concrete type: " + typeSignature);              // List<String>
            break;
        case ANY:
            System.out.println("Unbounded wildcard: ?");                        // List<?>
            break;  
        case EXTENDS:
            System.out.println("Upper bounded: ? extends " + typeSignature);    // List<? extends Number>
            break;
        case SUPER:
            System.out.println("Lower bounded: ? super " + typeSignature);      // List<? super Integer>
            break;
    }
}

Type Parameter Definitions

TypeParameter Details

// Analyzing bounded type parameters: <T extends Number & Comparable<T>>
ClassTypeSignature classSignature = classInfo.getTypeSignature();
List<TypeParameter> typeParams = classSignature.getTypeParameters();

for (TypeParameter param : typeParams) {
    String name = param.getName();                                              // "T"
    
    // Class bound (extends clause)
    ReferenceTypeSignature classBound = param.getClassBound();                 // Number
    
    // Interface bounds (multiple interfaces after &)
    List<ReferenceTypeSignature> interfaceBounds = param.getInterfaceBounds();  // [Comparable<T>]
    
    System.out.println("Type parameter: " + name);
    if (classBound != null) {
        System.out.println("  Class bound: " + classBound);
    }
    for (ReferenceTypeSignature bound : interfaceBounds) {
        System.out.println("  Interface bound: " + bound);
    }
}

Complex Generic Type Examples

Nested Generic Types

// Analyzing complex nested generics: Map<String, List<Map<Integer, Set<User>>>>
FieldInfo complexField = classInfo.getDeclaredFieldInfo("complexData");
ClassRefTypeSignature mapType = (ClassRefTypeSignature) complexField.getTypeSignature();

System.out.println("Base type: " + mapType.getBaseClassName());                // Map

List<TypeArgument> mapArgs = mapType.getTypeArguments();
TypeSignature keyType = mapArgs.get(0).getTypeSignature();                     // String
TypeSignature valueType = mapArgs.get(1).getTypeSignature();                   // List<Map<Integer, Set<User>>>

if (valueType instanceof ClassRefTypeSignature) {
    ClassRefTypeSignature listType = (ClassRefTypeSignature) valueType;
    System.out.println("Value list type: " + listType.getBaseClassName());     // List
    
    List<TypeArgument> listArgs = listType.getTypeArguments();
    TypeSignature listElementType = listArgs.get(0).getTypeSignature();        // Map<Integer, Set<User>>
    
    if (listElementType instanceof ClassRefTypeSignature) {
        ClassRefTypeSignature innerMapType = (ClassRefTypeSignature) listElementType;
        System.out.println("Inner map type: " + innerMapType.getBaseClassName()); // Map
        
        List<TypeArgument> innerMapArgs = innerMapType.getTypeArguments();
        TypeSignature innerKeyType = innerMapArgs.get(0).getTypeSignature();   // Integer
        TypeSignature innerValueType = innerMapArgs.get(1).getTypeSignature(); // Set<User>
        
        // Continue drilling down...
    }
}

Generic Method with Multiple Bounds

// Analyzing: <T extends Number & Comparable<T> & Serializable> T process(T input)
MethodInfo processMethod = classInfo.getDeclaredMethodInfo("process").get(0);
MethodTypeSignature methodSig = processMethod.getTypeSignature();

List<TypeParameter> typeParams = methodSig.getTypeParameters();
TypeParameter tParam = typeParams.get(0);

System.out.println("Type parameter: " + tParam.getName());                     // "T"

// Class bound
ReferenceTypeSignature classBound = tParam.getClassBound();
if (classBound != null) {
    System.out.println("Class bound: " + classBound);                          // Number
}

// Interface bounds
List<ReferenceTypeSignature> interfaceBounds = tParam.getInterfaceBounds();
for (ReferenceTypeSignature bound : interfaceBounds) {
    System.out.println("Interface bound: " + bound);                           // Comparable<T>, Serializable
}

// Return type (should be T)
TypeSignature returnType = methodSig.getResultType();
if (returnType instanceof TypeVariableSignature) {
    TypeVariableSignature returnTypeVar = (TypeVariableSignature) returnType;
    System.out.println("Return type variable: " + returnTypeVar.getName());    // "T"
}

Practical Applications

Generic Repository Pattern Analysis

// Find all repository classes and analyze their generic types
ClassInfoList repositories = scanResult.getClassesImplementing("com.example.Repository");

for (ClassInfo repo : repositories) {
    ClassTypeSignature repoSignature = repo.getTypeSignature();
    if (repoSignature != null) {
        // Get implemented interfaces to find Repository<EntityType, IdType>
        List<ClassRefTypeSignature> interfaces = repoSignature.getSuperinterfaceSignatures();
        
        for (ClassRefTypeSignature iface : interfaces) {
            if ("com.example.Repository".equals(iface.getBaseClassName())) {
                List<TypeArgument> args = iface.getTypeArguments();
                if (args.size() == 2) {
                    TypeSignature entityType = args.get(0).getTypeSignature();
                    TypeSignature idType = args.get(1).getTypeSignature();
                    
                    System.out.println("Repository: " + repo.getName());
                    System.out.println("  Entity type: " + entityType);
                    System.out.println("  ID type: " + idType);
                }
            }
        }
    }
}

Generic Service Method Analysis

// Analyze service methods for generic return types
ClassInfoList services = scanResult.getClassesWithAnnotation("org.springframework.stereotype.Service");

for (ClassInfo service : services) {
    MethodInfoList publicMethods = service.getDeclaredMethodInfo()
        .filter(method -> method.isPublic() && !method.isStatic());
        
    for (MethodInfo method : publicMethods) {
        MethodTypeSignature methodSig = method.getTypeSignature();
        if (methodSig != null) {
            TypeSignature returnType = methodSig.getResultType();
            
            // Look for generic collection return types
            if (returnType instanceof ClassRefTypeSignature) {
                ClassRefTypeSignature classReturn = (ClassRefTypeSignature) returnType;
                String returnClassName = classReturn.getBaseClassName();
                
                if (returnClassName.equals("java.util.List") || 
                    returnClassName.equals("java.util.Set") ||
                    returnClassName.equals("java.util.Optional")) {
                    
                    List<TypeArgument> typeArgs = classReturn.getTypeArguments();
                    if (!typeArgs.isEmpty()) {
                        TypeSignature elementType = typeArgs.get(0).getTypeSignature();
                        System.out.println(service.getName() + "." + method.getName() + 
                                         " returns " + returnClassName + "<" + elementType + ">");
                    }
                }
            }
        }
    }
}

The type signature system in ClassGraph provides complete access to Java's complex generic type system, enabling sophisticated analysis of generic types, bounds, and relationships without requiring class loading or runtime reflection.

Install with Tessl CLI

npx tessl i tessl/maven-io-github-classgraph--classgraph

docs

class-info.md

index.md

querying.md

resources.md

scanning.md

type-signatures.md

tile.json