How do I use val and var inside Kotlin classes?

In Kotlin classes, val and var are used to declare properties.

  • val means read-only after initialization
  • var means mutable / can be reassigned

Basic example

class User {
    val id: Int = 1
    var name: String = "Alice"
}

Usage:

fun main() {
    val user = User()

    println(user.id)      // 1
    println(user.name)    // Alice

    user.name = "Bob"     // OK: name is var

    // user.id = 2        // Error: id is val
}

val inside a class

Use val when the property should not be reassigned after it gets a value.

class Product {
    val sku: String = "ABC-123"
}

You can read it:

val product = Product()
println(product.sku)

But you cannot assign a new value:

// product.sku = "XYZ-999" // Not allowed

var inside a class

Use var when the property can change.

class Counter {
    var count: Int = 0

    fun increment() {
        count++
    }
}

Usage:

fun main() {
    val counter = Counter()

    counter.increment()
    counter.increment()

    println(counter.count) // 2
}

Declaring properties in the constructor

A common Kotlin style is to put properties directly in the class constructor.

class User(
    val id: Int,
    var name: String
)

This creates a class with:

  • a read-only id
  • a mutable name

Usage:

fun main() {
    val user = User(1, "Alice")

    println(user.id)
    println(user.name)

    user.name = "Bob" // OK

    // user.id = 2    // Error
}

Important distinction

If you write this:

val user = User(1, "Alice")

The variable user itself cannot point to another User, because it is a val.

But if the object has var properties, those properties can still change:

val user = User(1, "Alice")

user.name = "Bob" // OK, because name is var

// user = User(2, "Charlie") // Error, because user is val

So:

val user

means the reference cannot be reassigned.

var name

inside the class means the property can be changed.

Rule of thumb

Use val by default, and only use var when the value really needs to change.

class Person(
    val birthYear: Int,
    var displayName: String
)

Here, birthYear probably should not change, but displayName might.

How do I use var effectively in local variable declarations?

Using var effectively in local variable declarations in Java is primarily about balancing conciseness and clarity. Below are guidelines, best practices, and tips:


When to Use var

  1. Obvious Initializer Types
    Use var when the variable’s initializer makes its type clear:

    var name = "Alice"; // Clearly a String
    var age = 25;       // Clearly an int
    
  2. Reducing Verbosity
    Use var for complex or verbose type declarations:

    var map = new HashMap<String, List<String>>(); // Avoids repeating type
    var stream = Files.lines(Paths.get("data.txt")); // Cleaner syntax
    
  3. In Loops and Streams
    For-each or stream operations where the type is deduced from the context:

    for (var fruit : fruits) { // fruit is automatically inferred as a String
       System.out.println(fruit);
    }
    
    var filtered = list.stream()
                      .filter(element -> element.length() > 3)
                      .toList();
    
  4. Try-With-Resources
    Use var in resource declarations to simplify code:

    try (var reader = Files.newBufferedReader(Paths.get("file.txt"))) {
       System.out.println(reader.readLine());
    }
    

When to Avoid var

  1. Ambiguity or Complexity
    Avoid var when the type is not obvious from the initializer:

    var data = process(); // What is the type of 'data'? Unclear
    
  2. Primitive Numeric Values
    Be cautious with numeric literals as var might infer incorrect types:

    var number = 123;      // int by default
    var bigNumber = 123L;  // Prefer explicitly declaring long if intent matters
    
  3. Null Initializers
    var cannot be used with null initializers:

    // var x = null; // Compilation error
    
  4. Wide or Generic Types
    Avoid using var with overly generic types like Object or unchecked types:

    var object = methodReturningObject(); // Reduces clarity
    
  5. Public API Layers
    Avoid var in public APIs, as it may reduce readability or intent clarity:

    void process(var input); // Not valid for method parameters
    
  6. Excessively Chained Operations
    Avoid using var if the resulting type from operations (like streams) is unclear without deep inspection:

    var result = data.stream()
                        .filter(x -> x.isActive())
                        .map(Object::toString)
                        .toList(); // What type is 'result'? May not be obvious
    

Best Practices

  1. Good Naming Conventions
    Pair var with meaningful variable names to ensure intent is clear:

    var customerName = "John";  // Better than 'var name'
    var processedData = processData(file); // Descriptive name clarifies type
    
  2. Limits on Scope
    Use var for small, contained scopes where type inference is straightforward:

    var total = 0;
    for (var i = 0; i < 10; i++) {
       total += i;
    }
    
  3. Iterative Refactoring
    Start with explicit types during implementation, then refactor to var where appropriate for readability:

    // Explicit type during initial implementation
    List<String> items = List.of("One", "Two", "Three");
    
    // Refactored for conciseness
    var items = List.of("One", "Two", "Three");
    
  4. Use Type-Specific Factory Methods
    When using var with factories or APIs, ensure the API return type is clear:

    var list = List.of("Apple", "Orange"); // List<String>
    var map = Map.of(1, "One", 2, "Two"); // Map<Integer, String>
    

Summary of Guidelines

  • Use var for:
    • Clear, concise, and obvious initializers.
    • Reducing verbosity in long type declarations.
    • Improving readability in loops and resource management blocks.
    • Avoiding repetitive or redundant type definitions.
  • Avoid var when:
    • The type cannot be inferred easily, or it reduces readability.
    • The initializer returns a generic, ambiguous, or raw type.
    • Explicit types are necessary to convey specific intent (e.g., long vs int).
  • Key Rule of Thumb:
    Use var to improve readability and clarity, not at the cost of them.


By adopting these practices, you can harness the power of var to write more concise, readable, and maintainable code effectively while still preserving clarity and intent.

How do I declare variables in Kotlin using val and var?

In Kotlin, you can declare variables using two keywords: val and var. The choice between them depends on whether you want the variable to be immutable or mutable.

1. Declaring Immutable (Read-Only) Variables with val

  • Use val when the value of the variable will not change after it is initialized.
  • The variable becomes read-only (similar to final in Java).

Syntax:

val variableName: Type = value

Example:

val name: String = "John"
// name = "Jane" // This will cause a compilation error

Kotlin can also infer the type automatically if it can be deduced from the value:

val age = 30 // Kotlin infers that 'age' is of type Int

2. Declaring Mutable Variables with var

  • Use var when the value of the variable can change during the program’s lifecycle.
  • The variable becomes mutable.

Syntax:

var variableName: Type = value

Example:

var age: Int = 25
age = 26 // This is allowed because 'age' is mutable

Similarly, type inference can be used:

var firstName = "John" // Kotlin infers that it's a String
firstName = "Jane" // Allowed because 'firstName' is mutable

Key Differences Between val and var

Aspect val var
Mutability Immutable (read-only) Mutable (modifiable)
Reassignment Not allowed Allowed
Use Case When a value should not change When a value needs to change

Additional Examples

Example 1: Declaring Variables with Explicit Types

val city: String = "New York"
var temperature: Double = 25.5
temperature = 30.0 // This is valid

Example 2: Using Type Inference

val country = "USA" // 'country' is inferred to be a String
var isRaining = false // 'isRaining' is inferred to be a Boolean
isRaining = true // Valid because 'isRaining' is mutable

Important Notes

  • For complex objects (like lists and maps), val does not mean the contents of the object cannot change; it just means the variable reference cannot be reassigned.
val list = mutableListOf(1, 2, 3)
list.add(4) // Allowed, because the contents of the list are mutable
// list = mutableListOf(5, 6, 7) // Not allowed, because 'list' is immutable
  • Try to prefer val over var wherever possible to make your code safer and easier to reason about.

How to create cleaner code with type inference in Java 10

Type inference was introduced in Java 10 with the new var keyword, enabling developers to declare local variables without explicitly specifying their type. This feature can help create cleaner, more concise code by reducing boilerplate, though it should be used judiciously to maintain code readability.

Here’s a guide on how to use type inference effectively and write cleaner code in Java 10 and later:


1. Use var for Local Variables

The var keyword allows you to declare local variables without explicitly stating their type. The compiler infers the type based on the expression assigned to the variable. Here’s how it works:

Example:

var message = "Hello, World!"; // Compiler infers this as String
var count = 42;                // Compiler infers this as int
var list = new ArrayList<String>(); // Compiler infers this as ArrayList<String>

System.out.println(message);  // Hello, World!
System.out.println(count);    // 42

Benefits:

  • Eliminates redundancy. For instance:
List<String> list = new ArrayList<>();

becomes:

var list = new ArrayList<String>();

2. Use var in Loops

In for-each loops and traditional for-loops, var can simplify the code:

Example:

var numbers = List.of(1, 2, 3, 4, 5);
for (var num : numbers) {
    System.out.println(num); // Iterates through the numbers
}

Benefits:

  • Avoids unnecessary type declarations while maintaining readability.

3. Use var with Streams and Lambdas

var integrates well with Java Streams and Lambda expressions to reduce verbosity:

Example:

var numbers = List.of(1, 2, 3, 4, 5);
var result = numbers.stream()
                    .filter(n -> n % 2 == 0)
                    .map(n -> n * 2)
                    .toList();

System.out.println(result); // [4, 8]

When working with complex streams, var can make code shorter and easier to follow.


4. Restrictions on var

While var is versatile, there are some limitations and rules:

  • Only for Local Variables: var can only be used for local variables, loop variables, and indexes, not for class fields, method parameters, or return types.
  • Compiler Must Infer Type: You must assign a value to a var. For example, the following won’t work:
var uninitialized; // Error: cannot use 'var' without initializer
  • Anonymous Classes: Avoid overuse with anonymous classes to maintain clarity.

5. Maintain Readability

While var can simplify code, readability should always be a priority. Overusing var can obscure the code’s intent, especially when dealing with complex types:

Example of Overuse:

var map = new HashMap<List<String>, Set<Integer>>(); // Hard to understand

In such cases, it’s better to use explicit types.


6. Good Practices

  • Use var for Obvious Types:
var name = "John Doe"; // Obviously String
  • Avoid var for Ambiguous Types:
// Original:
var data = performOperation(); // What is the return type?
// Better:
List<String> data = performOperation();
  • Avoid Excessive Chaining:

    Using var with complex chains can make debugging harder. Be explicit when needed.


7. Refactoring Example

Here’s how you can refactor code for better clarity using var:

Before Refactoring:

ArrayList<String> names = new ArrayList<>();
HashMap<String, Integer> nameAgeMap = new HashMap<>();

After Refactoring:

var names = new ArrayList<String>();
var nameAgeMap = new HashMap<String, Integer>();

This is concise without sacrificing clarity.


Conclusion

Type inference with var in Java 10 improves code conciseness and readability when used appropriately. To ensure cleaner code:

  • Use var for obvious and readable scenarios.
  • Avoid using var when the inferred type is unclear or ambiguous.
  • Focus on balancing conciseness with the need for maintainable and self-explanatory code.

How to Use the var Keyword for Local Variable Type Inference in Java 10

In Java 10, the var keyword was introduced to allow local variable type inference. This means that when you declare a local variable, the compiler automatically infers its type based on the initialization value. This improves readability and reduces boilerplate code, especially when working with complex types, without compromising type safety.

Here’s a guide on how to use the var keyword:


1. Declaring and Initializing Local Variables with var

The var keyword replaces explicitly specifying the type while declaring local variables. However, you must initialize the variable at the time of declaration, as the compiler needs an expression to infer the type.

// Example of using var keyword
var message = "Hello, Java 10!";  // Inferred as String
var number = 10;                 // Inferred as int
var list = List.of("apple", "banana", "orange"); // Inferred as List<String>

Here, the type of each variable is deduced by the Java compiler:

  • message: String
  • number: int
  • list: List<String>

2. Scopes Where var Can Be Used

The var keyword can only be used in specific contexts:

a. Local Variables in Methods

public void demoVarUsage() {
    var name = "John";      // Inferred as a String
    var age = 30;           // Inferred as an int

    System.out.println(name + " is " + age + " years old.");
}

b. Loop Variables (for-each or traditional for-loops)

// for each loop
var items = List.of("A", "B", "C");
for (var item : items) {
    System.out.println(item);  // item inferred as String
}

// traditional for loop
for (var i = 0; i < 10; i++) {
    System.out.println(i);     // i inferred as int
}

c. Local Variables in Lambda Expressions

var lambda = (String x, String y) -> x + y; // Explicit lambda types
System.out.println(lambda.apply("Java", "10"));

3. Restrictions on var Usage

While var is versatile, there are limitations:

  1. Cannot Be Used Without Initialization
    var name; // Compilation error: cannot infer type
    name = "John";
    
  2. Cannot Be Used with Null Initializer
    var something = null; // Compilation error
    
  3. Cannot Be Used as a Method Parameter, Field, or Return Type
    The var keyword is limited to local variables inside methods and blocks, as well as loop variables. It cannot be used:

    • As a return type of method.
    • As a field in a class.
    • As a method parameter.
    public var getName() {    // Compilation error: 'var' is not allowed here
       return "Java";
    }
    
  4. Cannot Mix Explicit Types and var
    var name = "John", age = 30; // Compilation error
    var name = "John";
    var age = 30;               // Declare separately
    
  5. Cannot Infer Ambiguous Types
    var result = process();  // If `process()` returns Object, type can't be narrowed.
    

4. Advantages of Using var

  • Improved Code Readability: Reduces verbosity for complex types.
    var map = new HashMap<String, List<Integer>>(); // Cleaner than HashMap<String, List<Integer>>
    
  • Consistent with Type Inference: Makes Java more modern and closer to languages like Kotlin, Scala, or C#.


5. Best Practices

  • Avoid overusing var to ensure code remains understandable.
  • Use meaningful names for variables to compensate for the lack of explicit type.
  • Use var only when the type is obvious from the context.

Example Code:

package org.kodejava.basic;

import java.util.List;

public class VarExample {
   public static void main(String[] args) {
      // Using var for various local variable declarations
      var name = "Alice";                        // String
      var age = 25;                              // int
      var fruits = List.of("Apple", "Banana");  // List<String>

      System.out.println(name + " likes " + fruits);

      for (var fruit : fruits) {
         System.out.println(fruit);  // Inferred as String
      }

      var sum = add(10, 20);  // Inferred as int
      System.out.println("Sum: " + sum);
   }

   private static int add(int a, int b) {
      return a + b;
   }
}

Output:

Alice likes [Apple, Banana]
Apple
Banana
Sum: 30

The var keyword is a helpful addition, especially for simplifying local variables with inferred types, keeping code concise and readable while retaining type safety!