Go for Java Developers - Part 1

For the last 20+ years I’ve been mostly focused on Java development. But recently I had an opportunity to join a team which was focused on Go and having spent the early part of my career writing C++ I was intrigued by some of the decisions Go made. So this is the first (of possibly several) posts on things I found interesting about the language and since my I have been doing mostly Java development I’m going to be comparing and contrasting from that perspective.

Go interfaces

For this first post I’m going to discuss one specific implication of how Go handles interfaces.

If you are unfamiliar with Go, it uses structural typing for interfaces (which is more generally called “duck typing” meaning “if it walks like a duck and talks like a duck … it’s a duck.”) This means that if a struct in Go has functions on it which match the methods of an interface, instances of that struct are types of that interface. This sounds more complicated than it actually is so lets look at a quick example:

Suppose you wanted to create a Person class and a Named interface in Java, that might look like:

interface Named {
	String getName();
}

class Person implements Named {
	String name;

	String getName() {
    	return name;
	}
}

Pretty straight forward. Now, if we wanted to do a similar thing in Go it would probably look something like:

type Named interface {
     getName() string
}

type Person struct {
     name string
}

func (p Person) getName() string {
     return p.name
}

Ignoring the syntax differences, what is interesting is that in Go we don’t declare Person as implementing Named. This is implied by the function on Person matching the method in the Named interface. The compiler figures this out for us and instances of a Person are automatically of type Named.

So while this isn’t necessarily a new thing (Python mostly lets you do the same thing), this was something new for a statically typed language.

When I first saw this I thought “meh, so you can leave off the ‘implements’ keyword, whatever.” But I think this feature has a really interesting implication.

The setup

Back in Java land, let’s suppose you have a function that takes an SQL PreparedStatement as one of its arguments. Something like:

class Db {
   void setValue(PreparedStatement stmt, String value) {
 	stmt.setString(1, value);
   }
}

You might use this like:

PreparedStatement stmt = conn.prepareStatement("SELECT …");
Db db = new Db();
db.setValue(stmt, "someValue");

Writing a unit test for this would probably look something like:

class DbTest {

	@Test
	public void setValueTest() {
    	Db db = new Db();
    	PreparedStatement stmt = ??
    	db.setValue(stmt, "dog");

    	// todo - verify the statement received the value of "dog"
	}
}

The challenge is creating the test PreparedStatement. You could use one of the mocking frameworks in Java (Mockito, PowerMock, …) to mock the PreparedStatement and test that setValue is called with the value “dog”. While this does work, most/all of the mocking frameworks are basically a nice user interface over a rats nest of reflection calls. (Used incorrectly reflection is a tool to move compile time errors back to the runtime.) However, it is also possible to test this without using a mocking framework by building our own class which implements PreparedStatement:

class TestPreparedStatement implements PreparedStatement {

	String value;

	void setString(int parameterIndex, String x) {
    	this.value = x;
	}

	...
}

An instance of our TestPreparedStatement can now be passed into our setValue method and we can later verify that the internal ‘value’ is set to “dog”.

Ahhh … but there is a devil hidden in these details. PreparedStatement is a massive interface with well over 50 methods. In order to stub out the one method you want (setString) you are going to need to also stub out all of the other ones as well. We don’t ever use them so they can all throw a RuntimeException (and thankfully most modern IDEs can automatically generate this code for you) … but you are still dealing with a lot of boilerplate code.

The switch

Now let’s take a look at this same problem in Go land. (For the sake of argument let’s assume that PreparedStatement both exists in Go and works much in the same way as its Java counterpart).

type Db struct {
}

func (db Db) setValue(stmt PreparedStatement, value string) {
    stmt.setString(1, value)
}

And again it is used like:

stmt := conn.prepareStatement("SELECT …")
db := Db{}
db.setValue(stmt, "someValue")

Now let’s write the unit test:

func SetValueTest(t* testing.T) {
    db := Db{}
    stmt := ??
    db.setValue(stmt, "dog")

    // todo - verify the statement received the value of 'dog
}

We’ve once again hit the same issue. As with Java, we can use the Go’s mock framework (which also uses reflection) or, as before, we can try to roll our own.

Approaching it directly we could build out a TestPreparedStatement as:

type TestPreparedStatement struct {
    value string
}

func (tps TestPreparedStatement) setString(parameterIndex int, x string) {
     tps.value = x
}

But then we are going to have the same issue as we had with Java where we also need to stub out all of the other 50+ methods in the PreparedStatement interface which is just as wasteful as it was in Java. But we do have one other option.

Duck Typing

What if we didn’t stub out all of the other methods and instead created a new interface which only contained the one method we are using? So something like:

type StringSetter interface {
    setString(parameterIndex int, x string)
}

Now, since our TestPreparedStatement implements this one method it is automatically a type of StringSetter. But guess what, the object returned by conn.prepareStatement is also now a type of StringSetter since it too implements setString (by virtue of it being a PreparedStatement which also requires the same method.)

Now, we only need to change the signature of our setValue method to:

func (db Db) setValue(stmt StringSetter, value string) {
    stmt.setString(1, value)
} 

This is where the power of the duck typing comes into play as doing this in Java would require modifying the definition of PreparedStatement so it explicitly extend StringSetter, which is virtually impossible. Yet in Go this is trivial as we can assign interfaces to objects by simply copying over the methods we want into the interface.

I found this to be a surprising feature about Go and is not something I had considered before. It feels really powerful and, to be honest, I’m not yet sure if this is a good feature about the language or not, but it is an interesting one.

Joshua Gerth
Joshua Gerth
Engineering Manager
Distributed Systems Engineer
Systems Architect

My research interests include big data, language parsing and ray tracing.