Concepts / Wildcard Types in Generic Methods

Wildcard Types in Generic Methods

Wildcard types let a method work with collections of objects of various types, but they do not restrict the types allowed as formal type parameters in a generic class or method definition.

  • Programming

One Class, Many Kinds of Group

Generic code is usually written once and then used with many different types. Imagine you want to create groups of GUI components. Rather than writing one class for button groups and another for slider groups, you can write a single generic class named ControlGroup. Its type parameter stands for the kind of component held in a group. This article follows that one scenario through the rest of the lesson, because it shows exactly where wildcards help and where they stop helping.

What do you think happens?

If both ControlGroup<JButton> and ControlGroup<JSlider> exist in a program, how many class definitions did the programmer have to write?

  • One

  • Two

  • One for each component type used

Reveal answer
Answer:

One

Both parameterized types come from the same generic class definition. Only the type parameter changes from one use to the next, so the class itself is written once.

Tracing ControlGroup Through Two Type Arguments

The parameterized type ControlGroup<JButton> represents a group of JButton objects. The parameterized type ControlGroup<JSlider> represents a group of JSlider objects. In both cases the generic class is the same. The type argument in the angle brackets is what gets substituted for the type parameter when the class is used.

ControlGroupthe generic class name<opens the type argumentlistJButtonfills the type parameter>closes the type argumentlistparameterized with
Where does the type argument go in the ControlGroup declaration, and which part of the expression fills the type parameter?
Worked example

Following the type parameter through one class

Given a generic class ControlGroup whose type parameter stands for the component type held by the group, what does ControlGroup<JSlider> change and what stays the same?

  1. 1
    Identify the generic class

    ControlGroup is defined once. Its definition contains a type parameter, which is a placeholder for a component type.

  2. 2
    Supply a type argument

    Writing JSlider in the angle brackets fills that placeholder with the JSlider type.

  3. 3
    Read the result

    ControlGroup<JSlider> is a group of JSlider objects. The class body is the same as it would be for ControlGroup<JButton>. Only the type parameter has been replaced.

Result

The class definition is shared. The type argument determines which component type the group holds.

What a Wildcard Widens and What It Leaves Open

Definition

A wildcard type lets a method work with collections of objects of various types. It makes the method more general, because the method body does not depend on one specific element type.

Think of a wildcard as a way of widening what a method accepts. The method's parameter can refer to a collection whose element type varies. This is valuable when the same operation should apply to a list of strings, a list of numbers, or a list of components. The gain is real, but it answers only one design question. It does not answer which types a caller may supply for the formal type parameter of a generic class or method definition.

java
Notes on the code
  • The ? is a wildcard. It lets the parameter accept a List of any element type.
  • The method body only relies on each element being an Object, so it works for any element type.

Where a Bound Enters the Definition

Bounded types were introduced to fill the gap a wildcard leaves. A bound is written in the definition itself, so it applies to the type parameter wherever the class is used. The author of a generic class decides what the class is meant to hold. A bounded type lets the author state that a type parameter may only be filled with types that satisfy the stated restriction. Because the restriction belongs to the definition, every use of the class is held to it.

Componentthe declared upper boundJButtonaccepted as a type argumentJSlideraccepted as a type argumentStringrejected: not a Componentsubtype ofsubtype of
Which types are allowed to fill this type parameter once an upper bound is declared, and which are rejected?

The diagram above uses a hypothetical restriction. Suppose the author of ControlGroup wants it to hold only components that can be placed on a screen as interactive controls. The bound Component stands in for that requirement. JButton and JSlider satisfy it, so they may fill the type parameter. String does not, so a declaration such as ControlGroup<String> would be rejected by the restriction. The example is included only to show the role of a bound. The source does not give a specific bound for ControlGroup.

java
Notes on the code
  • The extends clause in the type parameter declaration is the bound.
  • The restriction sits in the class definition, so ControlGroup<String> is rejected wherever it appears.

Common Misunderstandings About Wildcards and Bounds

  • Common mistake

    Believing a wildcard limits which types a caller can use for a class's type parameter

    Why it fails

    A wildcard lets a method accept collections of varying element types. It does not restrict the types allowed as formal type parameters in a generic class or method definition.

    Do this instead

    If the goal is to restrict which types may fill a type parameter, declare a bound in the definition.

  • Common mistake

    Believing a bound is only a convenience for a wildcard

    Why it fails

    Wildcards and bounded types address different concerns. The choice depends on whether the goal is flexibility over collections or restriction of type parameters.

    Do this instead

    Ask first whether you need flexibility for a method's parameter or a restriction on the type parameter. Then choose the matching tool.

  • Common mistake

    Thinking ControlGroup<JButton> and ControlGroup<JSlider> require two generic class definitions

    Why it fails

    Both come from one class definition. The type argument changes, not the class.

    Do this instead

    Write the generic class once and supply different type arguments at each use.

  • Common mistake

    Assuming that a wildcard guarantees every element in a collection belongs to a particular family of types

Checking Your Reading of ControlGroup

MEDIUM

A teammate says: 'I will use a wildcard in the declaration of ControlGroup so that only components can be stored in it.' Explain whether this achieves the goal, and state what mechanism the source describes for that purpose.

Hints
  • Recall what the source says a wildcard does and does not restrict.
  • Think about where a bound is written: in the method body, or in the definition of the type parameter.

Key Takeaways

  • A single generic class such as ControlGroup can be used with different type arguments, such as ControlGroup<JButton> and ControlGroup<JSlider>, without writing a new class definition.
  • A wildcard type lets a method accept collections of objects of various types, but it does not restrict the types allowed as formal type parameters in a generic class or method definition.
  • A bounded type is written in the definition and states a restriction that every use of the class must satisfy.
  • Wildcards and bounds solve different problems: flexibility over collections versus restriction of type parameters.