Pointers, functions, and execution control in Go vs Java | Types - Language

Series: Go for Java Developers — analyzing pointer, closures, defer, panic/recover

In this article, we will analyze how Go manages the state and lifecycle of functions. A feature of Go is the ease of working with pointers without complex arithmetic, powerful closures, and built-in mechanisms for safe termination and error handling. For a Java developer, this allows understanding the differences with familiar references, exceptions, and lambdas.


Pointers and value vs reference semantics

Pointers in Go allow working with variable addresses, but without pointer arithmetic. This is safer than in C, and analogous to references in Java, but with explicit syntax.

Example of a function with a pointer

// Go
func update(x *int) {
    *x = 10
}

func main() {
    a := 5
    update(&a)
    fmt.Println(a) // 10
}

Java equivalent:

// Java
class Main {
    static void update(IntegerWrapper x) {
        x.value = 10;
    }

    public static void main(String[] args) {
        IntegerWrapper a = new IntegerWrapper(5);
        update(a);
        System.out.println(a.value); // 10
    }
}

class IntegerWrapper {
    int value;
    IntegerWrapper(int v) { value = v; }
}
Go makes references explicit through pointers. Value receiver works with a copy, pointer receiver works with the original.

Function literal / Closure — functions as objects

In Go, functions are full-fledged first-class objects. Closures allow maintaining state between calls.

Example of closure

// Go
func counter() func() int {
    i := 0
    return func() int {
        i++
        return i
    }
}

func main() {
    c := counter()
    fmt.Println(c()) // 1
    fmt.Println(c()) // 2
}

Java equivalent (via lambda with object)

// Java
import java.util.function.Supplier;

class Counter {
    int i = 0;
    Supplier<Integer> counter() {
        return () -> ++i;
    }

    public static void main(String[] args) {
        Counter obj = new Counter();
        Supplier<Integer> c = obj.counter();
        System.out.println(c.get()); // 1
        System.out.println(c.get()); // 2
    }
}
Closure allows Go to keep the internal state of a function without creating a separate class. This is convenient for counters, generators, callbacks.

Defer — deferred execution (analogous to try-with-resources)

Defer executes a function at the end of the current block. This is convenient for closing resources or freeing memory.

Example of defer

// Go
func main() {
    defer fmt.Println("world")
    fmt.Println("hello")
}
// Output:
// hello
// world

Java equivalent (try-with-resources / finally)

// Java
public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("hello");
        } finally {
            System.out.println("world");
        }
    }
}
// Output:
// hello
// world
Defer is convenient for clean code, especially with files, sockets, and other resources.

Panic / Recover — handling critical errors

In Go, panic is analogous to unchecked exceptions in Java, but more controllable. Recover allows intercepting the panic and continuing execution.

Example panic/recover

// Go
func safeDivide(a, b int) (result int) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered:", r)
            result = 0
        }
    }()
    if b == 0 {
        panic("division by zero")
    }
    return a / b
}

func main() {
    fmt.Println(safeDivide(10, 0)) // Recovered: division by zero \n 0
}

Java equivalent

// Java
public class Main {
    static int safeDivide(int a, int b) {
        try {
            return a / b;
        } catch (ArithmeticException e) {
            System.out.println("Recovered: " + e.getMessage());
            return 0;
        }
    }

    public static void main(String[] args) {
        System.out.println(safeDivide(10, 0));
    }
}
Panic and recover allow for graceful handling of critical errors without breaking the entire flow of execution.

Comparison of key concepts Go ↔ Java

Concept Go Java Comment
Pointers explicit pointer, no arithmetic object references Go makes the reference explicit
Value vs reference semantics value receiver / pointer receiver primitive vs object reference Pointer receiver modifies the original
Closure function literal, captures vars lambda / anonymous class Can store function state
Defer deferred execution try-with-resources / finally Convenient for releasing resources
Panic / Recover runtime error / catch unchecked exception / try-catch Catching critical errors

Practical Output

For a Java developer learning Go, it is important to understand:

  • Pointers in Go are safe and explicit, without arithmetic
  • Value vs pointer receiver allows controlling data changes
  • Functions are first-class objects, closures maintain state
  • Defer replaces finally or try-with-resources
  • Panic / recover – built-in handling of critical errors

These mechanisms make Go code compact, safe, and predictable, while maintaining control over state and resources without the bulky class structure of Java.


🌐 На русском
Total Likes:0

Оставить комментарий

My social media channel
By sending an email, you agree to the terms of the privacy policy

Useful Articles:

Channel direction and select patterns in Go vs Java | Patterns, idioms, and best practices in Go
← Related articles: Context, propagation and cancellation patterns in Go vs Java | Patterns, idioms and best practices in Go 1. Channel direction — channel directions In Go, channels can be one-w...
Generics, Reflection and Channels - Go vs Java | Types - Language
In this article we will analyze advanced type system features in Go: generics (type parameters), reflection, and channel types for concurrency. We will compare Go and Java approaches, so Java develope...
Reflection on why the completeness of knowledge is unattainable and how to build a personal architecture of professional growth. Every developer has at least once thought: “How to keep up with ev...

New Articles:

Concurrency is not about “starting many threads”. It’s about agreements between them. Imagine a restaurant kitchen: — cooks (threads / goroutines) — orders (tasks) — and the main question: how do th...
When HashMap starts killing production: the engineering story of ConcurrentHashMap
Imagine a typical production service. 32 CPU hundreds of threads configuration / session / rate limits cache tens of thousands of operations per second And somewhere inside — a regular Map. At first...
Zero Allocation in Java: what it is and why it matters
Zero Allocation — is an approach to writing code in which no unnecessary objects are created in heap memory during runtime. The main idea: fewer objects → less GC → higher stability and performance. ...
Fullscreen image