skip to content

Basic Types

Kotlin's built-in value types and how you write them down: numeric literals, unsigned variants, Boolean, Char and String, interpolation, and the conversions you must spell out. The recurring theme is that Kotlin refuses the implicit coercions many languages allow.

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

explore

questions

30

In Kotlin, is Char a numeric type? How do you get the numeric (code-point) value of a Char, and how do you turn a digit Char like '7' into the Int 7?

level: juniorimportance: must knowfreq 70%

answer

  1. Char is not a Number
  2. .code for the integer code point
  3. .digitToInt() turns '7' into 7
  4. '7'.code == 55, not 7
  5. Char(n) / n.toChar() to build a Char

basics

~10 s

No. A Char in Kotlin is a character, not a number. To get its underlying code you use .code, and to turn a digit character like '7' into the number 7 you use .digitToInt().

solid answer

~40 s

Kotlin's Char is a distinct type, not an Int, and there is no implicit conversion between them (unlike Java where a char auto-promotes to int in arithmetic). To get the UTF-16 code unit you call ch.code (returns Int); to build a Char from a code you use Char(intValue) or intValue.toChar(). For digit characters, '7'.digitToInt() returns 7 (and '7'.digitToIntOrNull() returns 7 or null for non-digits), while 7.digitToChar() goes the other way. Comparisons and ranges work directly ('a'..'z'), and 'a' + 1 yields a Char ('b') because Char has plus(Int): Char operators. Doing '7' - '0' also works (Char.minus(Char) returns Int) but .digitToInt() is the idiomatic, readable choice.

code

kotlin · 6 lines
kotlin
val c = '7'
println(c.code)          // 55  (UTF-16 code unit)
println(c.digitToInt())  // 7   (numeric value)
println('a' + 1)         // b   (Char + Int -> Char)
println('9' - '0')       // 9   (Char - Char -> Int)
println(Char(65))        // A   (build Char from code)

go deeper

for a junior

Knows Char isn't a number and that .code and .digitToInt() exist and differ.

for a middle

Explains the deprecation of toInt(), the Char(Int) constructor, and digitToIntOrNull/radix overloads.

for a senior

Discusses Char operators (plus/minus), '9' - '0' vs digitToInt() readability, and UTF-16 code-unit semantics.

for a principal

Frames the no-implicit-widening design as deliberate type-safety, and notes surrogate-pair caveats: a Char is a UTF-16 unit, not a full Unicode code point.

## Char is its own type In Kotlin, `Char` represents a single 16-bit Unicode (UTF-16) code unit. Crucially, **`Char` is NOT a subtype of `Int` and is NOT a `Number`**. Unlike Java — where `char` silently promotes to `int` in arithmetic so `'a' + 1` is the int `98` — Kotlin keeps them separate. This prevents a whole class of accidental-numeric bugs. ## Getting the numeric value: `.code` To read the integer code point of a character, use the `code` property: ```kotlin val c: Char = 'A' val n: Int = c.code // 65 val back: Char = Char(66) // 'B' (constructor from Int) val back2: Char = 65.toChar() // 'A' ``` Historically Kotlin had `Char.toInt()` / `Int.toChar()`, but `Char.toInt()` is **deprecated** — prefer `.code` and the `Char(Int)` constructor for clarity. ## Digit characters: `.digitToInt()` The character `'7'` has code point 55, NOT 7. To get the *numeric value* of a digit character, use: ```kotlin '7'.digitToInt() // 7 'F'.digitToInt(16) // 15 (radix overload) 'x'.digitToIntOrNull() // null (not a digit) 7.digitToChar() // '7' ``` `digitToInt()` throws `IllegalArgumentException` if the char is not a digit in the given radix; the `...OrNull` variant returns `null` instead. ## Char arithmetic that IS allowed Char defines a small set of operators: - `Char.plus(Int): Char` and `Char.minus(Int): Char` → shift by code: `'a' + 1 == 'b'`. - `Char.minus(Char): Int` → distance between codes: `'9' - '0' == 9`. So `'9' - '0'` is a valid alternative to `.digitToInt()`, but the named function is more readable and locale-safe. ## Ranges and comparison Char is `Comparable<Char>` and supports ranges: ```kotlin for (ch in 'a'..'z') print(ch) val isLetter = c in 'A'..'Z' ``` Useful helpers: `c.isDigit()`, `c.isLetter()`, `c.isWhitespace()`, `c.lowercaseChar()`, `c.uppercaseChar()`. ## Key takeaways - `Char` ≠ `Int`; no implicit widening. - Code value → `.code`; build from code → `Char(n)` / `n.toChar()`. - Digit value → `.digitToInt()` / `.digitToIntOrNull()`; reverse → `Int.digitToChar()`.

  • What is the difference between '7'.code and '7'.digitToInt()?
    '7'.code is the UTF-16 code point (55); '7'.digitToInt() is the numeric value of the digit (7). They differ by the code of '0' (48).
  • Why is Char.toInt() discouraged now?
    It was ambiguous and is deprecated; use .code for the code point. This makes the intent explicit and avoids confusion with digit value.

A Char is like a labeled key on a keyboard — it has a position number (.code) but the label '7' isn't the same as the quantity seven.

saying these in an interview costs you the question

  • Claiming '7'.code returns 7
  • Saying Char is a subtype of Int / a Number
  • Assuming Java-style implicit char-to-int promotion in arithmetic
  • Using toInt()/toChar() and being unaware .code is preferred
  • Thinking digitToInt() works on any letter (it throws unless it's a valid digit)

context

open as a page

In Kotlin, why does `val l: Long = anInt` fail to compile, and how do you fix it?

level: juniorimportance: must knowfreq 75%

basics

~10 s

Kotlin never auto-converts a smaller number type to a bigger one. An Int is not a Long. You must convert it yourself by calling a conversion function, for example anInt.toLong().

open as a page

What numeric types does Kotlin provide for whole and fractional numbers, and how do you write integer literals in hex, binary, with separators, and as a Long?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Whole numbers: Byte, Short, Int, Long. Fractional: Float, Double. Write hex as 0xFF, binary as 0b1010, use underscores like 1_000 for readability, and add an L suffix (1L) to make a Long.

open as a page

What is a triple-quoted (raw) string in Kotlin, and how does it differ from a regular double-quoted string?

level: juniorimportance: must knowfreq 55%

basics

~10 s

A triple-quoted string is wrapped in three quotes. It can span many lines and treats backslashes as plain text, so you don't need escape codes like \n or \.

open as a page

What are Kotlin string templates, and what is the difference between the $name and ${expression} forms?

level: juniorimportance: must knowfreq 80%

basics

~10 s

String templates let you put values inside a string. Use $name to insert a variable, and ${...} to insert a whole expression. Kotlin replaces them with the text of the value.

open as a page

Explain how Kotlin's String stores characters: is it mutable, how do you read a character at a position, and what happens when you 'modify' a String?

level: middleimportance: must knowfreq 65%

basics

~20 s

A String is an immutable sequence of characters. You read a character with square brackets, like s[0]. You can't change a String in place; any 'change' creates a brand-new String and leaves the original untouched.

open as a page

Explain how Kotlin decides whether a numeric literal is Int or Long, and how that affects an expression like `val ms = 60 * 60 * 1000 * 24 * 365`.

level: middleimportance: must knowfreq 60%

basics

~20 s

An unsuffixed integer literal is Int if it fits in 32 bits, otherwise Long. If every operand in a multiplication is Int, the math is done in Int and can overflow silently. Add an L to one operand to force Long math.

open as a page

What does trimIndent() do to a multi-line raw string, and how does it decide how much to remove?

level: middleimportance: must knowfreq 50%

basics

~10 s

trimIndent() finds the common leading whitespace shared by all non-blank lines and removes that much from every line, so indented source code produces clean, left-aligned text.

open as a page

How do you convert between signed and unsigned types in Kotlin, and what are the bit-level gotchas?

level: middleimportance: must knowfreq 28%

basics

~10 s

Kotlin never converts numbers implicitly, so you call explicit functions like toUInt(), toInt(), toULong(), or toUByte(). Converting reinterprets the bits, which can flip a negative signed number into a large unsigned one.

open as a page

What are Kotlin's unsigned integer types, and how do you write unsigned literals?

level: juniorimportance: should knowfreq 35%

basics

~20 s

Kotlin has four unsigned integer types: UByte, UShort, UInt, and ULong. They only hold zero and positive numbers. You write unsigned literals by adding the suffix u (or uL for ULong), like 42u or 0xFFu.

open as a page

What is the difference between Kotlin's &&/|| and the and/or infix functions on Boolean? When does short-circuit evaluation matter?

level: middleimportance: should knowfreq 55%

basics

~20 s

&& and || are short-circuit: they stop early once the result is known, so the right side may not run. and/or always evaluate both sides. Use && / || when the right side has a side effect or could crash if reached.

open as a page

When you write `1 + 2L` or `3 + 0.5`, no `.toLong()`/`.toDouble()` call appears. How does Kotlin produce the wider result type without implicit widening of variables?

level: middleimportance: should knowfreq 50%

basics

~20 s

Math operators like + come in many versions, one per type combination. Int.plus(Long) returns a Long. So the operator itself handles the mix and gives the wider result — it's not the variable being auto-converted.

open as a page

What does `300.toByte()` return, and what general rule governs narrowing conversions like `toByte()`, `toShort()`, and `Long.toInt()`?

level: middleimportance: should knowfreq 55%

basics

~10 s

Narrowing keeps only the low bits and throws nothing. 300.toByte() is 44 because 300 doesn't fit in a byte, so the extra bits are dropped. Always check ranges before narrowing.

open as a page

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%

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.

open as a page

How does trimMargin() work, and when would you choose it over trimIndent()?

level: middleimportance: should knowfreq 38%

basics

~10 s

trimMargin() removes everything up to and including a marker character (a pipe | by default) at the start of each line. You mark where each line should begin, giving explicit control.

open as a page

How do you put a literal dollar sign in a Kotlin string, including inside a raw (triple-quoted) string?

level: middleimportance: should knowfreq 55%

basics

~10 s

Write ${'$'} to output a real dollar sign. In normal strings you can also use the escape $, but in triple-quoted strings backslashes are literal, so ${'$'} is the only way.

open as a page

How are template expressions evaluated and converted to text? Walk through toString() invocation, null, and custom types.

level: middleimportance: should knowfreq 50%

basics

~10 s

Kotlin runs the expression, then calls its toString() to get text. A null becomes the word "null". For your own classes, the result depends on the toString() you write (or the default one).

open as a page

How are Kotlin's unsigned types implemented under the hood, and what does that mean for performance and the JVM signatures?

level: middleimportance: should knowfreq 30%

basics

~20 s

Each unsigned type is an inline value class that wraps the matching signed type. The JVM has no unsigned integers, so a UInt is really an Int at runtime, reinterpreted as unsigned. There's usually no boxing, so it's fast.

open as a page

How does a nullable Boolean? behave in conditions, and what does the expression `flag == true` accomplish compared to just `flag`?

level: seniorimportance: should knowfreq 45%

basics

~20 s

A Boolean? can be true, false, or null, so you can't use it directly in an if. Writing flag == true safely treats null as 'not true' (false). It's a common, null-safe way to check a nullable flag.

open as a page

How do explicit conversions interact with nullable numeric types and with Char, e.g. converting `Int?` to `Long?` and getting an Int from a Char?

level: seniorimportance: should knowfreq 35%

basics

~20 s

For a nullable number you call the conversion with a safe call: n?.toLong(). For a Char, you don't use toInt() anymore — you use the code property to get its numeric code, and Char(code) to go back.

open as a page

Kotlin numbers like Int are classes, not language primitives. How does the compiler represent them on the JVM, and when does boxing into java.lang wrappers happen?

level: seniorimportance: should knowfreq 45%

basics

~20 s

In source you always write the class name Int. On the JVM the compiler uses the cheap primitive int where it can, but switches to the boxed java.lang.Integer when the value must be nullable (Int?) or used as a generic type argument, like in a List<Int>.

open as a page

Why are raw strings the preferred way to write regex patterns and Windows file paths in Kotlin? Show the difference.

level: seniorimportance: should knowfreq 33%

basics

~10 s

Regex and Windows paths contain many backslashes. In a normal string you must double each one, which is noisy and error-prone. Raw strings keep backslashes literal, so the pattern reads exactly as intended.

open as a page

Inside a raw string, how do you include a literal $ and a literal sequence of three double quotes, given templates still expand?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Templates run even in raw strings, so a bare $ before a name becomes a variable. Write a literal dollar with ${'$'}. Three quotes are hard to embed; use a template like ${"""""} to insert them.

open as a page

What are the parsing edge cases of string templates — adjacent identifiers, $ before non-identifier characters, and nested expressions?

level: seniorimportance: should knowfreq 35%

basics

~20 s

The short form grabs the longest identifier after $, which can swallow characters you didn't mean. A $ followed by a space or symbol is literal. Braces let you nest other strings and expressions safely.

open as a page

When should you NOT use string templates, and what risks (injection, locale, logging) do they introduce?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Templates just paste text, so don't use them to build SQL, HTML, or shell commands from user input — that's an injection risk. Also be careful logging sensitive data and that number formatting may not match a user's locale.

open as a page

How does arithmetic, comparison, and overflow behave for Kotlin unsigned types, and where do mixed signed/unsigned operations fail?

level: seniorimportance: should knowfreq 22%

basics

~20 s

Unsigned arithmetic wraps around silently on overflow, never throwing. Division and comparison use unsigned rules, so big values aren't treated as negative. You cannot freely mix signed and unsigned in one expression; you must convert first.

open as a page

What are the rules and edge cases for digit underscores and radix prefixes in Kotlin numeric literals? Which forms are illegal and why?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

Underscores are just visual separators between digits and don't change the value. They can't be at the start, the end, or right next to the 0x/0b prefix. Hex is 0x, binary is 0b, and there is no octal.

open as a page

A Kotlin Char is a single 16-bit unit. What problems arise with characters outside the Basic Multilingual Plane (e.g. emoji), and how do String.length and indexing behave for them?

level: seniorimportance: nice to knowfreq 25%

basics

~20 s

A Kotlin Char only holds 16 bits, but some characters (like many emoji) need more. Those are stored as two Chars (a 'surrogate pair'), so String.length counts them as 2 and a single index gives you only half a character.

open as a page

Kotlin removed implicit numeric widening that Java has. Walk through the design rationale and the tradeoffs this creates for everyday code.

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Kotlin makes conversions explicit so number-type changes are visible and you don't get surprising silent ones. The cost is more .toLong() calls, but Kotlin softens that with smart literals and overloaded operators so normal code rarely suffers.

open as a page

When should a team adopt Kotlin unsigned types in a public API, and what are the trade-offs versus signed Int with manual masking?

level: principalimportance: nice to knowfreq 12%

basics

~20 s

Use unsigned types when a value is naturally non-negative and may exceed the signed maximum, like bit masks or binary protocol fields. Avoid them in public APIs shared with Java, because interop and tooling get awkward; signed Int with masking is often safer there.

open as a page