skip to content

When and why would you use Gradle's map dependency notation instead of the string form, and what keys does it support?

level: middleimportance: must knowfreq 60%

answer

  1. keys: group/name/version/classifier/ext
  2. classifier = sources, native platform
  3. ext = packaging (zip/aar), default jar
  4. Kotlin named args / Groovy literal map
  5. same resolution as string for same GAV

basics

~20 s

Map 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 s

The **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 lines
kotlin
dependencies {
    // 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

for a junior

Know that map notation names group/name/version as separate keys and that it exists for classifiers/extensions.

for a middle

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.

for a senior

Compare with the extended string form g:n:v:classifier@ext, explain when programmatic construction justifies map notation, and note resolution equivalence.

for a principal

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.

context