In Gatling, why does a Java or Kotlin simulation need two imports per DSL family while a Scala simulation needs only `import io.gatling.core.Predef._`?
answer
- One object versus a package plus a class
- Predef extends CoreDsl and aliases the types
- Predef also carries the implicits nobody names
- CoreDsl is a final class of static members
basics
~20 sScala's Predef is one object that inherits every DSL method, aliases the types and carries the implicits, so one wildcard import covers all three. Java keeps types in a package and DSL methods as statics on CoreDsl, so each needs its own import.
solid answer
~40 s`io.gatling.core.Predef` is declared `object Predef extends CoreDsl`, so every DSL method is an inherited member of it. The same object also declares type aliases such as `type Simulation = io.gatling.core.scenario.Simulation`, and carries implicits including `implicit def configuration` and the implicit duration classes. One wildcard import of an object brings methods, types and implicits in together. Java has no construct that is a namespace of types and a bag of callable members at once, so Gatling's Java API splits the job: types are ordinary classes in `io.gatling.javaapi.core`, and the DSL entry points are `public static` members of `CoreDsl`, a final class with a private constructor. A package import covers the first, a static import the second. Kotlin inherits the Java shape unchanged.
code
scala · 14 linesimport scala.concurrent.duration._
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class CheckoutSimulation extends Simulation {
private val httpProtocol = http.baseUrl("https://example.com")
private val scn = scenario("checkout")
.exec(http("home").get("/"))
setUp(scn.inject(rampUsers(50).during(30.seconds))).protocols(httpProtocol)
}go deeper
Recall that Scala simulations use one Predef import per family and Java or Kotlin simulations use two, and that both preambles are copy-paste blocks from the reference.
Be ready to say what each half supplies: types from the package, DSL entry points from the static members, and in Scala implicits on top of both.
Explain why a narrowed Scala import is riskier than a narrowed Java one, and what that means for editor settings and review of load-test source.
Frame the choice for a team: the SDK you standardise on decides the preamble, the duration idiom and the tooling rules you have to write down and enforce.
Both preambles put the same DSL in scope, and the difference is not a matter of taste — it follows from what Scala's `Predef` object can be and what a Java class cannot. ## What the two preambles look like ```java // Java import io.gatling.javaapi.core.*; import static io.gatling.javaapi.core.CoreDsl.*; ``` ```scala // Scala import io.gatling.core.Predef._ ``` Two lines against one, for exactly the same capability. ## What io.gatling.core.Predef actually is In the Gatling source, `io.gatling.core.Predef` is a single Scala **object** that does three jobs at once: 1. **It inherits the DSL.** Its declaration is `object Predef extends CoreDsl`, and `CoreDsl` is a trait mixing in `StructureSupport`, `PauseSupport`, `CheckSupport`, `FeederSupport`, `OpenInjectionSupport`, `ClosedInjectionSupport`, `ThrottlingSupport`, `AssertionSupport`, `BodySupport` and `DummySupport`. Every DSL method is therefore an inherited **member** of `Predef`, not a static on some other class. 2. **It re-exports the types as aliases.** `Predef` declares `type Simulation = io.gatling.core.scenario.Simulation`, plus `type Session`, `type Status`, `type Assertion` and `type Node`. So `class Foo extends Simulation` in a Scala simulation resolves through the same import that supplies `scenario(...)`. 3. **It carries the implicits.** `implicit def configuration: GatlingConfiguration`, the implicit classes `DurationInteger` and `DurationJLong`, and — through the `CoreDsl` trait — `CoreDefaultImplicits` and `ValidationImplicits`. A single wildcard import of an object brings all three categories in together. That is the whole reason one line suffices. ## Why the Java API cannot do the same Java has no construct that is simultaneously a namespace of types, a bag of callable members and a source of implicits. So Gatling's Java API splits the job: | need | Scala | Java API | |---|---|---| | types | `type` aliases on the `Predef` object | ordinary classes in the `io.gatling.javaapi.core` package | | DSL entry points | inherited members of `Predef` | `public static` members of `CoreDsl`, a `final class` with a private constructor | | implicits | `implicit` members of `Predef` | no equivalent; explicit overloads instead | | import cost | one wildcard, `Predef._` | one package wildcard **plus** one static import | Because types come from a package and methods come from a class, no single Java import line can cover both. `import io.gatling.javaapi.core.*;` imports type names only — it does not make `CoreDsl`'s statics callable unqualified. `import static io.gatling.javaapi.core.CoreDsl.*;` imports the statics only — it does not introduce `ScenarioBuilder` as a type name. ## What this changes in practice * **Kotlin follows the Java shape exactly**, because Kotlin consumes the Java API. Same packages, same `CoreDsl`/`HttpDsl` classes; only the `static` keyword drops away, since Kotlin imports Java class members with a plain `import`. * **Duration handling differs for the same reason.** Java and Kotlin import `java.time.Duration` and write `Duration.ofMinutes(5)`. Scala imports `scala.concurrent.duration._` and writes `5.minutes`, which works because the implicit classes on `Predef` and in `scala.concurrent.duration` add those methods to numbers. * **Every family repeats the pattern.** HTTP means `io.gatling.javaapi.http.*` plus `HttpDsl.*` in Java and Kotlin, and `io.gatling.http.Predef._` in Scala. JDBC, JMS and Redis have the same pair of shapes: `JdbcDsl`, `JmsDsl`, `RedisDsl` on the Java side; `io.gatling.jdbc.Predef`, `io.gatling.jms.Predef`, `io.gatling.redis.Predef` on the Scala side. * **A narrowed Scala import is more dangerous than a narrowed Java one.** Replacing `Predef._` with a list of named members drops the implicits, which no call site mentions by name. ## Reading Gatling's samples with this in mind When you see a Gatling Scala sample whose only import is `io.gatling.core.Predef._` and which nonetheless uses `Simulation`, `scenario`, `atOnceUsers` and `global`, nothing is being omitted for brevity — all four really do come from that one object. When you see a Java sample with four import lines for two families, nothing is redundant either. Counting import lines is a fast way to tell which SDK a snippet is written against, and it explains why a Java snippet cannot be converted to Scala by mechanically renaming methods: the package structure behind it is different. ## What the split does not change It is worth being precise about how far the difference reaches, because it is easy to over-generalise from four import lines into a belief that the two SDKs are different products. * **The imported identifiers are the same.** `scenario`, `exec`, `atOnceUsers`, `rampUsers`, `global`, `details`, `csv` and the rest are spelled identically on both sides, and for those only the declaration site differs. The claim stops at the import boundary, though, and the injection entry point is the conspicuous exception. Scala's `ScenarioBuilder` exposes `inject(...)`, which tells open from closed by the type of the injection steps it is handed; the Java and Kotlin `ScenarioBuilder` has no `inject` at all and splits the entry point into `injectOpen(...)` and `injectClosed(...)`. So a Scala snippet cannot be transliterated to Java by changing only the imports — `scn.inject(...)` does not compile against the Java API — which is a sharper version of the point this section is making. * **The class you extend is analogous, not identical.** Java and Kotlin extend `io.gatling.javaapi.core.Simulation`; Scala extends `io.gatling.core.scenario.Simulation`, which `Predef` re-exports under the short name. * **Kotlin gains nothing extra.** There is no Kotlin module and no Kotlin `Predef`. Kotlin simulations import the same `io.gatling.javaapi` packages and the same `CoreDsl`/`HttpDsl` classes a Java simulation does; the only visible difference is that Kotlin's import lines have no `static` keyword. * **The obligation on `setUp` is unchanged.** Whichever preamble you write, Gatling instantiates your class by its no-argument constructor, so the registration call has to have run by the time construction finishes. The practical upshot is that the import block is the fastest way to identify which SDK a snippet targets, and the one part of a simulation that genuinely cannot be translated by renaming methods.
- What does the difference imply for how a duration is written in each SDK?Java and Kotlin import `java.time.Duration` and pass `Duration.ofSeconds(30)`. Scala imports `scala.concurrent.duration._` and writes `30.seconds`, which compiles because implicit classes add those methods to numbers. Both SDKs also accept a bare number, which Gatling reads as a count of seconds.
- Does the same two-versus-one split apply to families other than core?Yes, identically. HTTP is `io.gatling.javaapi.http.*` plus static `HttpDsl.*` in Java and Kotlin, against `io.gatling.http.Predef._` in Scala. JDBC, JMS and Redis repeat the pattern with `JdbcDsl`, `JmsDsl` and `RedisDsl` on one side and their own `Predef` objects on the other.
Scala's Predef is one desk that hands you the forms, the room directory and the rubber stamps together. Java's API keeps the directory on a wall and the forms at a counter, so you have to visit both before you can start.
saying these in an interview costs you the question
- Thinking Scala's Predef import is only namespace convenience
- Believing a Java package wildcard also imports a class's statics
- Expecting Kotlin to have its own Predef-style single import
- Assuming CoreDsl is a base class you extend rather than import