0
# Type Signatures and Generics
1
2
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.
3
4
## Type Signature Hierarchy
5
6
```java { .api }
7
import io.github.classgraph.TypeSignature;
8
import io.github.classgraph.BaseTypeSignature;
9
import io.github.classgraph.ClassRefTypeSignature;
10
import io.github.classgraph.ArrayTypeSignature;
11
import io.github.classgraph.TypeVariableSignature;
12
import io.github.classgraph.ClassTypeSignature;
13
import io.github.classgraph.MethodTypeSignature;
14
import io.github.classgraph.TypeParameter;
15
import io.github.classgraph.TypeArgument;
16
import java.util.List;
17
```
18
19
### TypeSignature - Base Class
20
21
All type signatures extend from the abstract TypeSignature base class:
22
23
```java { .api }
24
TypeSignature typeSignature = fieldInfo.getTypeSignature();
25
26
// Type annotations (Java 8+)
27
AnnotationInfoList typeAnnotations = typeSignature.getTypeAnnotationInfo();
28
29
// Equality comparison ignoring type parameters
30
boolean equalIgnoringGenerics = typeSignature.equalsIgnoringTypeParams(otherSignature);
31
32
// String representation
33
String typeString = typeSignature.toString();
34
```
35
36
## Primitive Types - BaseTypeSignature
37
38
Represents primitive Java types (int, long, boolean, etc.):
39
40
```java { .api }
41
BaseTypeSignature primitiveType = (BaseTypeSignature) fieldInfo.getTypeSignature();
42
43
// Single character type code ('I' for int, 'J' for long, etc.)
44
char typeChar = primitiveType.getTypeSignatureChar();
45
46
// Human-readable type name
47
String typeName = primitiveType.getTypeStr(); // "int", "long", "boolean"
48
49
// Get primitive Class object
50
Class<?> primitiveClass = primitiveType.getType(); // int.class, long.class, etc.
51
```
52
53
### Primitive Type Examples
54
55
```java { .api }
56
// Analyzing primitive fields
57
FieldInfoList fields = classInfo.getDeclaredFieldInfo();
58
for (FieldInfo field : fields) {
59
TypeSignature type = field.getTypeSignature();
60
if (type instanceof BaseTypeSignature) {
61
BaseTypeSignature primitive = (BaseTypeSignature) type;
62
System.out.println("Field " + field.getName() + " is primitive type: " + primitive.getTypeStr());
63
}
64
}
65
```
66
67
## Class References - ClassRefTypeSignature
68
69
Represents references to classes and interfaces, including generic types:
70
71
```java { .api }
72
ClassRefTypeSignature classRef = (ClassRefTypeSignature) fieldInfo.getTypeSignature();
73
74
// Basic class information
75
String baseClassName = classRef.getBaseClassName(); // "java.util.List"
76
String fullyQualifiedName = classRef.getFullyQualifiedClassName(); // "java.util.List"
77
78
// Generic type arguments
79
List<TypeArgument> typeArguments = classRef.getTypeArguments();
80
81
// Inner class information
82
List<String> suffixes = classRef.getSuffixes(); // ["Inner", "Nested"]
83
List<List<TypeArgument>> suffixTypeArguments = classRef.getSuffixTypeArguments();
84
85
// Load referenced class
86
Class<?> referencedClass = classRef.loadClass(false); // Don't initialize
87
Class<?> referencedClassThrow = classRef.loadClass(); // Throw on error
88
89
// Get ClassInfo for referenced class
90
ClassInfo referencedClassInfo = classRef.getClassInfo();
91
```
92
93
### Generic Type Analysis
94
95
```java { .api }
96
// Analyzing generic field: List<String>
97
FieldInfo listField = classInfo.getDeclaredFieldInfo("items");
98
if (listField.getTypeSignature() instanceof ClassRefTypeSignature) {
99
ClassRefTypeSignature listType = (ClassRefTypeSignature) listField.getTypeSignature();
100
101
System.out.println("Base type: " + listType.getBaseClassName()); // "java.util.List"
102
103
List<TypeArgument> typeArgs = listType.getTypeArguments();
104
if (!typeArgs.isEmpty()) {
105
TypeArgument firstArg = typeArgs.get(0);
106
System.out.println("First type argument: " + firstArg.getTypeSignature()); // "java.lang.String"
107
}
108
}
109
110
// Complex generic type: Map<String, List<User>>
111
FieldInfo mapField = classInfo.getDeclaredFieldInfo("userGroups");
112
ClassRefTypeSignature mapType = (ClassRefTypeSignature) mapField.getTypeSignature();
113
114
List<TypeArgument> mapArgs = mapType.getTypeArguments();
115
if (mapArgs.size() == 2) {
116
TypeSignature keyType = mapArgs.get(0).getTypeSignature(); // String
117
TypeSignature valueType = mapArgs.get(1).getTypeSignature(); // List<User>
118
119
if (valueType instanceof ClassRefTypeSignature) {
120
ClassRefTypeSignature listValueType = (ClassRefTypeSignature) valueType;
121
List<TypeArgument> listArgs = listValueType.getTypeArguments();
122
if (!listArgs.isEmpty()) {
123
TypeSignature userType = listArgs.get(0).getTypeSignature(); // User
124
System.out.println("User type: " + userType);
125
}
126
}
127
}
128
```
129
130
## Array Types - ArrayTypeSignature
131
132
Represents array types with full dimension information:
133
134
```java { .api }
135
ArrayTypeSignature arrayType = (ArrayTypeSignature) fieldInfo.getTypeSignature();
136
137
// Full array type signature string
138
String arraySignature = arrayType.getTypeSignatureStr(); // "[Ljava/lang/String;"
139
140
// Element type information
141
TypeSignature elementType = arrayType.getElementTypeSignature(); // String
142
int dimensions = arrayType.getNumDimensions(); // 1 for String[], 2 for String[][]
143
144
// Nested type for multi-dimensional arrays
145
TypeSignature nestedType = arrayType.getNestedType();
146
147
// Array class information
148
ArrayClassInfo arrayClassInfo = arrayType.getArrayClassInfo();
149
150
// Load element and array classes
151
Class<?> elementClass = arrayType.loadElementClass(false);
152
Class<?> elementClassThrow = arrayType.loadElementClass();
153
Class<?> arrayClass = arrayType.loadClass(false);
154
Class<?> arrayClassThrow = arrayType.loadClass();
155
```
156
157
### Array Type Analysis
158
159
```java { .api }
160
// Analyzing various array types
161
FieldInfoList fields = classInfo.getDeclaredFieldInfo();
162
for (FieldInfo field : fields) {
163
TypeSignature type = field.getTypeSignature();
164
if (type instanceof ArrayTypeSignature) {
165
ArrayTypeSignature arrayType = (ArrayTypeSignature) type;
166
167
System.out.println("Field: " + field.getName());
168
System.out.println(" Dimensions: " + arrayType.getNumDimensions());
169
System.out.println(" Element type: " + arrayType.getElementTypeSignature());
170
System.out.println(" Full signature: " + arrayType.getTypeSignatureStr());
171
}
172
}
173
174
// Example outputs:
175
// Field: names
176
// Dimensions: 1
177
// Element type: java.lang.String
178
// Full signature: [Ljava/lang/String;
179
180
// Field: matrix
181
// Dimensions: 2
182
// Element type: int
183
// Full signature: [[I
184
```
185
186
## Type Variables - TypeVariableSignature
187
188
Represents generic type variables (T, E, K, V, etc.):
189
190
```java { .api }
191
TypeVariableSignature typeVar = (TypeVariableSignature) signature;
192
193
// Type variable name
194
String name = typeVar.getName(); // "T", "E", "K", "V", etc.
195
196
// Resolve to type parameter definition
197
TypeParameter typeParam = typeVar.resolve();
198
199
// String representation with bounds
200
String withBounds = typeVar.toStringWithTypeBound(); // "T extends Number"
201
```
202
203
### Type Variable Examples
204
205
```java { .api }
206
// Analyzing generic class: class Container<T extends Number>
207
ClassTypeSignature classSignature = classInfo.getTypeSignature();
208
if (classSignature != null) {
209
List<TypeParameter> typeParams = classSignature.getTypeParameters();
210
for (TypeParameter param : typeParams) {
211
String paramName = param.getName(); // "T"
212
ReferenceTypeSignature classBound = param.getClassBound(); // Number
213
List<ReferenceTypeSignature> interfaceBounds = param.getInterfaceBounds();
214
215
System.out.println("Type parameter: " + paramName);
216
if (classBound != null) {
217
System.out.println(" Class bound: " + classBound);
218
}
219
for (ReferenceTypeSignature bound : interfaceBounds) {
220
System.out.println(" Interface bound: " + bound);
221
}
222
}
223
}
224
225
// Analyzing generic method: <T extends Comparable<T>> T max(T a, T b)
226
MethodInfo genericMethod = classInfo.getDeclaredMethodInfo("max").get(0);
227
MethodTypeSignature methodSignature = genericMethod.getTypeSignature();
228
if (methodSignature != null) {
229
List<TypeParameter> methodTypeParams = methodSignature.getTypeParameters();
230
TypeSignature returnType = methodSignature.getResultType();
231
List<TypeSignature> paramTypes = methodSignature.getParameterTypeSignatures();
232
}
233
```
234
235
## Class Type Signatures - ClassTypeSignature
236
237
Represents complete class signatures including generics:
238
239
```java { .api }
240
ClassTypeSignature classSignature = classInfo.getTypeSignature();
241
242
if (classSignature != null) {
243
// Generic type parameters of the class
244
List<TypeParameter> typeParameters = classSignature.getTypeParameters();
245
246
// Superclass with generic information
247
ClassRefTypeSignature superclassSignature = classSignature.getSuperclassSignature();
248
249
// Implemented interfaces with generic information
250
List<ClassRefTypeSignature> superinterfaceSignatures = classSignature.getSuperinterfaceSignatures();
251
}
252
```
253
254
### Class Signature Analysis
255
256
```java { .api }
257
// Analyzing: class MyList<T extends Number> extends AbstractList<T> implements Serializable
258
ClassTypeSignature signature = classInfo.getTypeSignature();
259
260
// Type parameters: <T extends Number>
261
List<TypeParameter> typeParams = signature.getTypeParameters();
262
if (!typeParams.isEmpty()) {
263
TypeParameter tParam = typeParams.get(0);
264
System.out.println("Type parameter: " + tParam.getName()); // "T"
265
System.out.println("Class bound: " + tParam.getClassBound()); // Number
266
}
267
268
// Superclass: AbstractList<T>
269
ClassRefTypeSignature superclass = signature.getSuperclassSignature();
270
if (superclass != null) {
271
System.out.println("Superclass: " + superclass.getBaseClassName()); // AbstractList
272
List<TypeArgument> superArgs = superclass.getTypeArguments();
273
if (!superArgs.isEmpty()) {
274
System.out.println("Super type arg: " + superArgs.get(0).getTypeSignature()); // T
275
}
276
}
277
278
// Interfaces: Serializable
279
List<ClassRefTypeSignature> interfaces = signature.getSuperinterfaceSignatures();
280
for (ClassRefTypeSignature iface : interfaces) {
281
System.out.println("Interface: " + iface.getBaseClassName()); // Serializable
282
}
283
```
284
285
## Method Type Signatures - MethodTypeSignature
286
287
Represents complete method signatures including generics:
288
289
```java { .api }
290
MethodTypeSignature methodSignature = methodInfo.getTypeSignature();
291
292
if (methodSignature != null) {
293
// Method-level type parameters
294
List<TypeParameter> typeParameters = methodSignature.getTypeParameters();
295
296
// Parameter types
297
List<TypeSignature> parameterTypes = methodSignature.getParameterTypeSignatures();
298
299
// Return type
300
TypeSignature returnType = methodSignature.getResultType();
301
302
// Thrown exceptions
303
List<ClassRefOrTypeVariableSignature> thrownSignatures = methodSignature.getThrowsSignatures();
304
305
// Receiver type annotations (for inner class methods)
306
AnnotationInfoList receiverAnnotations = methodSignature.getReceiverTypeAnnotationInfo();
307
}
308
```
309
310
### Method Signature Analysis
311
312
```java { .api }
313
// Analyzing: public <T extends Comparable<T>> List<T> sort(Collection<? extends T> items) throws IllegalArgumentException
314
MethodInfo sortMethod = classInfo.getDeclaredMethodInfo("sort").get(0);
315
MethodTypeSignature methodSig = sortMethod.getTypeSignature();
316
317
// Method type parameters: <T extends Comparable<T>>
318
List<TypeParameter> methodTypeParams = methodSig.getTypeParameters();
319
if (!methodTypeParams.isEmpty()) {
320
TypeParameter tParam = methodTypeParams.get(0);
321
System.out.println("Method type parameter: " + tParam.getName()); // "T"
322
323
List<ReferenceTypeSignature> bounds = tParam.getInterfaceBounds();
324
for (ReferenceTypeSignature bound : bounds) {
325
System.out.println(" Bound: " + bound); // Comparable<T>
326
}
327
}
328
329
// Return type: List<T>
330
TypeSignature returnType = methodSig.getResultType();
331
System.out.println("Return type: " + returnType);
332
333
// Parameter types: Collection<? extends T>
334
List<TypeSignature> paramTypes = methodSig.getParameterTypeSignatures();
335
for (TypeSignature paramType : paramTypes) {
336
System.out.println("Parameter type: " + paramType);
337
}
338
339
// Thrown exceptions: IllegalArgumentException
340
List<ClassRefOrTypeVariableSignature> thrownTypes = methodSig.getThrowsSignatures();
341
for (ClassRefOrTypeVariableSignature thrown : thrownTypes) {
342
System.out.println("Throws: " + thrown);
343
}
344
```
345
346
## Type Arguments and Wildcards
347
348
### TypeArgument Analysis
349
350
```java { .api }
351
// Analyzing generic types with wildcards: List<? extends Number>
352
ClassRefTypeSignature listType = (ClassRefTypeSignature) fieldInfo.getTypeSignature();
353
List<TypeArgument> typeArgs = listType.getTypeArguments();
354
355
for (TypeArgument arg : typeArgs) {
356
// Wildcard information
357
Wildcard wildcard = arg.getWildcard(); // NONE, ANY, EXTENDS, or SUPER
358
359
// Actual type signature
360
ReferenceTypeSignature typeSignature = arg.getTypeSignature();
361
362
switch (wildcard) {
363
case NONE:
364
System.out.println("Concrete type: " + typeSignature); // List<String>
365
break;
366
case ANY:
367
System.out.println("Unbounded wildcard: ?"); // List<?>
368
break;
369
case EXTENDS:
370
System.out.println("Upper bounded: ? extends " + typeSignature); // List<? extends Number>
371
break;
372
case SUPER:
373
System.out.println("Lower bounded: ? super " + typeSignature); // List<? super Integer>
374
break;
375
}
376
}
377
```
378
379
## Type Parameter Definitions
380
381
### TypeParameter Details
382
383
```java { .api }
384
// Analyzing bounded type parameters: <T extends Number & Comparable<T>>
385
ClassTypeSignature classSignature = classInfo.getTypeSignature();
386
List<TypeParameter> typeParams = classSignature.getTypeParameters();
387
388
for (TypeParameter param : typeParams) {
389
String name = param.getName(); // "T"
390
391
// Class bound (extends clause)
392
ReferenceTypeSignature classBound = param.getClassBound(); // Number
393
394
// Interface bounds (multiple interfaces after &)
395
List<ReferenceTypeSignature> interfaceBounds = param.getInterfaceBounds(); // [Comparable<T>]
396
397
System.out.println("Type parameter: " + name);
398
if (classBound != null) {
399
System.out.println(" Class bound: " + classBound);
400
}
401
for (ReferenceTypeSignature bound : interfaceBounds) {
402
System.out.println(" Interface bound: " + bound);
403
}
404
}
405
```
406
407
## Complex Generic Type Examples
408
409
### Nested Generic Types
410
411
```java { .api }
412
// Analyzing complex nested generics: Map<String, List<Map<Integer, Set<User>>>>
413
FieldInfo complexField = classInfo.getDeclaredFieldInfo("complexData");
414
ClassRefTypeSignature mapType = (ClassRefTypeSignature) complexField.getTypeSignature();
415
416
System.out.println("Base type: " + mapType.getBaseClassName()); // Map
417
418
List<TypeArgument> mapArgs = mapType.getTypeArguments();
419
TypeSignature keyType = mapArgs.get(0).getTypeSignature(); // String
420
TypeSignature valueType = mapArgs.get(1).getTypeSignature(); // List<Map<Integer, Set<User>>>
421
422
if (valueType instanceof ClassRefTypeSignature) {
423
ClassRefTypeSignature listType = (ClassRefTypeSignature) valueType;
424
System.out.println("Value list type: " + listType.getBaseClassName()); // List
425
426
List<TypeArgument> listArgs = listType.getTypeArguments();
427
TypeSignature listElementType = listArgs.get(0).getTypeSignature(); // Map<Integer, Set<User>>
428
429
if (listElementType instanceof ClassRefTypeSignature) {
430
ClassRefTypeSignature innerMapType = (ClassRefTypeSignature) listElementType;
431
System.out.println("Inner map type: " + innerMapType.getBaseClassName()); // Map
432
433
List<TypeArgument> innerMapArgs = innerMapType.getTypeArguments();
434
TypeSignature innerKeyType = innerMapArgs.get(0).getTypeSignature(); // Integer
435
TypeSignature innerValueType = innerMapArgs.get(1).getTypeSignature(); // Set<User>
436
437
// Continue drilling down...
438
}
439
}
440
```
441
442
### Generic Method with Multiple Bounds
443
444
```java { .api }
445
// Analyzing: <T extends Number & Comparable<T> & Serializable> T process(T input)
446
MethodInfo processMethod = classInfo.getDeclaredMethodInfo("process").get(0);
447
MethodTypeSignature methodSig = processMethod.getTypeSignature();
448
449
List<TypeParameter> typeParams = methodSig.getTypeParameters();
450
TypeParameter tParam = typeParams.get(0);
451
452
System.out.println("Type parameter: " + tParam.getName()); // "T"
453
454
// Class bound
455
ReferenceTypeSignature classBound = tParam.getClassBound();
456
if (classBound != null) {
457
System.out.println("Class bound: " + classBound); // Number
458
}
459
460
// Interface bounds
461
List<ReferenceTypeSignature> interfaceBounds = tParam.getInterfaceBounds();
462
for (ReferenceTypeSignature bound : interfaceBounds) {
463
System.out.println("Interface bound: " + bound); // Comparable<T>, Serializable
464
}
465
466
// Return type (should be T)
467
TypeSignature returnType = methodSig.getResultType();
468
if (returnType instanceof TypeVariableSignature) {
469
TypeVariableSignature returnTypeVar = (TypeVariableSignature) returnType;
470
System.out.println("Return type variable: " + returnTypeVar.getName()); // "T"
471
}
472
```
473
474
## Practical Applications
475
476
### Generic Repository Pattern Analysis
477
478
```java { .api }
479
// Find all repository classes and analyze their generic types
480
ClassInfoList repositories = scanResult.getClassesImplementing("com.example.Repository");
481
482
for (ClassInfo repo : repositories) {
483
ClassTypeSignature repoSignature = repo.getTypeSignature();
484
if (repoSignature != null) {
485
// Get implemented interfaces to find Repository<EntityType, IdType>
486
List<ClassRefTypeSignature> interfaces = repoSignature.getSuperinterfaceSignatures();
487
488
for (ClassRefTypeSignature iface : interfaces) {
489
if ("com.example.Repository".equals(iface.getBaseClassName())) {
490
List<TypeArgument> args = iface.getTypeArguments();
491
if (args.size() == 2) {
492
TypeSignature entityType = args.get(0).getTypeSignature();
493
TypeSignature idType = args.get(1).getTypeSignature();
494
495
System.out.println("Repository: " + repo.getName());
496
System.out.println(" Entity type: " + entityType);
497
System.out.println(" ID type: " + idType);
498
}
499
}
500
}
501
}
502
}
503
```
504
505
### Generic Service Method Analysis
506
507
```java { .api }
508
// Analyze service methods for generic return types
509
ClassInfoList services = scanResult.getClassesWithAnnotation("org.springframework.stereotype.Service");
510
511
for (ClassInfo service : services) {
512
MethodInfoList publicMethods = service.getDeclaredMethodInfo()
513
.filter(method -> method.isPublic() && !method.isStatic());
514
515
for (MethodInfo method : publicMethods) {
516
MethodTypeSignature methodSig = method.getTypeSignature();
517
if (methodSig != null) {
518
TypeSignature returnType = methodSig.getResultType();
519
520
// Look for generic collection return types
521
if (returnType instanceof ClassRefTypeSignature) {
522
ClassRefTypeSignature classReturn = (ClassRefTypeSignature) returnType;
523
String returnClassName = classReturn.getBaseClassName();
524
525
if (returnClassName.equals("java.util.List") ||
526
returnClassName.equals("java.util.Set") ||
527
returnClassName.equals("java.util.Optional")) {
528
529
List<TypeArgument> typeArgs = classReturn.getTypeArguments();
530
if (!typeArgs.isEmpty()) {
531
TypeSignature elementType = typeArgs.get(0).getTypeSignature();
532
System.out.println(service.getName() + "." + method.getName() +
533
" returns " + returnClassName + "<" + elementType + ">");
534
}
535
}
536
}
537
}
538
}
539
}
540
```
541
542
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.