What is a capability in Gradle, and how does it differ from a module's GAV coordinates?
answer
- group:name:version = 'what it provides'
- implicit capability == GAV
- guava vs google-collections clash
- capability conflict forces a choice
- lives in Gradle Module Metadata
basics
~10 sA capability is a logical 'what this provides' identifier (group:name:version) attached to a variant. Two modules offering the same capability conflict, so only one can be on the classpath.
solid answer
~40 sA **capability** declares what functionality a component provides, expressed as `group:name:version`. By default every published module has an *implicit capability* equal to its GAV coordinates. Capabilities let Gradle detect when two different modules provide the **same** thing — e.g. `com.google.guava:guava` and `com.google.collections:google-collections` both implement the Guava collections API. If you tell Gradle they share a capability, the dependency resolution engine raises a **capability conflict** and forces you to pick one, instead of silently putting both on the classpath and getting duplicate classes. So GAV identifies *which artifact*, while a capability identifies *what is provided*. Capabilities are matched per **variant** (via Gradle Module Metadata), and they are the foundation for feature variants and for resolving 'same thing, different coordinates' clashes.
code
kotlin · 16 linesdependencies {
// Tell Gradle google-collections provides the same capability as guava
components.withModule("com.google.collections:google-collections") {
allVariants {
withCapabilities {
addCapability("com.google.guava", "guava", "1.0")
}
}
}
}
configurations.all {
resolutionStrategy.capabilitiesResolution.withCapability("com.google.guava:guava") {
select("com.google.guava:guava:0")
}
}go deeper
Know that a capability says 'what a module provides' and that two modules with the same capability conflict.
Explain implicit-capability == GAV, the guava/google-collections example, and that capabilities live in Gradle Module Metadata.
Discuss declaring capabilities via component metadata rules and resolving conflicts with capabilitiesResolution.
Frame capabilities as a graph-level governance tool for de-duplicating 'same API, different coordinates' across a large dependency surface and enforcing it org-wide.
## What a capability is In Gradle's dependency model, a published component (a module) is made of one or more **variants** (e.g. `apiElements`, `runtimeElements`). Each variant carries **attributes** (like `org.gradle.usage`) and one or more **capabilities**. A **capability** is a coordinate of the form `group:name:version` that answers the question *"what does this provide?"* — as opposed to GAV coordinates which answer *"where does this come from?"*. ## Implicit capability Every module automatically gets an **implicit capability** equal to its own GAV. So `com.fasterxml.jackson.core:jackson-databind:2.15.0` implicitly provides the capability `com.fasterxml.jackson.core:jackson-databind:2.15.0`. This is why you normally can't have two versions of the same module on a classpath — they share a capability and Gradle picks one (conflict resolution by version). ## Why explicit capabilities matter The classic problem: `com.google.collections:google-collections` and `com.google.guava:guava` provide the **same API** under **different GAV coordinates**. Without help, Gradle sees two unrelated modules and puts both on the classpath → duplicate classes, `NoSuchMethodError` at runtime. By declaring that both provide the same capability, Gradle detects the clash: ```kotlin dependencies { components.withModule("com.google.collections:google-collections") { allVariants { withCapabilities { addCapability("com.google.guava", "guava", "1.0") } } } } ``` Now both modules provide `com.google.guava:guava`, Gradle reports a **capability conflict**, and you resolve it explicitly. ## How conflicts are resolved When two variants in the graph expose the same capability, you must choose one with a resolution rule: ```kotlin configurations.all { resolutionStrategy.capabilitiesResolution.withCapability("com.google.guava:guava") { select("com.google.guava:guava:0") } } ``` ## Where capabilities are published Capabilities live in **Gradle Module Metadata** (the `.module` JSON file). Maven POMs cannot express capabilities, so consumers using only `.pom` won't see them — capability-based conflict detection is a Gradle-native feature. ## Relationship to feature variants Feature variants (`java { registerFeature(...) }`) work by attaching a **distinct capability** to an optional set of artifacts/dependencies, so a consumer must opt in by requesting that capability. That's covered in the conflict and feature-variant questions.
- Why can't a plain Maven POM express a capability?POM format has no field for capabilities or variants. Capabilities are a Gradle-native concept serialized only in Gradle Module Metadata (the `.module` file). Consumers resolving via POM see only GAV-based conflict detection.
- What capability does a module have if you never declare one?Its implicit capability, equal to its own `group:name:version`. That's why two versions of the same module conflict by default.
GAV is a product's barcode (which exact box); a capability is the product category on the shelf (only one brand of milk should end up in your cart for the same purpose).
saying these in an interview costs you the question
- Saying a capability is just another name for GAV coordinates — it's a separate 'what is provided' identifier.
- Claiming capabilities are visible to Maven consumers via the POM.