The CodeGentle naming system provides comprehensive support for representing generic and complex type constructs. This document covers the specialized TypeName implementations for arrays, parameterized types, type variables, and wildcards.
ArrayTypeName represents array types with a component type.
interface ArrayTypeName : TypeName {
val componentType: TypeRef<*>
}// Create array type from component type
val stringArrayType = ArrayTypeName(String::class.ref())
val intArrayType = ArrayTypeName(JavaPrimitiveTypeNames.INT.ref())
// Multi-dimensional arrays
val stringArrayArray = ArrayTypeName(stringArrayType.ref())// Content comparison
fun contentHashCode(): Int
infix fun contentEquals(other: ArrayTypeName): Boolean// Basic array types
val byteArray = ArrayTypeName(JavaPrimitiveTypeNames.BYTE.ref())
val stringArray = ArrayTypeName(JavaClassNames.STRING.ref())
// Multi-dimensional arrays
val intMatrix = ArrayTypeName(ArrayTypeName(JavaPrimitiveTypeNames.INT.ref()).ref())
// Generic component arrays
val listArray = ArrayTypeName(
JavaClassNames.LIST.parameterized(JavaClassNames.STRING.ref()).ref()
)
// Check array properties
println(stringArray.componentType.typeName) // java.lang.StringParameterizedTypeName represents generic types with type arguments (e.g., List<String>, Map<K, V>).
interface ParameterizedTypeName : TypeName, Named {
val enclosingType: ParameterizedTypeName? // For nested generic types
val rawType: ClassName // Base class without generics
val typeArguments: List<TypeRef<*>> // Generic type arguments
val name: String // Same as rawType.name
}// Create nested generic classes
fun nestedClass(name: String): ParameterizedTypeName
fun nestedClass(name: String, typeArguments: List<TypeRef<*>>): ParameterizedTypeName
fun nestedClass(name: String, vararg typeArguments: TypeRef<*>): ParameterizedTypeName// Create parameterized type from raw type and arguments
val listOfString = ParameterizedTypeName(
JavaClassNames.LIST,
JavaClassNames.STRING.ref()
)
val mapOfStringToInt = ParameterizedTypeName(
JavaClassNames.MAP,
JavaClassNames.STRING.ref(),
JavaPrimitiveTypeNames.INT.ref()
)
// From iterable type arguments
val typeArgs = listOf(JavaClassNames.STRING.ref(), JavaClassNames.INTEGER.ref())
val mapType = ParameterizedTypeName(JavaClassNames.MAP, typeArgs)// Using ClassName extension method
val listOfString = JavaClassNames.LIST.parameterized(JavaClassNames.STRING.ref())
val setOfInt = JavaClassNames.SET.parameterized(JavaPrimitiveTypeNames.INT.ref())
// Multiple type arguments
val mapOfStringToList = JavaClassNames.MAP.parameterized(
JavaClassNames.STRING.ref(),
JavaClassNames.LIST.parameterized(JavaClassNames.STRING.ref()).ref()
)// Create nested generic types
val outerGeneric = JavaClassNames.OUTER.parameterized(JavaClassNames.STRING.ref())
val innerGeneric = outerGeneric.nestedClass("Inner", JavaPrimitiveTypeNames.INT.ref())
// Complex nesting
val complexType = outerGeneric
.nestedClass("Middle", JavaClassNames.STRING.ref())
.nestedClass("Deep", JavaPrimitiveTypeNames.LONG.ref())// Common generic types
val arrayList = ClassName("java.util", "ArrayList")
.parameterized(JavaClassNames.STRING.ref())
val hashMap = ClassName("java.util", "HashMap").parameterized(
JavaClassNames.STRING.ref(),
JavaPrimitiveTypeNames.INT.ref()
)
// Nested generics
val listOfLists = JavaClassNames.LIST.parameterized(
JavaClassNames.LIST.parameterized(JavaClassNames.STRING.ref()).ref()
)
// Complex generic types
val functionType = ClassName("java.util.function", "Function").parameterized(
JavaClassNames.STRING.ref(),
JavaPrimitiveTypeNames.INT.ref()
)
// Check properties
println(listOfString.rawType) // java.util.List
println(listOfString.typeArguments.first()) // java.lang.String
println(listOfString.name) // ListTypeVariableName represents type variables used in generic declarations (e.g., T, E, K, V).
interface TypeVariableName : TypeName, Named {
val name: String // Variable name (e.g., "T", "E")
val bounds: List<TypeRef<*>> // Upper bounds constraints
}// Simple type variable without bounds
val t = TypeVariableName("T")
val e = TypeVariableName("E")
// Type variable with single bound
val bounded = TypeVariableName("T", JavaClassNames.STRING.ref())
// Type variable with multiple bounds
val multiBounded = TypeVariableName("T",
JavaClassNames.STRING.ref(),
ClassName("java.io", "Serializable").ref()
)
// From iterable bounds
val bounds = listOf(JavaClassNames.STRING.ref(), JavaClassNames.COMPARABLE.ref())
val complexBounded = TypeVariableName("T", bounds)// Common type variables
val genericT = TypeVariableName("T")
val elementE = TypeVariableName("E")
val keyK = TypeVariableName("K")
val valueV = TypeVariableName("V")
// Bounded type variables
val numberBound = TypeVariableName("N", JavaClassNames.NUMBER.ref())
val comparableBound = TypeVariableName("T", JavaClassNames.COMPARABLE.parameterized(
TypeVariableName("T").ref()
).ref())
// Multiple constraints
val serializable = ClassName("java.io", "Serializable")
val cloneable = ClassName("java.lang", "Cloneable")
val multiConstrained = TypeVariableName("T", serializable.ref(), cloneable.ref())
// Check properties
println(numberBound.name) // N
println(numberBound.bounds.first().typeName) // java.lang.Number
println(multiConstrained.bounds.size) // 2WildcardTypeName represents wildcard types used in Java generics (e.g., ?, ? extends String, ? super Integer).
sealed interface WildcardTypeName : TypeName {
val bounds: List<TypeRef<*>>
val isEmpty: Boolean // True if no bounds
}
// No bounds: `?` in Java, `*` in Kotlin
data object EmptyWildcardTypeName : WildcardTypeName
// Upper bounds: `? extends T` in Java, `out T` in Kotlin
interface UpperWildcardTypeName : WildcardTypeName
// Lower bounds: `? super T` in Java, `in T` in Kotlin
interface LowerWildcardTypeName : WildcardTypeName// Unbounded wildcard
val unbounded = WildcardTypeName() // or EmptyWildcardTypeName// Single upper bound: ? extends String
val extendsString = LowerWildcardTypeName(JavaClassNames.STRING.ref())
// Multiple upper bounds: ? extends T1 & T2
val multipleUpper = LowerWildcardTypeName(listOf(
JavaClassNames.STRING.ref(),
ClassName("java.io", "Serializable").ref()
))// Single lower bound: ? super Integer
val superInteger = UpperWildcardTypeName(JavaClassNames.INTEGER.ref())
// Multiple lower bounds: ? super T1 & T2
val multipleLower = UpperWildcardTypeName(listOf(
JavaClassNames.NUMBER.ref(),
ClassName("java.io", "Serializable").ref()
))val wildcard = WildcardTypeName()
// Convert to specific bound types
val asUpper = wildcard.toUpper(listOf(JavaClassNames.STRING.ref()))
val asLower = wildcard.toLower(listOf(JavaClassNames.INTEGER.ref()))
// Convert existing bounds
val existing = LowerWildcardTypeName(JavaClassNames.STRING.ref())
val converted = existing.toUpper() // Uses existing bounds// Unbounded wildcard for List<?>
val listOfAnything = JavaClassNames.LIST.parameterized(EmptyWildcardTypeName.ref())
// Upper bounded for List<? extends Number>
val extendsNumber = LowerWildcardTypeName(JavaClassNames.NUMBER.ref())
val listOfNumbers = JavaClassNames.LIST.parameterized(extendsNumber.ref())
// Lower bounded for List<? super Integer>
val superInteger = UpperWildcardTypeName(JavaClassNames.INTEGER.ref())
val listSuperInteger = JavaClassNames.LIST.parameterized(superInteger.ref())
// Complex wildcard usage
val comparator = ClassName("java.util", "Comparator")
val wildComparator = comparator.parameterized(
LowerWildcardTypeName(JavaClassNames.STRING.ref()).ref()
)
// Check wildcard properties
println(extendsNumber.bounds.first()) // java.lang.Number
println(EmptyWildcardTypeName.isEmpty) // true
println(EmptyWildcardTypeName.toString()) // "*"All generic type names work seamlessly with the TypeRef system:
// Create type references
val stringRef = JavaClassNames.STRING.ref()
val listRef = JavaClassNames.LIST.parameterized(stringRef).ref()
val arrayRef = ArrayTypeName(stringRef).ref()
// Use in parameterized types
val mapOfListToArray = JavaClassNames.MAP.parameterized(listRef, arrayRef)
// Complex type hierarchies
val nestedGeneric = JavaClassNames.LIST.parameterized(
JavaClassNames.MAP.parameterized(
JavaClassNames.STRING.ref(),
ArrayTypeName(JavaPrimitiveTypeNames.INT.ref()).ref()
).ref()
).ref()Prefer DSL methods over direct constructor calls:
// Preferred
val listOfString = JavaClassNames.LIST.parameterized(JavaClassNames.STRING.ref())
// Avoid
val listOfString = ParameterizedTypeName(JavaClassNames.LIST, JavaClassNames.STRING.ref())Always use .ref() when passing types as arguments:
// Correct
val parameterized = rawType.parameterized(argumentType.ref())
// Incorrect
val parameterized = rawType.parameterized(argumentType) // Compilation errorFollow Java naming conventions for wildcards:
// Upper bounds (covariant): ? extends T
val producer = LowerWildcardTypeName(elementType.ref())
// Lower bounds (contravariant): ? super T
val consumer = UpperWildcardTypeName(elementType.ref())Build multi-dimensional arrays incrementally:
// 2D array: String[][]
val stringArray = ArrayTypeName(JavaClassNames.STRING.ref())
val string2DArray = ArrayTypeName(stringArray.ref())
// 3D array: String[][][]
val string3DArray = ArrayTypeName(string2DArray.ref())// List<String>
val listOfString = JavaClassNames.LIST.parameterized(JavaClassNames.STRING.ref())
// Map<String, Integer>
val stringToInt = JavaClassNames.MAP.parameterized(
JavaClassNames.STRING.ref(),
JavaClassNames.INTEGER.ref()
)
// Set<? extends Number>
val numberSet = JavaClassNames.SET.parameterized(
LowerWildcardTypeName(JavaClassNames.NUMBER.ref()).ref()
)// Function<String, Integer>
val function = ClassName("java.util.function", "Function").parameterized(
JavaClassNames.STRING.ref(),
JavaClassNames.INTEGER.ref()
)
// Predicate<T>
val predicate = ClassName("java.util.function", "Predicate").parameterized(
TypeVariableName("T").ref()
)// Class<? extends Enum<?>>
val enumClass = JavaClassNames.CLASS.parameterized(
LowerWildcardTypeName(
ClassName("java.lang", "Enum").parameterized(EmptyWildcardTypeName.ref()).ref()
).ref()
)