Kotlin Operators: Arithmetic, Relational, Logical, Elvis ?:, Safe Call ?. & Precedence
1. What are Arithmetic, Relational, Logical, and Assignment Operators in Kotlin?
Arithmetic Operators: Perform mathematical operations on numeric types.
+(addition),-(subtraction),*(multiplication),/(division),%(remainder).
Relational Operators: Compare values and return Boolean.
==(equal),!=(not equal),>(greater),<(less),>=(greater or equal),<=(less or equal).
Logical Operators: Combine boolean expressions.
&&(and),||(or),!(not).
Assignment Operators: Assign values, often combining arithmetic.
=(assign),+=,-=,*=,/=,%=.
Use Case: Calculations, comparisons, and updating variables.
Example: Arithmetic, Relational, Logical, and Assignment Operators
// Arithmetic, relational, logical, and assignment operators
fun main() {
val a = 10
val b = 3
// Arithmetic
val sum = a + b
val product = a * b
val remainder = a % b
// Relational
val isGreater = a > b
val isEqual = a == b
// Logical
val condition = a > 5 && b < 5
// Assignment
var total = 0
total += sum
total *= 2
println("Arithmetic: Sum=$sum, Product=$product, Remainder=$remainder")
println("Relational: a > b = $isGreater, a == b = $isEqual")
println("Logical: a > 5 && b < 5 = $condition")
println("Assignment: total after operations = $total")
}
Note: Arithmetic operators work with Int, Double, etc. Relational operators return Boolean. Assignment operators modify the variable in place.
2. What are Increment and Decrement Operators in Kotlin?
Increment (++): Increases a variable's value by 1.
- Pre-increment:
++x(increments first, then uses value). - Post-increment:
x++(uses value, then increments).
Decrement (--): Decreases a variable's value by 1.
- Pre-decrement:
--x. - Post-decrement:
x--.
Use Case: Loop counters or value adjustments.
Note: Only for numeric types (Int, Long, etc.); not for Double or Float.
Example: Increment/Decrement Operators
// Increment/decrement operators
fun main() {
var counter = 5
// Pre-increment
val preInc = ++counter
println("Pre-increment: ++counter = $preInc, counter = $counter")
// Post-increment
val postInc = counter++
println("Post-increment: counter++ = $postInc, counter = $counter")
// Pre-decrement
val preDec = --counter
println("Pre-decrement: --counter = $preDec, counter = $counter")
// Post-decrement
val postDec = counter--
println("Post-decrement: counter-- = $postDec, counter = $counter")
}
Note: Pre- operators modify the value before use; post- modify after. Useful in loops or counters.
3. What is Operator Precedence in Kotlin?
Operator Precedence: Determines the order of evaluation when multiple operators are present in an expression.
- Higher Precedence: Evaluated first (e.g.,
*before+). - Lower Precedence: Evaluated later.
- Associativity: Left-to-right or right-to-left for same precedence (e.g.,
**is right-to-left).
Common Precedence Order:
- Postfix (
++,--) - Unary (
!,+,-) - Multiplicative (
*,/,%) - Additive (
+,-) - Relational (
<,>,<=,>=) - Equality (
==,!=) - Logical AND (
&&) - Logical OR (
||) - Assignment (
=,+=, etc.)
Use Case: Ensuring correct expression evaluation (use parentheses for clarity).
Example: Operator Precedence
// Operator precedence example
fun main() {
val a = 10
val b = 3
val c = 2
// Precedence: * before +, left-to-right
val result1 = a + b * c // 10 + (3 * 2) = 16
val result2 = (a + b) * c // (10 + 3) * 2 = 26
// Precedence: ++ postfix after use
var x = 5
val y = x++ + 2 // 5 + 2 = 7, then x = 6
val z = ++x + 2 // x = 7, then 7 + 2 = 9
println("Result1 (* before +): $result1")
println("Result2 ((+ before *)): $result2")
println("y (post-increment): $y, x after: $x")
println("z (pre-increment): $z")
}
Note: * has higher precedence than +. Postfix ++ uses the current value before incrementing. Use parentheses to override precedence.
4. What are the Elvis Operator (?:) and Safe Call Operator (?.) in Kotlin?
Safe Call Operator (?.): Accesses a property or method only if the object is not null; returns null otherwise.
- Syntax:
obj?.property. - Use Case: Avoiding
NullPointerExceptionin nullable types.
Elvis Operator (?:): Provides a default value if an expression is null.
- Syntax:
expression ?: default_value. - Use Case: Assigning defaults for nullable values.
Combined: obj?.property ?: "Default".
Note: Kotlin's null safety is a key feature, unlike Java's nullable handling.
Example: Elvis and Safe Call Operators
// Elvis operator and safe call operator
fun main() {
val nullableString: String? = null
val nonNullString = "Hello"
// Safe call operator
val safeLength1 = nullableString?.length // null
val safeLength2 = nonNullString?.length // 5
// Elvis operator
val default1 = nullableString ?: "Default" // "Default"
val default2 = nonNullString ?: "Default" // "Hello"
// Combined
val combined = nullableString?.length ?: 0 // 0
println("Safe Length (null): $safeLength1")
println("Safe Length (non-null): $safeLength2")
println("Elvis (null): '$default1'")
println("Elvis (non-null): '$default2'")
println("Combined: $combined")
}
Note: ?. prevents crashes on null objects. ?: provides defaults for null results. Combined usage is common for null-safe defaults.
5. Comprehensive Example Combining All Operators
This example combines all operator categories in a single Kotlin program.
// Comprehensive Kotlin operators example
fun main() {
val a = 10
val b = 3
var c = 5
val name = "Kotlin"
// Arithmetic operators
val sum = a + b
val product = a * b
val remainder = a % b
println("Arithmetic: Sum=$sum, Product=$product, Remainder=$remainder")
// Relational operators
val isGreater = a > b
val isEqual = a == b
println("Relational: a > b = $isGreater, a == b = $isEqual")
// Logical operators
val condition = a > 5 && b < 5
println("Logical: a > 5 && b < 5 = $condition")
// Assignment operators
c += 2 // c = c + 2
c *= 3 // c = c * 3
println("Assignment: c after operations = $c")
// Increment/decrement operators
val preInc = ++c
val postDec = c--
println("Increment/Decrement: preInc=$preInc, postDec=$postDec, c=$c")
// Operator precedence
val result1 = a + b * c // * before +
val result2 = (a + b) * c
println("Precedence: a + b * c = $result1, (a + b) * c = $result2")
// Elvis and safe call operators
val nullableInt: Int? = null
val nonNullInt = 42
val safeValue1 = nullableInt ?: 0
val safeValue2 = nonNullInt ?: 0
println("Elvis: nullable ?: 0 = $safeValue1")
println("Elvis: non-null ?: 0 = $safeValue2")
}
Description:
- Demonstrates all operator categories in a single program.
- Shows precedence with
*before+. - Uses Elvis (
?:) for null-safe defaults. - Includes increment/decrement with pre/post effects.
6. Common Mistakes and Best Practices
Common Mistakes:
- Arithmetic/Assignment: Overflow with
Intoperations (useLongfor large numbers). Misusing/for integer division (usetoInt()for explicit casting). - Relational/Logical: Comparing nullable types without safe calls (
?.), causing crashes. Misinterpreting short-circuit evaluation in&&/||. - Increment/Decrement: Confusing pre/post behavior (e.g.,
++xvs.x++). Using on non-numeric types, causing compile errors. - Precedence: Forgetting precedence (e.g.,
*before+), leading to incorrect results. Not using parentheses for clarity in complex expressions. - Elvis/Safe Call: Overusing
?.unnecessarily, reducing readability. Misusing?:with non-nullable types. - General: Ignoring null safety, causing runtime errors. Not handling edge cases (e.g., division by zero).
Best Practices:
- Arithmetic/Assignment: Use explicit types (e.g.,
Longfor large numbers). Handle division by zero with checks. - Relational/Logical: Use safe calls (
?.) for nullable types. Use parentheses for complex conditions. - Increment/Decrement: Prefer pre/post based on need (e.g.,
++ifor counters). Avoid in expressions where order matters. - Precedence: Use parentheses to enforce desired order. Test expressions with sample values to verify results.
- Elvis/Safe Call: Use
?.for nullable access;?:for defaults. Combine for null-safe defaults (e.g.,obj?.value ?: 0). - General: Follow Kotlin coding conventions (e.g., camelCase for variables). Use
whenexpressions for complex conditions instead of chainedif. Test with edge cases (e.g., null, zero, negative values). Profile expressions for performance in loops.