Struct, methods and interfaces in Go vs Java | Types - Language

Series: Go for Java Developers — exploring struct, interface, receiver types and type embedding

In this article, we will examine how types architecture is built in Go. For a Java developer, this is especially important, as Go does not use classes in the conventional sense, and types and methods are organized differently. We will compare the Go ↔ Java approach to understand how structures, methods, and interfaces are implemented.


Struct — basic data structure in Go

Struct in Go is an analogue of a class without methods. It stores data, but behavior is added through methods.

Example of struct

// Go
type User struct {
    Name string
}

func main() {
    u := User{Name: "Alice"}
    fmt.Println(u.Name)
}

Java equivalent:

// Java
class User {
    String name;
}

public class Main {
    public static void main(String[] args) {
        User u = new User();
        u.name = "Alice";
        System.out.println(u.name);
    }
}
Struct is just a container for data. Methods are added separately. No need to inherit or create classes to store fields.

Methods and receiver types

In Go a method is bound to a type via receiver. Receiver can be a value or a pointer.

Example of a method with a value

// Go
type User struct {
    Name string
}

func (u User) Print() {
    fmt.Println(u.Name)
}

func main() {
    u := User{Name: "Alice"}
    u.Print()
}

Java equivalent:

// Java
class User {
    String name;

    void print() {
        System.out.println(name);
    }
}

public class Main {
    public static void main(String[] args) {
        User u = new User();
        u.name = "Alice";
        u.print();
    }
}

Receiver type affects behavior:

  • Value receiver — the method works with a copy
  • Pointer receiver — the method can change the original data

Pointer receiver example

// Go
func (u *User) Rename(newName string) {
    u.Name = newName
}

func main() {
    u := User{Name: "Alice"}
    u.Rename("Bob")
    fmt.Println(u.Name) // Bob
}
For Java developers: Pointer receiver is similar to passing an object by reference, Value receiver — like passing primitives or copies of objects.

Interface — implicit implementation

In Go, interfaces are implemented automatically. If a type has methods that match the interface — it implements it. No implements is needed.

Example of an interface

// Go
type Printer interface {
    Print()
}

type User struct {
    Name string
}

func (u User) Print() {
    fmt.Println(u.Name)
}

func main() {
    var p Printer = User{Name: "Alice"}
    p.Print()
}

Java equivalent:

// Java
interface Printer {
    void print();
}

class User implements Printer {
    String name;

    public void print() {
        System.out.println(name);
    }
}

public class Main {
    public static void main(String[] args) {
        Printer p = new User();
        ((User)p).name = "Alice";
        p.print();
    }
}
Go makes the interface system very flexible. You can implement an interface without explicit declaration.

Type embedding — composition instead of inheritance

Go does not use inheritance like in Java. For code reuse, embedding is used.

Example of embedding

// Go
type Person struct {
    Name string
}

func (p Person) PrintName() {
    fmt.Println(p.Name)
}

type Employee struct {
    Person // embedding
    Position string
}

func main() {
    e := Employee{Person: Person{Name: "Alice"}, Position: "Developer"}
    e.PrintName() // can be called directly
}

Java equivalent:

// Java
class Person {
    String name;

    void printName() {
        System.out.println(name);
    }
}

class Employee extends Person {
    String position;
}

public class Main {
    public static void main(String[] args) {
        Employee e = new Employee();
        e.name = "Alice";
        e.position = "Developer";
        e.printName();
    }
}
Embedding ≈ inheritance, but it is composition. Methods of the embedded type are accessible directly, but Employee and Person are different types.

Comparison of Key Concepts Go ↔ Java

Concept Go Java Comment
Class / struct struct class Struct holds data; methods are bound separately
Methods receiver (value / pointer) regular class methods Pointer receiver allows modifying data
Interface implicitly implemented implements Go is more flexible, no explicit declaration
Inheritance type embedding extends Go uses composition instead of inheritance

Practical Output

For a Java developer transitioning to Go, it is important to remember:

  • Struct = data container without behavior
  • Methods are bound through receiver (value/pointer)
  • Interfaces are implemented automatically
  • Embedding replaces inheritance, but this is composition
  • The Go type system is simple, flexible, and safe

These principles allow building Go architecture that is understandable and convenient for large projects, without the unnecessary complexity inherent to Java classes.


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

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

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

Useful Articles:

Modern architectural approaches: from monolith to event-driven systems
Introduction Architecture is more than just a way to arrange classes and modules. It is the language a system uses to communicate time. Today, Java developers live in a world where the boundaries bet...
Resource cleanup, rate-limiting strategies, bounded vs unbounded channels - in Go vs Java | Patterns, idioms, and best practices for Go
We continue the series of articles for developers who want to learn Go based on knowledge of Java, and vice versa. In this article, we will discuss three key topics: Resource Cleanup (resource release...
Context, propagation, and cancellation patterns in Go vs Java | Patterns, idioms, and best practices of Go
← Related articles: Part 1 — Error handling and defer in Go (Concurrency and synchronization) | Patterns, idioms and best practices for Go 1. Context and its role In Go context.Context is used to...

New Articles:

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. ...
Stream vs For in Java: how to write the fastest code possible
In Java, performance is often determined not by the "beauty of the code," but by how it interacts with memory, the JIT compiler, and CPU cache. Let s analyze why the usual for is often faster than Str...
Compiler, Build, and Tooling in Go and Java: how assembly, initialization, analysis, and diagnostics are organized in two ecosystems
This article is dedicated to a general overview of how the compiler, build, and tooling practices are arranged in Go, and how to better understand them through comparison with Java. We will not delve ...
Fullscreen image