skip to content

Bitwise Operations

Kotlin spells bitwise operations as infix functions — shl, shr, ushr, and, or, xor, inv — rather than symbols, and adds bit-counting helpers. The distinction between shr and ushr for negative numbers is the usual follow-up.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

How do you perform bitwise operations in Kotlin, and how does this differ syntactically from Java?

level: juniorimportance: must knowfreq 55%

answer

  1. No &|^~<<>> symbols in Kotlin
  2. and/or/xor/shl/shr/ushr are infix functions
  3. inv() is a member, not infix (unary)
  4. Only on Int/Long (and unsigned types)
  5. Parenthesize when mixing with arithmetic/comparison

basics

~20 s

Kotlin has no &, |, ^, <<, >> symbols. Instead it uses named infix functions like and, or, xor, shl, shr on Int and Long. So you write a and b instead of a & b.

solid answer

~40 s

Kotlin deliberately omits Java's bitwise operator symbols (&, |, ^, ~, <<, >>, >>>). Instead, the standard library defines named infix functions on Int and Long: and, or, xor, inv (replaces ~), shl (signed left shift), shr (signed/arithmetic right shift), and ushr (unsigned/logical right shift). You call them infix-style: 0xF0 and 0x0F, 1 shl 4, x.inv(). inv() is a regular member function, not infix, because it is unary. These functions only exist on Int and Long (and the unsigned types) — Byte and Short get promoted to Int first. Because they are normal function calls, they bind with lower precedence than arithmetic, so parentheses are often needed: (a shl 2) + 1.

code

kotlin · 8 lines
kotlin
val a = 0b1100
val b = 0b1010
println(a and b) // 8  (0b1000)
println(a or b)  // 14 (0b1110)
println(a xor b) // 6  (0b0110)
println(a.inv()) // -13 (all bits flipped)
println(1 shl 4) // 16
println((a shl 1) + 1) // parentheses needed

go deeper

for a junior

Knows the symbol-to-word mapping and that you write 'a and b', 'a shl b'.

for a middle

Explains inv() is a member not infix, that only Int/Long support them, and the precedence pitfall.

for a senior

Discusses Byte conversion/masking and why parentheses matter when mixing with comparison/arithmetic.

for a principal

Reasons about API design tradeoffs of readable infix vs symbolic operators and impact on DSLs/readability.

## Why Kotlin uses words, not symbols Unlike Java/C, Kotlin does **not** reserve the punctuation operators `&`, `|`, `^`, `~`, `<<`, `>>`, `>>>` for bit manipulation. The language designers freed those tokens (some are used elsewhere or reserved), and instead expose bit operations as **named infix functions** in the standard library. An **infix function** is a function marked with the `infix` keyword that can be called without the dot and parentheses: `a shl b` is exactly `a.shl(b)`. ## The full mapping (Java symbol -> Kotlin function) - `a & b` -> `a and b` - `a | b` -> `a or b` - `a ^ b` -> `a xor b` - `~a` -> `a.inv()` (member function, NOT infix — it's unary) - `a << b` -> `a shl b` (shift left) - `a >> b` -> `a shr b` (arithmetic / signed right shift) - `a >>> b`-> `a ushr b` (logical / unsigned right shift) ## Where they live These functions are declared **only on `Int` and `Long`** (plus the unsigned `UInt`/`ULong`). There are no bitwise functions on `Byte`, `Short`, `Char`, `Float`, or `Double`. To bit-twiddle a `Byte` you first convert: `byteVal.toInt() and 0xFF`. ```kotlin val flags = 0b0000 val READ = 1 shl 0 // 1 val WRITE = 1 shl 1 // 2 val combined = READ or WRITE // 3 val hasWrite = combined and WRITE != 0 // careful with precedence! val cleared = combined and WRITE.inv() // remove WRITE bit ``` ## Precedence gotcha Because infix functions are ordinary calls, they have **lower precedence than arithmetic** and named-infix calls are left-associative with each other but bind looser than `+`, `*`, and comparison can be surprising. `combined and WRITE != 0` parses as `combined and (WRITE != 0)` which won't even compile (type mismatch); write `(combined and WRITE) != 0`. Always parenthesize when mixing shifts/masks with arithmetic or comparisons. ## Summary The semantics are identical to Java's bit operators on the same two's-complement 32/64-bit integers; only the surface syntax changed from symbols to readable words.

  • Why is inv() not an infix function?
    infix functions must take exactly one parameter besides the receiver. Bitwise NOT is unary (only a receiver, no argument), so it's a plain member function called with dot syntax: x.inv().
  • Can you use these on a Byte?
    No. Byte/Short have no bitwise functions. Convert with toInt() first, and usually mask with and 0xFF to avoid sign extension before operating.

Kotlin spells the bit operators out in words the way a calculator labels buttons 'AND'/'OR' instead of cryptic symbols.

saying these in an interview costs you the question

  • Claiming Kotlin supports & | ^ << >> operators like Java
  • Saying inv() is infix or writing 'a inv b'
  • Thinking Byte/Short have bitwise functions directly
  • Forgetting parentheses so 'mask and x != 0' fails to compile
  • Confusing shr and ushr

context

open as a page

What is the difference between shr and ushr in Kotlin, and when does it actually matter?

level: middleimportance: must knowfreq 50%

basics

~10 s

shr keeps the sign: shifting a negative number right fills the top bits with 1s. ushr ignores the sign and always fills the top bits with 0s. They only differ for negative numbers.

open as a page

Show how to implement a bit-flag set in Kotlin: setting, clearing, toggling, and testing a flag using the infix bit functions.

level: middleimportance: should knowfreq 45%

basics

~10 s

Use one bit per flag. Set a flag with 'or', clear it with 'and' of the inverted mask, toggle with 'xor', and test it with 'and' then compare to zero.

open as a page

What do countOneBits, countLeadingZeroBits, and countTrailingZeroBits do on Int/Long, and what are typical uses?

level: seniorimportance: should knowfreq 30%

basics

~10 s

They count bits in a number: countOneBits counts the 1s (population count), countLeadingZeroBits counts zeros before the first 1 from the left, and countTrailingZeroBits counts zeros after the last 1 from the right.

open as a page

What are Kotlin's unsigned integer types (UInt/ULong), how do they relate to bitwise work, and what should you watch out for?

level: seniorimportance: should knowfreq 28%

basics

~20 s

UInt and ULong are integer types that hold only non-negative values by reinterpreting the sign bit as magnitude. They make bit work clearer because right shift never sign-extends, and printing/comparison treat the value as unsigned.

open as a page