skip to content

How do you perform bitwise operations in Kotlin given there are no &, |, ^, <<, >> symbols?

level: middleimportance: should knowfreq 45%

answer

  1. and / or / xor / inv (NOT) / shl / shr / ushr
  2. No &, |, ^, ~, <<, >>, >>> symbols
  3. inv() is a method (unary), the rest are infix
  4. shr sign-extends, ushr zero-fills
  5. Int & Long only — convert Byte/Short first

basics

~20 s

Kotlin uses named functions you write between the values, like a and b, a or b, a xor b, a shl 2, a shr 2. There are no &, |, ^, <<, or >> symbols like in Java.

solid answer

~40 s

Kotlin deliberately drops C/Java-style bitwise symbols and provides named **infix** functions on Int and Long instead: `and`, `or`, `xor`, `inv` (bitwise NOT, takes no argument), `shl` (signed left shift), `shr` (signed/arithmetic right shift, sign-extends), and `ushr` (unsigned right shift, zero-fills). So Java's `a & b` is `a and b`, `a | b` is `a or b`, `a ^ b` is `a xor b`, `~a` is `a.inv()`, `a << 2` is `a shl 2`, `a >> 2` is `a shr 2`, `a >>> 2` is `a ushr 2`. These are only defined for Int and Long (not Byte/Short — convert first). Because they are ordinary functions, they have lower precedence than arithmetic, so you often need parentheses: `a and 0xFF == x` parses surprisingly — write `(a and 0xFF) == x`.

code

kotlin · 9 lines
kotlin
fun main() {
    println(0xF0 and 0x0F)   // 0
    println(0xF0 or 0x0F)    // 255
    println(0b1010 xor 0b0110) // 12 (0b1100)
    println(0.inv())         // -1 (all bits set)
    println(1 shl 4)         // 16
    println(-8 shr 1)        // -4  (sign-extended)
    println(-8 ushr 1)       // 2147483644 (zero-filled)
}

go deeper

for a junior

Knows you use named words like and/or/shl instead of symbols.

for a middle

Maps each Java symbol to its Kotlin function and knows inv() is the unary NOT method.

for a senior

Explains shr vs ushr sign behavior, the Int/Long-only restriction, and infix precedence pitfalls.

for a principal

Discusses why named operators improve readability/safety, interop with bit-packed Java formats, and when to prefer EnumSet/typed flags over raw masks.

## No symbolic bitwise operators Unlike Java/C, Kotlin has **no** `&`, `|`, `^`, `~`, `<<`, `>>`, `>>>` symbols. Instead it exposes bitwise operations as named **infix functions** declared on `Int` and `Long`: | Java symbol | Kotlin | Meaning | |-------------|--------|---------| | `a & b` | `a and b` | bitwise AND | | `a \| b` | `a or b` | bitwise OR | | `a ^ b` | `a xor b` | bitwise XOR | | `~a` | `a.inv()` | bitwise NOT (one's complement) | | `a << n` | `a shl n` | signed left shift | | `a >> n` | `a shr n` | arithmetic (sign-extending) right shift | | `a >>> n` | `a ushr n` | logical (zero-filling) right shift | `inv` is a **method**, not infix, because it is unary: `0.inv()` -> `-1` (all bits set). ## Shift semantics ```kotlin 1 shl 4 // 16 (1 << 4) -8 shr 1 // -4 sign bit preserved (arithmetic) -8 ushr 1 // 2147483644 zero-filled (logical) 0xF0 and 0x0F // 0 0xF0 or 0x0F // 255 ``` `shr` sign-extends so negative numbers stay negative; `ushr` shifts in zeros from the left, which is why `-8 ushr 1` becomes a large positive number for `Int`. ## Defined only on Int and Long These functions exist for `Int` and `Long` only. `Byte`, `Short`, `Char` do **not** have them — you must `.toInt()` first, operate, and convert back if needed. ## Precedence gotcha Because `and`, `or`, etc. are regular infix functions, they sit at **infix precedence**, which is **lower** than arithmetic and comparison in many configurations and differs from Java's bitwise precedence. Always parenthesize when mixing with `==` or arithmetic: ```kotlin val masked = (flags and MASK) == MASK // parentheses required for intent ``` ## When to use Bitwise infix functions show up in flag/bitmask handling, low-level protocol parsing, hashing, and interop with C/Java bit-packed formats. For typed flag sets, an `enum` + `EnumSet` is usually clearer than raw masks.

  • What is the difference between shr and ushr?
    shr is arithmetic: it preserves the sign bit (sign-extends). ushr is logical: it always shifts zeros in from the left, so negatives become large positives.
  • Why is inv() a method while and/or are infix?
    inv is unary (one operand) so infix syntax wouldn't fit; and/or/xor/shl/shr/ushr are binary and declared with the infix modifier.

saying these in an interview costs you the question

  • Trying to use & or | symbols in Kotlin
  • Confusing shr and ushr semantics
  • Calling inv with an argument like a inv b
  • Assuming Byte/Short support these functions directly
  • Forgetting parentheses and getting wrong precedence

context