skip to content

Walk through the literal suffixes (L, f/F, and the lack of others) and explain how a small literal can initialize a Byte or Short without a suffix.

level: middleimportance: should knowfreq 40%

answer

  1. suffixes: only L and f/F
  2. no Byte/Short/Int suffix, no d/D
  3. constant fits expected type -> OK
  4. runtime value needs .toByte()
  5. no implicit widening or narrowing

basics

~20 s

L makes a Long, f/F makes a Float. There is no Byte/Short suffix and no Int suffix. A small constant like 100 can be assigned to a Byte or Short variable directly because the compiler sees it fits the declared type.

solid answer

~40 s

Kotlin's only numeric literal suffixes are `L` (Long) and `f`/`F` (Float). There is no suffix for `Byte`, `Short`, or `Int`, and no `d`/`D` for `Double` (decimals are `Double` by default). `Byte` and `Short` have no literal form of their own, yet you can still write `val b: Byte = 100`. This works because of a compile-time **constant-fit** rule: when an integer literal is a compile-time constant that fits the *expected* (declared) type's range, the compiler accepts it directly. So `100` fits `Byte` (`-128..127`) and `Short`. If it doesn't fit — `val b: Byte = 200` — it's a compile error, and you'd need an explicit conversion. This only applies to literals/constants with a known target type; a runtime `Int` value still needs `.toByte()` because Kotlin has no implicit narrowing.

code

kotlin · 5 lines
kotlin
val b: Byte = 100        // constant fits -> OK
val f: Float = 2.5f      // suffix required
val i = 100
// val bad: Byte = i     // compile error: no implicit narrowing
val good: Byte = i.toByte()  // explicit conversion

go deeper

for a junior

Knows L and f/F suffixes and that small constants can go into Byte/Short.

for a middle

Explains the constant-fit rule versus the lack of implicit narrowing for runtime values.

for a senior

Articulates the compile-time-constant + expected-type mechanism precisely and the Float/Double pitfall.

for a principal

Frames the design tradeoff: ergonomic constant init vs. safety from silent conversions, and how APIs should expose typed constants.

## The complete suffix set - **`L`** -> `Long` (uppercase only). Example: `100L`. - **`f` / `F`** -> `Float`. Example: `1.0f`, `2F`. - **No suffix for `Int`** — it's the default for fitting integer literals. - **No suffix for `Double`** — decimals/exponents default to `Double` (Kotlin has no `d`/`D`). - **No `Byte`/`Short` suffix** — these have no dedicated literal form. - **No unsigned here** beyond noting `u`/`U`/`uL` exist for unsigned types (covered by the Unsigned Types leaf — out of scope). ## How Byte/Short get values without a suffix Kotlin applies a **constant-fit** rule for integer *constants*: when the value is known at compile time and the expected type is `Byte` or `Short` (or `Long`), a fitting literal is accepted without conversion. ```kotlin val b: Byte = 100 // OK: 100 in -128..127 val s: Short = 30000 // OK: fits Short range val big: Byte = 200 // ERROR: 200 out of Byte range ``` The key is *expected type* + *compile-time constant fits*. Contrast with a runtime value: ```kotlin val i = 100 // inferred Int val b2: Byte = i // ERROR: no implicit narrowing val b3: Byte = i.toByte() // OK: explicit conversion ``` Kotlin deliberately has **no implicit widening or narrowing** between number types; every cross-type move at runtime uses explicit `toXxx()` (`toByte()`, `toLong()`, `toDouble()`, …). ## Float vs Double precision Because decimals default to `Double`, forgetting `f` where a `Float` is required is a common slip: ```kotlin val f: Float = 1.5 // ERROR: 1.5 is Double, no implicit narrowing val f2: Float = 1.5f // OK val f3: Float = 1.5.toFloat() // also OK ``` ## Why this matters The constant-fit shortcut keeps small-type initialization ergonomic while the no-implicit-conversion rule prevents silent precision/overflow bugs. The two coexist: constants are checked at compile time, runtime values are not auto-converted.

  • Why does `val b: Byte = 100` compile but `val b: Byte = i` (where i is an Int variable) does not?
    100 is a compile-time constant that fits Byte's range, so the compiler accepts it; i is a runtime Int, and Kotlin has no implicit narrowing, so it needs i.toByte().
  • Is there a suffix for Double?
    No. A literal with a decimal point or exponent is Double by default; there is no d/D suffix.

The constant-fit rule is a bouncer who lets a literal in if it clearly fits the dress code (range); a runtime value has no ID, so it must explicitly change clothes (toByte()).

saying these in an interview costs you the question

  • Inventing a 'b' or 's' suffix for Byte/Short
  • Claiming a runtime Int auto-narrows to Byte
  • Saying decimals default to Float
  • Using a 'd' suffix for Double
  • Confusing the constant-fit rule with implicit conversion of variables

context