Experience Lombok in Golang

Experience Lombok in Golang

October 24, 2023

Lombok?

Lombok is a library in Java that reduces repetitive code like getter and setter.

Due to its characteristics, the Go language makes tasks like Getter, Setter, and constructor quite tedious.

In languages like Java, there are libraries that automate these tasks.

For instance, consider creating a constructor for a struct like the one below:

type User struct {
    Name string
    Age int
}

Most of us would probably write a constructor like this:

func NewUser(name string, age int) User {
    return User{
        Name: name,
        Age: age,
    }
}

In Java, constructor creation is typically done like this:

public class User {
    private String name;
    private int age;
    
    public User(String name, int age){
        this.name = name;
        this.age = age;
    } 
}

Lombok is a library that automates such tasks. By using Lombok, you can write it as follows:

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class User {
    private String name;
    private int age;
}

It’s a library that helps developers reduce repetitive tasks like creating constructors and minimize human errors.

Gombok

The Go language inherently has a lot of boilerplate, which can make it difficult to focus on writing actual business logic when you’re constantly writing such code. (Maybe I’m just lazy..) Thinking about how to automate this, I created a Code Generator library called gombok by combining Lombok with Go, which can be utilized well at work.

Installation

It can be easily installed using the install command:

 $ go install github.com/YangTaeyoung/gombok@v1.1.0

Usage

Simply write the annotations used in Lombok as comments on the struct you want to use.

// @AllArgsConstructor
// @Builder
// @Getter
// @Setter
type User struct {
    Name string
    Age int
}

Then, execute it as follows. There’s no need to specify additional options, and when this command is executed, a file named {filename}_gombok.go is generated.

$ gombok

> main.go
> 2023/10/24 23:25:39 Found @AllArgsConstructor in User
> 2023/10/24 23:25:39 Found @Builder in User
> 2023/10/24 23:25:39 Found @Getter in User
> 2023/10/24 23:25:39 Found @Setter in User

If you open the generated file, you’ll see that the constructor, builder, Getter, and Setter have been generated as follows:

// Code generated by gombok. DO NOT EDIT.
package main

// NewUserWithAllArgs
func NewUserWithAllArgs(name string, age int) User {
	return User{
		Name: name,
		Age:  age,
	}
}

// UserBuilder
// a builder for User
type UserBuilder struct {
	target *User
}

// WithName
// sets the Name field of the target User
func (ub UserBuilder) WithName(name string) UserBuilder {
	ub.target.Name = name

	return ub
}

// WithAge
// sets the Age field of the target User
func (ub UserBuilder) WithAge(age int) UserBuilder {
	ub.target.Age = age

	return ub
}

// Build
// constructs a User from the builder
func (ub UserBuilder) Build() User {
	return *ub.target
}

// NewUserBuilder
// creates a new builder instance for User
func NewUserBuilder() UserBuilder {
	return UserBuilder{target: &User{}}
}

// GetName
func (u *User) GetName() string {
	return u.Name
}

// GetAge
func (u *User) GetAge() int {
	return u.Age
}

// SetName
func (u *User) SetName(name string) {
	u.Name = name
}

// SetAge
func (u *User) SetAge(age int) {
	u.Age = age
}

Now, simply use the generated receiver functions where you need them.

// @AllArgsConstructor
// @Builder
// @Getter
// @Setter
type User struct {
	Name string
	Age  int
}

func main() {
	user := NewUserBuilder().WithAge(10).WithName("John").Build()

	fmt.Println(user.GetName(), user.GetAge())

	user.SetName("Jane")
	user.SetAge(20)

	fmt.Println(user.GetName(), user.GetAge())

	user2 := NewUserWithAllArgs("giraffe", 27)

	fmt.Println(user2.GetName(), user2.GetAge())
}

Result

John 10
Jane 20
giraffe 27