Skip to content
Back to skills

Java Generics

ASecurity

Generics as a compile-time contract over an erased runtime: raw types and what they disable, eliminating unchecked warnings rather than suppressing them, why arrays and generics do not mix, generic types and methods, bounded wildcards for API flexibility (PECS), generic varargs and @SafeVarargs, and typesafe heterogeneous containers with class tokens. Use when a raw type, a cast to a generic type, or an unchecked warning appears; when code creates an array of a generic type or a generic varar...

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 19, 2026
developmentrustgojavaexpressgitapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 5 files and shows the line behind each finding

Scanned September 29, 2026

npx -y skills add robsonkades/agent-skills --skill java-generics --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Java Generics?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Java Generics
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/robsonkades-java-generics/badge)](https://www.skillsdirectory.com/skills/robsonkades-java-generics)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: java-generics
description: >
  Generics as a compile-time contract over an erased runtime: raw types and what they
  disable, eliminating unchecked warnings rather than suppressing them, why arrays and
  generics do not mix, generic types and methods, bounded wildcards for API flexibility
  (PECS), generic varargs and @SafeVarargs, and typesafe heterogeneous containers with class
  tokens. Use when a raw type, a cast to a generic type, or an unchecked warning appears;
  when code creates an array of a generic type or a generic varargs parameter; when a
  collection parameter forces callers to convert before calling; when ClassCastException
  surfaces far from any visible cast; when a deserialised list of strings turns out to
  contain something else; or when designing a container that must hold values of several
  types safely. Does not cover null contracts (java-null-safety), stream pipelines
  (java-streams), collection implementation choice, or wider API-shape decisions (java-api-design).
---

# Java Generics

## Purpose

Get the compiler to reject casts that would otherwise fail at runtime, in a language where most
instantiated type arguments are erased from runtime object identity. Generic signatures may remain
in class-file/reflection metadata and some types are reifiable; do not equate erasure with “no
generic metadata.” Two failure modes: the codebase that opts out—
raw types, `@SuppressWarnings("unchecked")` on whole classes, `Object` parameters and casts
at the call sites — so type errors surface as `ClassCastException` in production; and the
signature so wildcard-heavy that callers cannot call it and nobody can read it.

## Workflow

Examples target Java 21 without preview. Inspect compiler release/toolchains and resolved
framework versions before changing signatures or type-token APIs; do not upgrade the project
or add a serialization library to make an illustration work. References contain partial snippets
unless explicitly presented as complete classes; supply imports and the enclosing declarations.
Start with supported caller expressions, including ordinary use, a useful subtype case and a
misuse that should be rejected. Preserve an adequate signature and its ownership/failure contract;
fewer warnings or fewer wildcards alone do not justify changing the API.

1. **Compile with relevant warnings on and govern them.** `-Xlint:unchecked`, `rawtypes`, and a
   deliberately maintained warning policy are often safer than blanket `-Werror` across JDK/tool
   upgrades. Every unchecked warning is a place where the
   compiler is telling you it cannot prove what your code assumes.
2. **Eliminate warnings from the inside out.** Fix the cause (parameterise the type, use a
   collection instead of an array, pass a class token). Suppress only when you can prove the
   invariant, on the narrowest declaration possible, with a comment giving the proof.
3. **Parameterise the relationship callers need.** Use a class parameter when callers choose
   one element type for an instance's lifetime, or a method parameter for a per-call relationship.
   A fixed domain type need not become generic.
4. **Set use-site variance from semantic data flow.** A source is often `? extends T`; a sink is
   often `? super T`; a parameter requiring exact read/write correlation may be `T`. Return types
   usually avoid wildcards for usability, but public families such as `Class<? extends X>` show
   legitimate exceptions.
5. **Check the runtime boundary.** Deserialisation, reflection, raw aliases or untyped caches can
   bypass the static contract; a typed cache/callback does not inherently lose it. Check the
   producer and token/validation behavior, including nested element types, before trusting values.
6. **Verify.** Account for unchecked warnings and justify each narrow suppression. Compile
   positive and deliberately invalid caller examples, and exercise runtime boundaries where
   static checking ends. Remove type-workaround copies, not copies required for ownership or
   isolation; report what the checks actually establish.

## Rules

- Avoid raw types except where required by class literals or legacy interoperation. Raw instance
  member types are erased under JLS rules; static members are not erased merely through a raw
  qualifier. `List<Object>` says "any object"; `List<?>` says "unknown element type";
  a raw `List` bypasses element-type checks and can introduce unchecked conversions.
- Use `List<?>` when element type is irrelevant. An arbitrary non-null value cannot be added directly, but this
  is not a read-only view: `clear`, iterator removal, and some `null` mutations remain possible.
  Unmodifiable wrappers restrict mutation through that view; backing aliases and mutable
  elements may still change. Snapshot/copy ownership belongs to java-immutability.
- Every unchecked warning is either eliminated or proven. Placing `@SuppressWarnings` on a
  class or a long method hides the next unchecked operation somebody adds there. Put it on the
  narrowest declaration — often a local variable extracted for that purpose — and write the
  one-line reason the cast is safe.
- Prefer lists to arrays wherever both would work. Arrays are covariant and reified
  (`Object[] a = new String[1]; a[0] = 1;` compiles and throws `ArrayStoreException`);
  generics are invariant and erased (the same mistake does not compile). Mixing them —
  `new List<String>[10]` (illegal directly) or unchecked `T[]` casts—can create heap pollution
  when aliases allow values inconsistent with the static element type.
- Avoid exposing arrays whose reified runtime component type cannot honor the generic promise.
  `ArrayList` stores an `Object[]` and casts elements on read; it does not make the whole backing
  array a truthful `T[]`. Controlled unchecked array creation requires confinement and proof.
- Bound wildcards by direction, and usually avoid them in return types. `Collection<? extends T>` for
  a producer, `Collection<? super T>` for a consumer, plain `Collection<T>` when the method
  needs exact read/write correlation. Wildcard capture can also support safe mutations such as
  swapping existing elements; return wildcards need the deliberate reason described in step 4.
- If a type parameter appears exactly once in a method signature, it should probably be a
  wildcard instead — and if a wildcard appears where the body needs to name the type, extract
  a private generic helper method to capture it. `swap(List<?>)` delegating to
  `swapHelper(List<E>)` is the canonical shape.
- A generic/non-reifiable varargs declaration needs a heap-pollution audit; `@SafeVarargs` is an
  assertion that the body and callees do not perform potentially unsafe operations, not a ritual
  requirement for all generic varargs. Avoid unsafe writes/aliases. It is legal on constructors
  and on static, final, or private instance methods; overridable instance methods cannot promise
  all implementations are safe.
- Use recursive bounds where the type must be comparable with itself:
  `<T extends Comparable<? super T>>`, not `<T extends Comparable<T>>` — the `super` form
  accepts a subtype whose comparison is inherited from a base class.
- With an `Object` operand, `instanceof List<String>` is illegal and `(List<String>) value`
  checks only that the object is a `List`, not its elements. Untyped JSON object elements may
  become maps while JSON strings remain strings. Pass an explicit type token (`Class<T>` for
  reifiable types, `TypeReference<List<String>>`,
  `ParameterizedTypeReference`) or validate the elements at the boundary.
- Represent "a container of many types" with a class token as key (`Map<Class<?>, Object>`
  behind an API that casts with `type.cast(value)`), not with `Object` values that callers
  cast themselves. For reifiable keys, `Class.cast` performs a checked cast with no unchecked
  suppression; `List.class` cannot distinguish lists by their element type. Choose this key
  only when one value per exact class fits; multiple same-typed attributes need distinct typed
  keys or domain fields. Define replacement and lookup semantics before adopting the container.
- Generifying an existing API is often binary compatible because erasures remain, and raw source
  uses may still compile with warnings, but it is not automatically compatible: erasure clashes,
  changed bounds/return inference, overload resolution and generated bridge methods can affect
  clients. Compile old source and run old binaries as compatibility tests (`java-api-design`).

- At override boundaries, inspect erasure and compiler-generated bridge methods. Changing generic
  bounds or introducing an overload with the same erasure can be illegal or binary-sensitive even
  when parameterized source signatures look distinct.

## References

- [Erasure, arrays and unchecked warnings](references/erasure-and-arrays.md) — read when a
  warning cannot be eliminated obviously, when generic arrays or varargs are involved, when
  `ClassCastException` appears without a visible cast, or when deciding what a suppression
  must prove.
- [Wildcards and generic API design](references/wildcards-and-api-design.md) — read when
  designing a signature callers must pass collections to, when choosing between a type
  parameter and a wildcard, when a generic method needs bounds, or when inference (`var`,
  diamond, lambdas) produces a type you did not expect.
- [Typesafe heterogeneous containers](references/typesafe-heterogeneous-containers.md) — read
  when one structure must hold values of several unrelated types — attribute maps, context
  propagation, plugin registries, caches keyed by type — or when a generic type must survive a
  serialisation boundary.

Files in this skill

  • SKILL.md9.3 KB
  • references/erasure-and-arrays.md10.2 KB
  • references/typesafe-heterogeneous-containers.md7.2 KB
  • references/wildcards-and-api-design.md8.5 KB
  • skill.yaml1.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…