When and why would you use Gradle's map dependency notation instead of the string form, and what keys does it support?
answer
- keys: group/name/version/classifier/ext
- classifier = sources, native platform
- ext = packaging (zip/aar), default jar
- Kotlin named args / Groovy literal map
- same resolution as string for same GAV
basics
~20 sMap notation spells out each coordinate as a key: group:, name:, version:, plus classifier: and ext:. Use it when you need a classifier/extension or to set a segment programmatically — string notation can't express those.
solid answer
~50 sThe **map notation** expands a dependency into explicit key/value pairs: `group`, `name`, `version`, and optionally `classifier` and `ext` (extension). You use it when the compact `"g:n:v"` string can't express what you need — chiefly a **classifier** (e.g. `linux-x86_64` for native artifacts, or `sources`/`javadoc`) or a non-default **ext**/packaging (e.g. `zip`, `aar`). It's also handy when a coordinate segment is computed (a property/variable) so you don't have to do string concatenation. In Kotlin DSL you pass named arguments; in Groovy DSL a literal map. Resolution is identical to string notation for the same coordinates — the map form just exposes extra attributes. For multi-classifier or attribute-rich needs, you can also pass a configuring closure/lambda to set transitivity or excludes. Most teams keep string notation as the default and drop to map notation only where a classifier/ext is genuinely required.
code
kotlin · 10 linesdependencies {
// map notation with classifier + extension
runtimeOnly(
group = "io.netty",
name = "netty-transport-native-epoll",
version = "4.1.107.Final",
classifier = "linux-x86_64",
ext = "jar"
)
}go deeper
Know that map notation names group/name/version as separate keys and that it exists for classifiers/extensions.
List all keys (group/name/version/classifier/ext), give a real classifier use case (native libs, sources), and show the Kotlin named-arg vs Groovy literal-map forms.
Compare with the extended string form g:n:v:classifier@ext, explain when programmatic construction justifies map notation, and note resolution equivalence.
Discuss governance: keeping notation consistent across a large build, when classifier-pinned native artifacts hurt portability, and steering teams toward catalogs over ad-hoc map declarations.
## Why a second notation exists The string form `"group:name:version"` covers the common case but can't carry every attribute Gradle (and Maven repositories) understand. Two attributes in particular live outside GAV: - **classifier** — an extra qualifier on the artifact file name, e.g. `guava-33.0.0-jre-sources.jar` (classifier `sources`) or a platform-specific native build (`netty-...-linux-x86_64`). - **ext** (extension / packaging type) — the artifact extension when it isn't the default `jar`, e.g. `zip`, `aar`, `pom`. The **map notation** names every component explicitly so these can be set: ```kotlin dependencies { // Kotlin DSL: named arguments implementation(group = "com.google.guava", name = "guava", version = "33.0.0-jre") // with a classifier + extension runtimeOnly( group = "io.netty", name = "netty-transport-native-epoll", version = "4.1.107.Final", classifier = "linux-x86_64" ) } ``` ```groovy // Groovy DSL: a literal map dependencies { runtimeOnly group: 'io.netty', name: 'netty-transport-native-epoll', version: '4.1.107.Final', classifier: 'linux-x86_64' } ``` ## Supported keys | Key | Meaning | Required? | |-----|---------|-----------| | `group` | publishing namespace | effectively yes for external modules | | `name` | artifact/module id | yes | | `version` | version string | optional (BOM/constraint may supply) | | `classifier` | artifact qualifier | optional | | `ext` | extension/packaging | optional, defaults to `jar` | ## Equivalence to string notation For the same GAV, `"g:n:v"` and `group:"g", name:"n", version:"v"` resolve to the exact same module — the map form simply *adds* the ability to express `classifier`/`ext`. Note the string notation also has a compact extension for classifiers: `"g:n:v:classifier@ext"` (e.g. `"io.netty:netty...:4.1.107.Final:linux-x86_64"`). Map notation is preferred when readability or programmatic construction matters. ## Configuring closure / lambda Both notations accept a trailing configuration block to set per-dependency options on the resulting `ExternalModuleDependency`: ```kotlin implementation("org.hibernate:hibernate-core:6.4.4.Final") { isTransitive = false exclude(group = "org.jboss.logging", module = "jboss-logging") } ``` That block is part of declaring the dependency itself (notation + options), not a separate mechanism. ## Practical guidance Default to string notation for legibility; switch to map notation when you need a classifier or non-jar extension, or when a segment is a computed value and string interpolation would hurt readability.
- Can you express a classifier in string notation at all?Yes — the extended string form `"group:name:version:classifier@ext"`, e.g. `"io.netty:netty-...:4.1.107.Final:linux-x86_64"`. Map notation is just clearer and supports programmatic values.
- What does the `ext` key default to?`jar`. You only set it for non-default packaging like `zip`, `aar`, or `pom`.
- Do string and map notation resolve differently?No — for the same coordinates they resolve to the identical module. Map notation only *adds* the ability to set classifier/ext (and reads better for computed segments).
saying these in an interview costs you the question
- Saying classifiers can only be done with map notation (string supports `:classifier@ext`).
- Confusing `classifier` (artifact qualifier, e.g. sources/native) with `ext` (file extension/packaging).
- Claiming map notation changes resolution semantics versus string notation.