CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-palantir-javapoet--javapoet

JavaPoet is a Java API for generating .java source files programmatically with support for modern Java features including records and sealed types

Overview
Eval results
Files

wildcard-type-names.mddocs/

Wildcard Type Names

WildcardTypeName represents wildcard types used in generic type arguments, such as ? extends Number (upper bounded) or ? super Integer (lower bounded). It extends TypeName and is commonly used in parameterized types to provide flexibility in generic type declarations.

Capabilities

Creating Wildcard Types

Factory methods for creating wildcard type instances.

/**
 * Creates an upper-bounded wildcard (? extends Type)
 * @param upperBound - The upper bound type
 * @return WildcardTypeName for "? extends upperBound"
 */
static WildcardTypeName subtypeOf(TypeName upperBound);

/**
 * Creates an upper-bounded wildcard from reflection Type
 * @param upperBound - The upper bound type
 * @return WildcardTypeName for "? extends upperBound"
 */
static WildcardTypeName subtypeOf(Type upperBound);

/**
 * Creates a lower-bounded wildcard (? super Type)
 * @param lowerBound - The lower bound type
 * @return WildcardTypeName for "? super lowerBound"
 */
static WildcardTypeName supertypeOf(TypeName lowerBound);

/**
 * Creates a lower-bounded wildcard from reflection Type
 * @param lowerBound - The lower bound type
 * @return WildcardTypeName for "? super lowerBound"
 */
static WildcardTypeName supertypeOf(Type lowerBound);

/**
 * Creates a WildcardTypeName from a WildcardType mirror
 * @param mirror - The wildcard type from annotation processing
 * @return WildcardTypeName from the mirror
 */
static WildcardTypeName get(javax.lang.model.type.WildcardType mirror);

/**
 * Creates a WildcardTypeName from reflection WildcardType
 * @param wildcardType - The wildcard type from reflection
 * @return WildcardTypeName from the reflection type
 */
static WildcardTypeName get(java.lang.reflect.WildcardType wildcardType);

Usage Examples:

// ? extends Number
WildcardTypeName extendsNumber = WildcardTypeName.subtypeOf(Number.class);

// ? extends Comparable
WildcardTypeName extendsComparable = WildcardTypeName.subtypeOf(Comparable.class);

// ? super Integer
WildcardTypeName superInteger = WildcardTypeName.supertypeOf(Integer.class);

// ? extends Object (unbounded wildcard)
WildcardTypeName unbounded = WildcardTypeName.subtypeOf(Object.class);

// ? extends User
ClassName user = ClassName.get("com.example", "User");
WildcardTypeName extendsUser = WildcardTypeName.subtypeOf(user);

// ? super String
WildcardTypeName superString = WildcardTypeName.supertypeOf(String.class);

Accessing Bounds

Get the upper and lower bounds of the wildcard.

/**
 * Returns the upper bounds of this wildcard
 * @return List of upper bound TypeName (always contains at least Object)
 */
List<TypeName> upperBounds();

/**
 * Returns the lower bounds of this wildcard
 * @return List of lower bound TypeName (empty for upper-bounded wildcards)
 */
List<TypeName> lowerBounds();

Usage Examples:

// ? extends Number
WildcardTypeName extendsNumber = WildcardTypeName.subtypeOf(Number.class);
List<TypeName> upper = extendsNumber.upperBounds(); // [Number]
List<TypeName> lower = extendsNumber.lowerBounds(); // []

// ? super Integer
WildcardTypeName superInteger = WildcardTypeName.supertypeOf(Integer.class);
List<TypeName> upper2 = superInteger.upperBounds(); // [Object]
List<TypeName> lower2 = superInteger.lowerBounds(); // [Integer]

Working with Annotations

Add or remove type annotations.

/**
 * Returns a copy with additional annotations
 * @param annotations - Annotations to add
 * @return New WildcardTypeName with annotations
 */
WildcardTypeName annotated(AnnotationSpec... annotations);

/**
 * Returns a copy with additional annotations from a list
 * @param annotations - List of annotations to add
 * @return New WildcardTypeName with annotations
 */
WildcardTypeName annotated(List<AnnotationSpec> annotations);

/**
 * Returns a copy without any annotations
 * @return New WildcardTypeName without annotations
 */
WildcardTypeName withoutAnnotations();

Common Patterns

Unbounded Wildcard

// List<?>
WildcardTypeName unbounded = WildcardTypeName.subtypeOf(Object.class);
ParameterizedTypeName listOfAnything = ParameterizedTypeName.get(
    ClassName.get(List.class),
    unbounded
);

// Class<?>
ParameterizedTypeName anyClass = ParameterizedTypeName.get(
    ClassName.get(Class.class),
    unbounded
);

Upper-Bounded Wildcards (? extends)

// List<? extends Number>
WildcardTypeName extendsNumber = WildcardTypeName.subtypeOf(Number.class);
ParameterizedTypeName listOfNumbers = ParameterizedTypeName.get(
    ClassName.get(List.class),
    extendsNumber
);

// Collection<? extends String>
WildcardTypeName extendsString = WildcardTypeName.subtypeOf(String.class);
ParameterizedTypeName collectionOfStrings = ParameterizedTypeName.get(
    ClassName.get(Collection.class),
    extendsString
);

// Set<? extends Serializable>
WildcardTypeName extendsSerializable = WildcardTypeName.subtypeOf(Serializable.class);
ParameterizedTypeName setOfSerializable = ParameterizedTypeName.get(
    ClassName.get(Set.class),
    extendsSerializable
);

Lower-Bounded Wildcards (? super)

// List<? super Integer>
WildcardTypeName superInteger = WildcardTypeName.supertypeOf(Integer.class);
ParameterizedTypeName listSuperInteger = ParameterizedTypeName.get(
    ClassName.get(List.class),
    superInteger
);

// Comparator<? super String>
WildcardTypeName superString = WildcardTypeName.supertypeOf(String.class);
ParameterizedTypeName comparatorSuperString = ParameterizedTypeName.get(
    ClassName.get(Comparator.class),
    superString
);

// Consumer<? super User>
ClassName user = ClassName.get("com.example", "User");
WildcardTypeName superUser = WildcardTypeName.supertypeOf(user);
ParameterizedTypeName consumerSuperUser = ParameterizedTypeName.get(
    ClassName.get(Consumer.class),
    superUser
);

Producer-Consumer (PECS) Pattern

// Producer: ? extends T (provides values)
TypeVariableName t = TypeVariableName.get("T");
WildcardTypeName producer = WildcardTypeName.subtypeOf(t);
ParameterizedTypeName producerCollection = ParameterizedTypeName.get(
    ClassName.get(Collection.class),
    producer
);

MethodSpec addAll = MethodSpec.methodBuilder("addAll")
    .addModifiers(Modifier.PUBLIC)
    .addTypeVariable(t)
    .addParameter(producerCollection, "source")
    .returns(TypeName.VOID)
    .addStatement("// add elements from source")
    .build();

// Consumer: ? super T (accepts values)
WildcardTypeName consumer = WildcardTypeName.supertypeOf(t);
ParameterizedTypeName consumerCollection = ParameterizedTypeName.get(
    ClassName.get(Collection.class),
    consumer
);

MethodSpec copyTo = MethodSpec.methodBuilder("copyTo")
    .addModifiers(Modifier.PUBLIC)
    .addTypeVariable(t)
    .addParameter(consumerCollection, "destination")
    .returns(TypeName.VOID)
    .addStatement("// copy elements to destination")
    .build();

Using in Method Parameters

// public void process(List<? extends Number> numbers)
WildcardTypeName extendsNumber = WildcardTypeName.subtypeOf(Number.class);
ParameterizedTypeName listOfNumbers = ParameterizedTypeName.get(
    ClassName.get(List.class),
    extendsNumber
);

MethodSpec process = MethodSpec.methodBuilder("process")
    .addModifiers(Modifier.PUBLIC)
    .addParameter(listOfNumbers, "numbers")
    .returns(TypeName.VOID)
    .build();

// public void add(Collection<? super String> collection)
WildcardTypeName superString = WildcardTypeName.supertypeOf(String.class);
ParameterizedTypeName collectionSuperString = ParameterizedTypeName.get(
    ClassName.get(Collection.class),
    superString
);

MethodSpec add = MethodSpec.methodBuilder("add")
    .addModifiers(Modifier.PUBLIC)
    .addParameter(collectionSuperString, "collection")
    .returns(TypeName.VOID)
    .build();

Using in Return Types

// public List<?> getAll()
WildcardTypeName unbounded = WildcardTypeName.subtypeOf(Object.class);
ParameterizedTypeName listOfAnything = ParameterizedTypeName.get(
    ClassName.get(List.class),
    unbounded
);

MethodSpec getAll = MethodSpec.methodBuilder("getAll")
    .addModifiers(Modifier.PUBLIC)
    .returns(listOfAnything)
    .addStatement("return items")
    .build();

// public Class<?> getType()
ParameterizedTypeName anyClass = ParameterizedTypeName.get(
    ClassName.get(Class.class),
    unbounded
);

MethodSpec getType = MethodSpec.methodBuilder("getType")
    .addModifiers(Modifier.PUBLIC)
    .returns(anyClass)
    .build();

Using in Fields

// private List<? extends Animal> animals;
ClassName animal = ClassName.get("com.example", "Animal");
WildcardTypeName extendsAnimal = WildcardTypeName.subtypeOf(animal);
ParameterizedTypeName listOfAnimals = ParameterizedTypeName.get(
    ClassName.get(List.class),
    extendsAnimal
);

FieldSpec animals = FieldSpec.builder(listOfAnimals, "animals")
    .addModifiers(Modifier.PRIVATE)
    .build();

// private Comparator<?> comparator;
WildcardTypeName unbounded = WildcardTypeName.subtypeOf(Object.class);
ParameterizedTypeName anyComparator = ParameterizedTypeName.get(
    ClassName.get(Comparator.class),
    unbounded
);

FieldSpec comparator = FieldSpec.builder(anyComparator, "comparator")
    .addModifiers(Modifier.PRIVATE)
    .build();

Complex Generic Signatures

// Map<String, List<? extends Number>>
WildcardTypeName extendsNumber = WildcardTypeName.subtypeOf(Number.class);
ParameterizedTypeName listOfNumbers = ParameterizedTypeName.get(
    ClassName.get(List.class),
    extendsNumber
);
ParameterizedTypeName mapType = ParameterizedTypeName.get(
    ClassName.get(Map.class),
    ClassName.get(String.class),
    listOfNumbers
);

// Function<? super String, ? extends Integer>
WildcardTypeName superString = WildcardTypeName.supertypeOf(String.class);
WildcardTypeName extendsInteger = WildcardTypeName.subtypeOf(Integer.class);
ParameterizedTypeName functionType = ParameterizedTypeName.get(
    ClassName.get(Function.class),
    superString,
    extendsInteger
);

Stream Collectors

// Collector<String, ?, List<String>>
WildcardTypeName unbounded = WildcardTypeName.subtypeOf(Object.class);
ParameterizedTypeName listOfStrings = ParameterizedTypeName.get(
    ClassName.get(List.class),
    ClassName.get(String.class)
);
ParameterizedTypeName collector = ParameterizedTypeName.get(
    ClassName.get(Collector.class),
    ClassName.get(String.class),
    unbounded,
    listOfStrings
);

MethodSpec toList = MethodSpec.methodBuilder("toList")
    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
    .returns(collector)
    .addStatement("return $T.toList()", Collectors.class)
    .build();

Generic Copy Method

// public <T> void copy(List<? extends T> source, List<? super T> dest)
TypeVariableName t = TypeVariableName.get("T");
WildcardTypeName extendsT = WildcardTypeName.subtypeOf(t);
WildcardTypeName superT = WildcardTypeName.supertypeOf(t);

ParameterizedTypeName sourceList = ParameterizedTypeName.get(
    ClassName.get(List.class),
    extendsT
);
ParameterizedTypeName destList = ParameterizedTypeName.get(
    ClassName.get(List.class),
    superT
);

MethodSpec copy = MethodSpec.methodBuilder("copy")
    .addModifiers(Modifier.PUBLIC)
    .addTypeVariable(t)
    .addParameter(sourceList, "source")
    .addParameter(destList, "dest")
    .returns(TypeName.VOID)
    .addStatement("dest.addAll(source)")
    .build();

Comparable and Comparator

// Comparable<? super T>
TypeVariableName t = TypeVariableName.get("T");
WildcardTypeName superT = WildcardTypeName.supertypeOf(t);
ParameterizedTypeName comparableSuperT = ParameterizedTypeName.get(
    ClassName.get(Comparable.class),
    superT
);

// Class<? extends Number>
WildcardTypeName extendsNumber = WildcardTypeName.subtypeOf(Number.class);
ParameterizedTypeName numberClass = ParameterizedTypeName.get(
    ClassName.get(Class.class),
    extendsNumber
);

With Type Variables

// class Box<T extends Comparable<? super T>>
TypeVariableName t = TypeVariableName.get("T");
WildcardTypeName superT = WildcardTypeName.supertypeOf(t);
ParameterizedTypeName comparableSuperT = ParameterizedTypeName.get(
    ClassName.get(Comparable.class),
    superT
);
TypeVariableName boundedT = TypeVariableName.get("T", comparableSuperT);

TypeSpec box = TypeSpec.classBuilder("Box")
    .addTypeVariable(boundedT)
    .build();

Understanding Wildcards

When to Use ? extends (Upper Bound)

Use when you want to read from a structure (producer):

// Can read Number or its subtypes from the list
void sum(List<? extends Number> numbers) {
    double total = 0;
    for (Number num : numbers) {
        total += num.doubleValue(); // Safe to read
    }
}

When to Use ? super (Lower Bound)

Use when you want to write to a structure (consumer):

// Can add Integer or its subtypes to the list
void addIntegers(List<? super Integer> list) {
    list.add(42);        // Safe to write
    list.add(Integer.valueOf(10)); // Safe to write
}

Types

class WildcardTypeName extends TypeName {
    static WildcardTypeName subtypeOf(TypeName upperBound);
    static WildcardTypeName subtypeOf(Type upperBound);
    static WildcardTypeName supertypeOf(TypeName lowerBound);
    static WildcardTypeName supertypeOf(Type lowerBound);
    static WildcardTypeName get(javax.lang.model.type.WildcardType mirror);
    static WildcardTypeName get(java.lang.reflect.WildcardType wildcardType);

    List<TypeName> upperBounds();
    List<TypeName> lowerBounds();
    WildcardTypeName annotated(AnnotationSpec... annotations);
    WildcardTypeName annotated(List<AnnotationSpec> annotations);
    WildcardTypeName withoutAnnotations();
}

Install with Tessl CLI

npx tessl i tessl/maven-com-palantir-javapoet--javapoet

docs

annotation-specifications.md

array-type-names.md

class-names.md

code-blocks.md

field-specifications.md

index.md

java-files.md

method-specifications.md

name-allocator.md

parameter-specifications.md

parameterized-type-names.md

type-names.md

type-specifications.md

type-variable-names.md

wildcard-type-names.md

tile.json