Contents

mastering golang taught me java

I spend much of my time writing go, but the rest is occupied predominantly by java. It’s a well known idiom that new languages at least take inspiration from (if not recycle) the best features of previous language. There’s a lot to be taken from the similarities between languages.

This is a comparison of go and java and some of the go features that have made me write better java.

enums

Java enums are a little more advanced than those of go. Infact, go doesn’t really have them. There are ways of creating enums in go and this article does a great job of explaining.

All java enums implicitly extend the enum class:

1
public abstract class Enum<E extends Enum<E>>

Which has a protected constructor that takes two arguments:

1
Enum(String name, int ordinal)

If we look at these two fields:

name - The name of this enum constant, which is the identifier used to declare it.

ordinal - The ordinal of this enumeration constant (its position in the enum declaration, where the initial constant is assigned an ordinal of zero).

So what’s the golang comparison here? Well one of the best ways of creating enumerated constants in go is using the iota command. (Taken from the same great enum article ⬆️)

1
2
3
4
5
6
const (
 Monday     Weekday = iota
 Tuesday    Weekday 
 Wednesday  Weekday 
 ...
)

Sure - there’s no built in easy way of accessing the name of the constant that you’ve defined for your enumeration (java gives you a name() method). But the two are backed by an increasing constant that starts at zero, and to fix the name() problem effective go suggests:

attach[ing] a method such as String to any user-defined type makes it possible for arbitrary values to format themselves automatically for printing.

In the article’s example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func (day Weekday) String() string {
    names := [...]string{
        "Sunday", 
        "Monday", 
        "Tuesday", 
        "Wednesday",
        "Thursday", 
        "Friday", 
        "Saturday"}

    if day < Sunday || day > Saturday {
      return "Unknown"
    }

    return names[day]
}

When you compare the ordinal and name that backs both go and java enums, they start to look very similar.

pass by reference

This could be one of the ways that a difference between the two languages explains the most. The difference between pass by reference and by value is well documented in this stackoverflow answer, among other sources. But Java is pass by reference and go is pass by value (there is no pass by reference in go).

Lets look at pointers:

1
2
3
4
p := &BookCase{
    ... // fields omitted
}
// p is a pointer

Using pointers gives us pass by reference style behaviours. By copying the value of the pointer when passing by value. The great thing here is that you know what you are working with, take this example:

1
2
3
func calculate(p *BookCase) {
    ...
}

You can tell from the type definition of the signature of the function what you are dealing with. It reminds you to remember that the modifications to p could have side effects outside the function calculate. If we look at an example in java - we have a class BookCase which holds a number of Book[s].

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class BookCase {

  private final List<Book> books;
  
  public BookCase(final List<Book> books) {
    this.books = books;
  }
  
  public List<Book> getBooks() {
    if (books == null) {
      return Collections.emptyList();
    }
    return books;
  }

If we ignore the fact that this is bad because BookCase is exposing it’s internal representation of Book[s], then it would be quite easy to end up with some code…

1
2
3
4
5
6
7
public Book getNextBookToRead(final BookCase bookCase) {
  List<Book> alreadyRead = bookService.getAlreadyReadBooks();
  List<Book> books = bookCase.getBooks();
  
  books.removeAll(alreadyRead);
  return books.get(0);
}

The next call to bookCase.getBooks() is not return all the books, as the getNextBookToRead function has a major side effect. It leaves the books list in a state where it no longer has all the books in it. Fixing the more fundamental problems with this example, like pushing the removal logic down into the BookCase model would help, but it’s an easy mistake to forget that you are modifying the underlying storage of books within the bookcase.

Go helps to save you from yourself by using *BookCase syntax that makes it explicit that a pointer is being passed.

composition

Composition is the pattern of one object containing another, often to give the outer object (or container object) additional behaviours. There is no inheritance in go, only composition. But that’s not a big loss. Composition over inheritance encourages classes and objects to gain polymorphic behaviours and code reuse by their composition. Given the Animal type and the Name() method

1
2
3
4
5
6
7
type Animal struct {
    name string
}

func (a Animal) Name() string {
    return a.name
}

We can define a Dog type and give it a Bark() method

1
2
3
4
5
6
7
type Dog struct {
    Animal
}

func (Dog) Bark() string {
    return "Woof!"
}

But because the Dog type has an anonymous field Animal that composes the Animal type, Dog now also get’s access to the Name method. If we defined an interface:

1
2
3
type Animaler interface {
    Name() string
}

And said that really an animal is anything that implements the Animaler interface, now anything that composes the Animal type becomes an Animaler. We get polymorphic like behaviors using only interfaces and composition. This promotes the use Animal type for all other types of animal, better code reuse.

You could argue that not having inheritance in go is more restrictive than java, but the same behaviours can be implemented with slightly different patterns and potentially with better and cleaner code.

The same anonymous field composition cannot be achieved in java, but it is possible to define methods on an object that just call the composed or contained object to implement that behaviour, think delegation or proxy patterns.

The slightly unintuitive thing about composition is that the more specific types are on the outside of the onion. Dog is the outer type and Animal the inner.

interfaces over abstract classes

The previous point about composition > inheritance might lead the the desire to make use of abstract classes in java. Abstract classes give you the benefit of code reuse from methods that are defined on the base or abstract class and the polymorphic behaviours from the subclasses that extend the abstract parent.

The problem comes when trying to change the implementation of the non abstract methods defined on the parent. The parent should have no knowledge of the subclasses that extend it, but you cannot change the implementation of the parent without considering the subclasses. This is the fragile base class problem. While this problem can affect composition there are ways of making composition immune to it, you can insert anywhere in the chain of composed types a type that implements the different behaviour and reuse all the downstream types again.

This leads on to using interfaces over abstract types, this helps to avoid the problems of fragile base class, and allows polymorphism. It also promotes the use of interface embedding (or composition) where one interface will embed another.

Maybe we decide that Dog is an interface

1
2
3
4
type Dog interface {
    fmt.Stringer
    Bark() string
}

The Dog interface embeds the fmt.Stringer interface that defines a String() string method. This means that all dogs will be able to be stringified, and can have different barks.

Ultimately polymorphism should defined behaviours and not implementations. Interfaces define the behaviours, the methods, of a type. Abstract classes try to provide some of the implementation - and it’s this that creates fragility.

Go lets you define interfaces for the behaviours and use composition for the code reuse. Using these patterns allows better, cleaner and safer java code, even though java offers more complex inheritance patterns.