7

I'm rewriting my app from Java to Go, I know the languages are completely different along with paradigms but still I managed to convert nearly everything.

Unfortunately I'm having trouble passing a generic function as an argument to another function. In Java it looks like this:

I have an interface:

public interface Parser<T> {
    public T parse(String line);
}

Then I use this interface in a generic fetch method

public static <T> List<T> fetch(Parser<T> parser) {
    ...
    parser.parse(someLine);
    ...
    return the list of generic types
}

Then a parser method looks like this:

class Parser{
    static Foo ParseFoo(String line){
        ...
        some custom parsing
        ...
    }
}

And I use it like this:

List<Foo> list = fetch(Parser::ParseFoo);

How do I achieve this in Go?

I've tried declaring generic interfaces, generic functions but I can't wrap my head around this.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
Jacob
  • 351
  • 2
  • 9
  • 2
    Passing values that implement interfaces is easier / cleaner. But you _can_ pass methods, see [Pass method argument to function](https://stackoverflow.com/questions/38897529/pass-method-argument-to-function/38897667#38897667). – icza May 27 '23 at 08:02

1 Answers1

6

An equivalent generic Parser interface would look like this:

type Parser[T any] interface {
    Parse(line string) T
}

Any type with a Parse(string) S method will implicitly satisfy the Parser[S] interface. For example, the following type Foo satisfies Parser[int]:

type Foo struct{}

func (f Foo) Parse(string) int {
    return 0
}

Then, with the generic Parser interface, you can implement your generic Fetch function:

func Fetch[T any](parser Parser[T]) []T {
    panic("not implemented")
}

Passing values that satisfy interfaces

How would you then do in Go the equivalent of passing Java static method like ParseFoo() below?

class Parser {
    static Foo ParseFoo(String line) {
      ...
    }
}

In Go, there are no static methods. Functions can be attached to types, in which case they become methods. As such, for the cases when you would have a static method in Java, you would normally end up with a non-method function or standalone function in Go:

func ParseFoo(line string) Foo {
    ...
}

To make this function satisfy the Parser[Foo] interface, you can define the following ParserFunc adapter:

type ParserFunc[T any] func(string) T

func (f ParserFunc[T]) Parse(line string) T {
    return f(line)
}

This way, from the standalone function ParseFoo – which isn't attached to any type – you can create a value of ParserFunc[Foo], which does satisfy Parser[Foo], and will just call ParseFoo when its Parse method is called:

f := ParserFunc[Foo](ParseFoo)

Finally, you can pass this f value to fetch since f satisfies Parser[Foo]:

fetch[Foo](f)

Note that you need to specify the type argument Foo to fetch as the compiler cannot infer it.


If you really need a method (i.e., like a non-static/instance method in Java), then you may want to look at method values. The procedure will be similar to the one exposed here.

JFMR
  • 23,265
  • 4
  • 52
  • 76
  • Thanks JFMR! Might sound like a stupid thing, but how do I later call the new Fetch method, passing that specific Parse function? – Jacob May 27 '23 at 09:08
  • @Jacob, I've extended the answer. I hope it helps – JFMR May 27 '23 at 10:51