internal,bind: resolve overloaded methods at runtime

Before this CL, calling overloaded methods on reverse bound Java
classes and interfaces involved confusing and ugly name mangling.
If a set of methods with the same name differed only in argument count,
the mangling was simply adding the argument count to the name:

func F()
func F1(int32)

But if two or more methods had the same number of arguments, the type
had to be appended:

func (...) F() int32
func (...) F1(int32) (int32, error)
func (...) F__I(int32, int32)
func (...) F__JLjava_util_concurrent_TimeUnit_2(int64, concurrent.TimeUnit)

This CL sacrifices a bit of type safety and performance to regain the
convenience and simplicity of Go by resolving overloaded method dispatch
at runtime.

Overloaded Java methods are combined to one Go method that, when invoked,
determines the correct Java method variant at runtime.

The signature of the Go method  is compatible with every Java method with
that name. For the example above, the single Go method becomes the most
general

func (...) F(...interface{}) (interface{}, error)

The method is variadic to cover function with a varying number of
arguments, and it returns interface{} to cover int32, int64 and no
argument. Finally, it returns an error to cover the variant that returns
an error. The generator tries to be specific; for example

func G1(int32) int32
func G2(int32, int32) int32

becomes

func G(int32, ...int32) int32

Overriding Java methods in Go is changed to use the Go parameter types to
determine to correct Java method. To avoid name clashes when overriding
multiple overloaded methods, trailing underscores in the method name are
ignored when matching Java methods.  See the Get methods of GoFuture in
bind/testpkg/javapkg for an example.

Change-Id: I6ac3e024141daa8fc2c35187865c5d7a63368094
Reviewed-on: https://go-review.googlesource.com/35186
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/bind/genclasses.go b/bind/genclasses.go
index 1833703..4aa903b 100644
--- a/bind/genclasses.go
+++ b/bind/genclasses.go
@@ -7,6 +7,7 @@
 import (
 	"fmt"
 	"path"
+	"reflect"
 	"strings"
 	"unicode"
 	"unicode/utf8"
@@ -49,6 +50,15 @@
 	}
 }
 
+func (g *ClassGen) isFuncSetSupported(fs *java.FuncSet) bool {
+	for _, f := range fs.Funcs {
+		if g.isFuncSupported(f) {
+			return true
+		}
+	}
+	return false
+}
+
 func (g *ClassGen) isFuncSupported(f *java.Func) bool {
 	for _, a := range f.Params {
 		if !g.isSupported(a) {
@@ -62,6 +72,11 @@
 }
 
 func (g *ClassGen) goType(t *java.Type, local bool) string {
+	if t == nil {
+		// interface{} is used for parameters types for overloaded methods
+		// where no common ancestor type exists.
+		return "interface{}"
+	}
 	switch t.Kind {
 	case java.Int:
 		return "int32"
@@ -153,13 +168,16 @@
 		g.Printf("var (\n")
 		g.Indent()
 		// Functions
-		for _, f := range cls.Funcs {
-			if !f.Public || !g.isFuncSupported(f) {
-				continue
+	loop:
+		for _, fs := range cls.Funcs {
+			for _, f := range fs.Funcs {
+				if f.Public && g.isFuncSupported(f) {
+					g.Printf("%s func", fs.GoName)
+					g.genFuncDecl(false, fs)
+					g.Printf("\n")
+					continue loop
+				}
 			}
-			g.Printf("%s func", f.GoName)
-			g.genFuncDecl(false, f)
-			g.Printf("\n")
 		}
 		g.Printf("// Cast takes a proxy for a Java object and converts it to a %s proxy.\n", cls.Name)
 		g.Printf("// Cast panics if the argument is not a proxy or if the underlying object does\n")
@@ -209,17 +227,19 @@
 	}
 	g.Printf("\n")
 	for _, cls := range g.classes {
-		for _, f := range cls.AllMethods {
-			if !g.isFuncSupported(f) {
-				continue
-			}
-			g.Printf("extern ")
-			g.genCMethodDecl("cproxy", cls.JNIName, f)
-			g.Printf(";\n")
-			if _, ok := g.supers[cls.Name]; ok {
+		for _, fs := range cls.AllMethods {
+			for _, f := range fs.Funcs {
+				if !g.isFuncSupported(f) {
+					continue
+				}
 				g.Printf("extern ")
-				g.genCMethodDecl("csuper", cls.JNIName, f)
+				g.genCMethodDecl("cproxy", cls.JNIName, f)
 				g.Printf(";\n")
+				if _, ok := g.supers[cls.Name]; ok {
+					g.Printf("extern ")
+					g.genCMethodDecl("csuper", cls.JNIName, f)
+					g.Printf(";\n")
+				}
 			}
 		}
 	}
@@ -235,17 +255,21 @@
 		if _, ok := g.supers[cls.Name]; ok {
 			g.Printf("static jclass sclass_%s;\n", cls.JNIName)
 		}
-		for _, f := range cls.Funcs {
-			if !f.Public || !g.isFuncSupported(f) {
-				continue
+		for _, fs := range cls.Funcs {
+			for _, f := range fs.Funcs {
+				if !f.Public || !g.isFuncSupported(f) {
+					continue
+				}
+				g.Printf("static jmethodID m_s_%s_%s;\n", cls.JNIName, f.JNIName)
 			}
-			g.Printf("static jmethodID m_s_%s_%s;\n", cls.JNIName, f.JNIName)
 		}
-		for _, f := range cls.AllMethods {
-			if g.isFuncSupported(f) {
-				g.Printf("static jmethodID m_%s_%s;\n", cls.JNIName, f.JNIName)
-				if _, ok := g.supers[cls.Name]; ok {
-					g.Printf("static jmethodID sm_%s_%s;\n", cls.JNIName, f.JNIName)
+		for _, fs := range cls.AllMethods {
+			for _, f := range fs.Funcs {
+				if g.isFuncSupported(f) {
+					g.Printf("static jmethodID m_%s_%s;\n", cls.JNIName, f.JNIName)
+					if _, ok := g.supers[cls.Name]; ok {
+						g.Printf("static jmethodID sm_%s_%s;\n", cls.JNIName, f.JNIName)
+					}
 				}
 			}
 		}
@@ -263,22 +287,26 @@
 			g.Printf("sclass_%s = (*env)->GetSuperclass(env, clazz);\n", cls.JNIName)
 			g.Printf("sclass_%s = (*env)->NewGlobalRef(env, sclass_%s);\n", cls.JNIName, cls.JNIName)
 		}
-		for _, f := range cls.Funcs {
-			if !f.Public || !g.isFuncSupported(f) {
-				continue
-			}
-			g.Printf("m_s_%s_%s = ", cls.JNIName, f.JNIName)
-			if f.Constructor {
-				g.Printf("go_seq_get_method_id(clazz, \"<init>\", %q);\n", f.Desc)
-			} else {
-				g.Printf("go_seq_get_static_method_id(clazz, %q, %q);\n", f.Name, f.Desc)
+		for _, fs := range cls.Funcs {
+			for _, f := range fs.Funcs {
+				if !f.Public || !g.isFuncSupported(f) {
+					continue
+				}
+				g.Printf("m_s_%s_%s = ", cls.JNIName, f.JNIName)
+				if f.Constructor {
+					g.Printf("go_seq_get_method_id(clazz, \"<init>\", %q);\n", f.Desc)
+				} else {
+					g.Printf("go_seq_get_static_method_id(clazz, %q, %q);\n", f.Name, f.Desc)
+				}
 			}
 		}
-		for _, f := range cls.AllMethods {
-			if g.isFuncSupported(f) {
-				g.Printf("m_%s_%s = go_seq_get_method_id(clazz, %q, %q);\n", cls.JNIName, f.JNIName, f.Name, f.Desc)
-				if _, ok := g.supers[cls.Name]; ok {
-					g.Printf("sm_%s_%s = go_seq_get_method_id(sclass_%s, %q, %q);\n", cls.JNIName, f.JNIName, cls.JNIName, f.Name, f.Desc)
+		for _, fs := range cls.AllMethods {
+			for _, f := range fs.Funcs {
+				if g.isFuncSupported(f) {
+					g.Printf("m_%s_%s = go_seq_get_method_id(clazz, %q, %q);\n", cls.JNIName, f.JNIName, f.Name, f.Desc)
+					if _, ok := g.supers[cls.Name]; ok {
+						g.Printf("sm_%s_%s = go_seq_get_method_id(sclass_%s, %q, %q);\n", cls.JNIName, f.JNIName, cls.JNIName, f.Name, f.Desc)
+					}
 				}
 			}
 		}
@@ -287,15 +315,17 @@
 	g.Outdent()
 	g.Printf("}\n\n")
 	for _, cls := range g.classes {
-		for _, f := range cls.AllMethods {
-			if !g.isFuncSupported(f) {
-				continue
-			}
-			g.genCMethodDecl("cproxy", cls.JNIName, f)
-			g.genCMethodBody(cls, f, false)
-			if _, ok := g.supers[cls.Name]; ok {
-				g.genCMethodDecl("csuper", cls.JNIName, f)
-				g.genCMethodBody(cls, f, true)
+		for _, fs := range cls.AllMethods {
+			for _, f := range fs.Funcs {
+				if !g.isFuncSupported(f) {
+					continue
+				}
+				g.genCMethodDecl("cproxy", cls.JNIName, f)
+				g.genCMethodBody(cls, f, false)
+				if _, ok := g.supers[cls.Name]; ok {
+					g.genCMethodDecl("csuper", cls.JNIName, f)
+					g.genCMethodBody(cls, f, true)
+				}
 			}
 		}
 	}
@@ -365,76 +395,84 @@
 	return string(unicode.ToUpper(r)) + s[n:]
 }
 
-func (g *ClassGen) genFuncDecl(local bool, f *java.Func) {
+func (g *ClassGen) genFuncDecl(local bool, fs *java.FuncSet) {
 	g.Printf("(")
-	for i, a := range f.Params {
+	for i, a := range fs.Params {
 		if i > 0 {
 			g.Printf(", ")
 		}
-		g.Printf("a%d %s", i, g.goType(a, local))
+		g.Printf("a%d ", i)
+		if i == len(fs.Params)-1 && fs.Variadic {
+			g.Printf("...")
+		}
+		g.Printf(g.goType(a, local))
 	}
 	g.Printf(")")
-	if f.Throws != "" {
-		if f.Ret != nil {
-			g.Printf(" (%s, error)", g.goType(f.Ret, local))
+	if fs.Throws {
+		if fs.HasRet {
+			g.Printf(" (%s, error)", g.goType(fs.Ret, local))
 		} else {
 			g.Printf(" error")
 		}
-	} else if f.Ret != nil {
-		g.Printf(" %s", g.goType(f.Ret, local))
+	} else if fs.HasRet {
+		g.Printf(" %s", g.goType(fs.Ret, local))
 	}
 }
 
 func (g *ClassGen) genC(cls *java.Class) {
-	for _, f := range cls.Funcs {
-		if !f.Public || !g.isFuncSupported(f) {
-			continue
+	for _, fs := range cls.Funcs {
+		for _, f := range fs.Funcs {
+			if !f.Public || !g.isFuncSupported(f) {
+				continue
+			}
+			g.genCFuncDecl(cls.JNIName, f)
+			g.Printf(" {\n")
+			g.Indent()
+			g.Printf("JNIEnv *env = go_seq_push_local_frame(%d);\n", len(f.Params))
+			for i, a := range f.Params {
+				g.genCToJava(fmt.Sprintf("a%d", i), a)
+			}
+			if f.Constructor {
+				g.Printf("jobject res = (*env)->NewObject(env")
+			} else if f.Ret != nil {
+				g.Printf("%s res = (*env)->CallStatic%sMethod(env", f.Ret.JNIType(), f.Ret.JNICallType())
+			} else {
+				g.Printf("(*env)->CallStaticVoidMethod(env")
+			}
+			g.Printf(", class_%s, m_s_%s_%s", cls.JNIName, cls.JNIName, f.JNIName)
+			for i := range f.Params {
+				g.Printf(", _a%d", i)
+			}
+			g.Printf(");\n")
+			g.Printf("jobject _exc = go_seq_get_exception(env);\n")
+			g.Printf("int32_t _exc_ref = go_seq_to_refnum(env, _exc);\n")
+			if f.Ret != nil {
+				g.genCRetClear("res", f.Ret, "_exc")
+				g.genJavaToC("res", f.Ret)
+			}
+			g.Printf("go_seq_pop_local_frame(env);\n")
+			if f.Ret != nil {
+				g.Printf("ret_%s __res = {_res, _exc_ref};\n", f.Ret.CType())
+				g.Printf("return __res;\n")
+			} else {
+				g.Printf("return _exc_ref;\n")
+			}
+			g.Outdent()
+			g.Printf("}\n\n")
 		}
-		g.genCFuncDecl(cls.JNIName, f)
-		g.Printf(" {\n")
-		g.Indent()
-		g.Printf("JNIEnv *env = go_seq_push_local_frame(%d);\n", len(f.Params))
-		for i, a := range f.Params {
-			g.genCToJava(fmt.Sprintf("a%d", i), a)
-		}
-		if f.Constructor {
-			g.Printf("jobject res = (*env)->NewObject(env")
-		} else if f.Ret != nil {
-			g.Printf("%s res = (*env)->CallStatic%sMethod(env", f.Ret.JNIType(), f.Ret.JNICallType())
-		} else {
-			g.Printf("(*env)->CallStaticVoidMethod(env")
-		}
-		g.Printf(", class_%s, m_s_%s_%s", cls.JNIName, cls.JNIName, f.JNIName)
-		for i := range f.Params {
-			g.Printf(", _a%d", i)
-		}
-		g.Printf(");\n")
-		g.Printf("jobject _exc = go_seq_get_exception(env);\n")
-		g.Printf("int32_t _exc_ref = go_seq_to_refnum(env, _exc);\n")
-		if f.Ret != nil {
-			g.genCRetClear("res", f.Ret, "_exc")
-			g.genJavaToC("res", f.Ret)
-		}
-		g.Printf("go_seq_pop_local_frame(env);\n")
-		if f.Ret != nil {
-			g.Printf("ret_%s __res = {_res, _exc_ref};\n", f.Ret.CType())
-			g.Printf("return __res;\n")
-		} else {
-			g.Printf("return _exc_ref;\n")
-		}
-		g.Outdent()
-		g.Printf("}\n\n")
 	}
 }
 
 func (g *ClassGen) genH(cls *java.Class) {
-	for _, f := range cls.Funcs {
-		if !f.Public || !g.isFuncSupported(f) {
-			continue
+	for _, fs := range cls.Funcs {
+		for _, f := range fs.Funcs {
+			if !f.Public || !g.isFuncSupported(f) {
+				continue
+			}
+			g.Printf("extern ")
+			g.genCFuncDecl(cls.JNIName, f)
+			g.Printf(";\n")
 		}
-		g.Printf("extern ")
-		g.genCFuncDecl(cls.JNIName, f)
-		g.Printf(";\n")
 	}
 }
 
@@ -480,13 +518,20 @@
 	g.Printf("	return\n")
 	g.Printf("}\n")
 	g.Printf("class_%s = clazz\n", cls.JNIName)
-	for _, f := range cls.Funcs {
-		if !f.Public || !g.isFuncSupported(f) {
+	for _, fs := range cls.Funcs {
+		var supported bool
+		for _, f := range fs.Funcs {
+			if f.Public && g.isFuncSupported(f) {
+				supported = true
+				break
+			}
+		}
+		if !supported {
 			continue
 		}
-		g.Printf("%s.%s = func", cls.PkgName, f.GoName)
-		g.genFuncDecl(false, f)
-		g.genFuncBody(cls, f, "cproxy_s", true)
+		g.Printf("%s.%s = func", cls.PkgName, fs.GoName)
+		g.genFuncDecl(false, fs)
+		g.genFuncBody(cls, fs, "cproxy_s", true)
 	}
 	g.Printf("%s.Cast = func(v interface{}) Java.%s {\n", cls.PkgName, goClsName(cls.Name))
 	g.Indent()
@@ -503,13 +548,13 @@
 	g.Printf("}\n\n")
 	g.Printf("type proxy_class_%s _seq.Ref\n\n", cls.JNIName)
 	g.Printf("func (p *proxy_class_%s) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }\n\n", cls.JNIName)
-	for _, f := range cls.AllMethods {
-		if !g.isFuncSupported(f) {
+	for _, fs := range cls.AllMethods {
+		if !g.isFuncSetSupported(fs) {
 			continue
 		}
-		g.Printf("func (p *proxy_class_%s) %s", cls.JNIName, f.GoName)
-		g.genFuncDecl(false, f)
-		g.genFuncBody(cls, f, "cproxy", false)
+		g.Printf("func (p *proxy_class_%s) %s", cls.JNIName, fs.GoName)
+		g.genFuncDecl(false, fs)
+		g.genFuncBody(cls, fs, "cproxy", false)
 	}
 	if cls.Throwable {
 		g.Printf("func (p *proxy_class_%s) Error() string {\n", cls.JNIName)
@@ -521,54 +566,145 @@
 		g.Printf("	return &super_%s{p}\n", cls.JNIName)
 		g.Printf("}\n\n")
 		g.Printf("type super_%s struct {*proxy_class_%[1]s}\n\n", cls.JNIName)
-		for _, f := range cls.AllMethods {
-			if !g.isFuncSupported(f) {
+		for _, fs := range cls.AllMethods {
+			if !g.isFuncSetSupported(fs) {
 				continue
 			}
-			g.Printf("func (p *super_%s) %s", cls.JNIName, f.GoName)
-			g.genFuncDecl(false, f)
-			g.genFuncBody(cls, f, "csuper", false)
+			g.Printf("func (p *super_%s) %s", cls.JNIName, fs.GoName)
+			g.genFuncDecl(false, fs)
+			g.genFuncBody(cls, fs, "csuper", false)
 		}
 	}
 }
 
-func (g *ClassGen) genFuncBody(cls *java.Class, f *java.Func, prefix string, static bool) {
+// genFuncBody generated a Go function body for a FuncSet. It resolves overloading dynamically,
+// by inspecting the number of arguments (if the FuncSet contains varying parameter counts),
+// and their types.
+func (g *ClassGen) genFuncBody(cls *java.Class, fs *java.FuncSet, prefix string, static bool) {
+	maxp := len(fs.Funcs[0].Params)
+	minp := maxp
+	// sort the function variants into argument sizes.
+	buckets := make(map[int][]*java.Func)
+	for _, f := range fs.Funcs {
+		n := len(f.Params)
+		if n < minp {
+			minp = n
+		} else if n > maxp {
+			maxp = n
+		}
+		buckets[n] = append(buckets[n], f)
+	}
 	g.Printf(" {\n")
 	g.Indent()
-	for i, a := range f.Params {
-		g.genWrite(fmt.Sprintf("a%d", i), a, modeTransient)
+	if fs.Variadic {
+		// Switch over the number of arguments.
+		g.Printf("switch %d + len(a%d) {\n", minp, minp)
 	}
-	g.Printf("res := C.%s_%s_%s(", prefix, cls.JNIName, f.JNIName)
-	if !static {
-		g.Printf("C.jint(p.Bind_proxy_refnum__())")
-	}
-	for i := range f.Params {
-		if !static || i > 0 {
-			g.Printf(", ")
+	for i := minp; i <= maxp; i++ {
+		funcs := buckets[i]
+		if len(funcs) == 0 {
+			continue
 		}
-		g.Printf("_a%d", i)
+		if fs.Variadic {
+			g.Printf("case %d:\n", i)
+			g.Indent()
+		}
+		for _, f := range funcs {
+			if len(funcs) > 1 {
+				g.Printf("{\n")
+				g.Indent()
+			}
+			var argNames []string
+			var preds []string
+			for i, a := range f.Params {
+				var ct *java.Type
+				var argName string
+				if i >= minp {
+					argName = fmt.Sprintf("a%d[%d]", minp, i-minp)
+					ct = fs.Params[minp]
+				} else {
+					argName = fmt.Sprintf("a%d", i)
+					ct = fs.Params[i]
+				}
+				if !reflect.DeepEqual(ct, a) {
+					g.Printf("_a%d, ok%d := %s.(%s)\n", i, i, argName, g.goType(a, false))
+					argName = fmt.Sprintf("_a%d", i)
+					preds = append(preds, fmt.Sprintf("ok%d", i))
+				}
+				argNames = append(argNames, argName)
+			}
+			if len(preds) > 0 {
+				g.Printf("if %s {\n", strings.Join(preds, " && "))
+				g.Indent()
+			}
+			for i, a := range f.Params {
+				g.genWrite(fmt.Sprintf("__a%d", i), argNames[i], a, modeTransient)
+			}
+			g.Printf("res := C.%s_%s_%s(", prefix, cls.JNIName, f.JNIName)
+			if !static {
+				g.Printf("C.jint(p.Bind_proxy_refnum__())")
+			}
+			for i := range f.Params {
+				if !static || i > 0 {
+					g.Printf(", ")
+				}
+				g.Printf("__a%d", i)
+			}
+			g.Printf(")\n")
+			g.genFuncRet(fs, f)
+			if len(preds) > 0 {
+				g.Outdent()
+				g.Printf("}\n")
+			}
+			if len(funcs) > 1 {
+				g.Outdent()
+				g.Printf("}\n")
+			}
+		}
+		if fs.Variadic {
+			g.Outdent()
+		}
 	}
-	g.Printf(")\n")
-	g.genFuncRet(f)
+	if fs.Variadic {
+		g.Printf("}\n")
+	}
+	if fs.Variadic || len(fs.Funcs) > 1 {
+		g.Printf("panic(\"no overloaded method found for %s.%s that matched the arguments\")\n", cls.Name, fs.Name)
+	}
 	g.Outdent()
 	g.Printf("}\n\n")
 }
 
-func (g *ClassGen) genFuncRet(f *java.Func) {
+func (g *ClassGen) genFuncRet(fs *java.FuncSet, f *java.Func) {
 	if f.Ret != nil {
 		g.genRead("_res", "res.res", f.Ret, modeRetained)
 		g.genRefRead("_exc", "res.exc", "error", "proxy_error", true)
 	} else {
 		g.genRefRead("_exc", "res", "error", "proxy_error", true)
 	}
-	if f.Throws == "" {
+	if !fs.Throws {
 		g.Printf("if (_exc != nil) { panic(_exc) }\n")
-		if f.Ret != nil {
-			g.Printf("return _res\n")
+		if fs.HasRet {
+			if f.Ret != nil {
+				g.Printf("return _res\n")
+			} else {
+				// The variant doesn't return a value, but the common
+				// signature does. Use nil as a placeholder return value.
+				g.Printf("return nil\n")
+			}
+		} else if fs.Variadic || len(fs.Funcs) > 1 {
+			// If there are overloaded variants, return here to avoid the fallback
+			// panic generated in genFuncBody.
+			g.Printf("return\n")
 		}
 	} else {
-		if f.Ret != nil {
-			g.Printf("return _res, _exc\n")
+		if fs.HasRet {
+			if f.Ret != nil {
+				g.Printf("return _res, _exc\n")
+			} else {
+				// As above, use a nil placeholder return value.
+				g.Printf("return nil, _exc\n")
+			}
 		} else {
 			g.Printf("return _exc\n")
 		}
@@ -612,26 +748,26 @@
 	g.Printf("}\n")
 }
 
-func (g *ClassGen) genWrite(v string, t *java.Type, mode varMode) {
+func (g *ClassGen) genWrite(dst, v string, t *java.Type, mode varMode) {
 	switch t.Kind {
 	case java.Int, java.Short, java.Char, java.Byte, java.Long, java.Float, java.Double:
-		g.Printf("_%s := C.%s(%s)\n", v, t.CType(), v)
+		g.Printf("%s := C.%s(%s)\n", dst, t.CType(), v)
 	case java.Boolean:
-		g.Printf("_%s := C.jboolean(C.JNI_FALSE)\n", v)
+		g.Printf("%s := C.jboolean(C.JNI_FALSE)\n", dst)
 		g.Printf("if %s {\n", v)
-		g.Printf("	_%s = C.jboolean(C.JNI_TRUE)\n", v)
+		g.Printf("	%s = C.jboolean(C.JNI_TRUE)\n", dst)
 		g.Printf("}\n")
 	case java.String:
-		g.Printf("_%s := encodeString(%s)\n", v, v)
+		g.Printf("%s := encodeString(%s)\n", dst, v)
 	case java.Array:
 		if t.Elem.Kind != java.Byte {
 			panic("unsupported array type")
 		}
-		g.Printf("_%s := fromSlice(%s, %v)\n", v, v, mode == modeRetained)
+		g.Printf("%s := fromSlice(%s, %v)\n", dst, v, mode == modeRetained)
 	case java.Object:
-		g.Printf("var _%s C.jint = _seq.NullRefNum\n", v)
+		g.Printf("var %s C.jint = _seq.NullRefNum\n", dst)
 		g.Printf("if %s != nil {\n", v)
-		g.Printf("	_%s = C.jint(_seq.ToRefNum(%s))\n", v, v)
+		g.Printf("	%s = C.jint(_seq.ToRefNum(%s))\n", dst, v)
 		g.Printf("}\n")
 	default:
 		panic("invalid kind")
@@ -698,12 +834,12 @@
 	g.Printf("type %s interface {\n", goClsName(cls.Name))
 	g.Indent()
 	// Methods
-	for _, f := range cls.AllMethods {
-		if !g.isFuncSupported(f) {
+	for _, fs := range cls.AllMethods {
+		if !g.isFuncSetSupported(fs) {
 			continue
 		}
-		g.Printf(f.GoName)
-		g.genFuncDecl(true, f)
+		g.Printf(fs.GoName)
+		g.genFuncDecl(true, fs)
 		g.Printf("\n")
 	}
 	if _, ok := g.supers[cls.Name]; ok {
diff --git a/bind/genjava.go b/bind/genjava.go
index 3164b77..3080fa1 100644
--- a/bind/genjava.go
+++ b/bind/genjava.go
@@ -9,6 +9,7 @@
 	"go/constant"
 	"go/types"
 	"math"
+	"reflect"
 	"regexp"
 	"strings"
 
@@ -38,7 +39,7 @@
 	extends *java.Class
 	// All Java classes and interfaces this class extends and implements.
 	supers  []*java.Class
-	methods map[string]*java.Func
+	methods map[string]*java.FuncSet
 	// Does the class need a default no-arg constructor
 	genNoargCon bool
 }
@@ -59,16 +60,21 @@
 			continue
 		}
 		inf := &javaClassInfo{
-			methods:     make(map[string]*java.Func),
+			methods:     make(map[string]*java.FuncSet),
 			genNoargCon: true, // java.lang.Object has a no-arg constructor
 		}
 		for _, n := range classes {
 			cls := g.clsMap[n]
-			for _, f := range cls.AllMethods {
-				if f.Final {
-					continue
+			for _, fs := range cls.AllMethods {
+				hasMeth := false
+				for _, f := range fs.Funcs {
+					if !f.Final {
+						hasMeth = true
+					}
 				}
-				inf.methods[f.GoName] = f
+				if hasMeth {
+					inf.methods[fs.GoName] = fs
+				}
 			}
 			inf.supers = append(inf.supers, cls)
 			if !cls.Interface {
@@ -96,6 +102,104 @@
 	}
 }
 
+func (j *javaClassInfo) toJavaType(T types.Type) *java.Type {
+	switch T := T.(type) {
+	case *types.Basic:
+		var kind java.TypeKind
+		switch T.Kind() {
+		case types.Bool, types.UntypedBool:
+			kind = java.Boolean
+		case types.Uint8:
+			kind = java.Byte
+		case types.Int16:
+			kind = java.Short
+		case types.Int32, types.UntypedRune: // types.Rune
+			kind = java.Int
+		case types.Int64, types.UntypedInt:
+			kind = java.Long
+		case types.Float32:
+			kind = java.Float
+		case types.Float64, types.UntypedFloat:
+			kind = java.Double
+		case types.String, types.UntypedString:
+			kind = java.String
+		default:
+			return nil
+		}
+		return &java.Type{Kind: kind}
+	case *types.Slice:
+		switch e := T.Elem().(type) {
+		case *types.Basic:
+			switch e.Kind() {
+			case types.Uint8: // Byte.
+				return &java.Type{Kind: java.Array, Elem: &java.Type{Kind: java.Byte}}
+			}
+		}
+		return nil
+	case *types.Named:
+		if isJavaType(T) {
+			return &java.Type{Kind: java.Object, Class: classNameFor(T)}
+		}
+	}
+	return nil
+}
+
+// lookupMethod searches the Java class descriptor for a method
+// that matches the Go method.
+func (j *javaClassInfo) lookupMethod(m *types.Func, hasThis bool) *java.Func {
+	jm := j.methods[m.Name()]
+	if jm == nil {
+		// If an exact match is not found, try the method with trailing underscores
+		// stripped. This way, name clashes can be avoided when overriding multiple
+		// overloaded methods from Go.
+		base := strings.TrimRight(m.Name(), "_")
+		jm = j.methods[base]
+		if jm == nil {
+			return nil
+		}
+	}
+	// A name match was found. Now use the parameter and return types to locate
+	// the correct variant.
+	sig := m.Type().(*types.Signature)
+	params := sig.Params()
+	// Convert Go parameter types to their Java counterparts, if possible.
+	var jparams []*java.Type
+	i := 0
+	if hasThis {
+		i = 1
+	}
+	for ; i < params.Len(); i++ {
+		jparams = append(jparams, j.toJavaType(params.At(i).Type()))
+	}
+	var ret *java.Type
+	var throws bool
+	if results := sig.Results(); results.Len() > 0 {
+		ret = j.toJavaType(results.At(0).Type())
+		if results.Len() > 1 {
+			throws = isErrorType(results.At(1).Type())
+		}
+	}
+loop:
+	for _, f := range jm.Funcs {
+		if len(f.Params) != len(jparams) {
+			continue
+		}
+		if throws != (f.Throws != "") {
+			continue
+		}
+		if !reflect.DeepEqual(ret, f.Ret) {
+			continue
+		}
+		for i, p := range f.Params {
+			if !reflect.DeepEqual(p, jparams[i]) {
+				continue loop
+			}
+		}
+		return f
+	}
+	return nil
+}
+
 // ClassNames returns the list of names of the generated Java classes and interfaces.
 func (g *JavaGen) ClassNames() []string {
 	var names []string
@@ -217,32 +321,11 @@
 		var jm *java.Func
 		hasThis := false
 		if jinf != nil {
-			jm = jinf.methods[m.Name()]
+			hasThis = g.hasThis(n, m)
+			jm = jinf.lookupMethod(m, hasThis)
 			if jm != nil {
 				g.Printf("@Override ")
 			}
-			// Check the implicit this argument, if any
-			sig := m.Type().(*types.Signature)
-			params := sig.Params()
-			if params.Len() > 0 {
-				v := params.At(0)
-				if v.Name() == "this" {
-					t := v.Type()
-					if t, ok := t.(*types.Named); ok {
-						obj := t.Obj()
-						pkg := obj.Pkg()
-						if pkgFirstElem(pkg) == "Java" {
-							clsName := classNameFor(t)
-							exp := g.javaPkgName(g.Pkg) + "." + s.obj.Name()
-							if clsName != exp {
-								g.errorf("the type %s of the `this` argument to method %s.%s is not %s", clsName, n, m.Name(), exp)
-								continue
-							}
-							hasThis = true
-						}
-					}
-				}
-			}
 		}
 		g.Printf("public native ")
 		g.genFuncSignature(m, jm, hasThis)
@@ -259,6 +342,35 @@
 	g.Printf("}\n\n")
 }
 
+// hasThis returns whether a method has an implicit "this" parameter.
+func (g *JavaGen) hasThis(sName string, m *types.Func) bool {
+	sig := m.Type().(*types.Signature)
+	params := sig.Params()
+	if params.Len() == 0 {
+		return false
+	}
+	v := params.At(0)
+	if v.Name() != "this" {
+		return false
+	}
+	t, ok := v.Type().(*types.Named)
+	if !ok {
+		return false
+	}
+	obj := t.Obj()
+	pkg := obj.Pkg()
+	if pkgFirstElem(pkg) != "Java" {
+		return false
+	}
+	clsName := classNameFor(t)
+	exp := g.javaPkgName(g.Pkg) + "." + sName
+	if clsName != exp {
+		g.errorf("the type %s of the `this` argument to method %s.%s is not %s", clsName, sName, m.Name(), exp)
+		return false
+	}
+	return true
+}
+
 func (g *JavaGen) genConstructor(f *types.Func, n string, jcls bool) {
 	g.Printf("public %s(", n)
 	g.genFuncArgs(f, nil, false)
@@ -1386,7 +1498,7 @@
 		for _, m := range exportedMethodSet(types.NewPointer(s.obj.Type())) {
 			var jm *java.Func
 			if jinf != nil {
-				jm = jinf.methods[m.Name()]
+				jm = jinf.lookupMethod(m, g.hasThis(s.obj.Name(), m))
 			}
 			g.genJNIFunc(m, sName, jm, false, jinf != nil)
 		}
diff --git a/bind/testdata/classes.go b/bind/testdata/classes.go
index 0dead86..7263ac7 100644
--- a/bind/testdata/classes.go
+++ b/bind/testdata/classes.go
@@ -36,12 +36,13 @@
 	concurrent.Future
 }
 
-func (_ *Future) Get() lang.Object {
-	return nil
+func (_ *Future) Get() (lang.Object, error) {
+	return nil, nil
 }
 
-func (_ *Future) Get2(_ int64, _ concurrent.TimeUnit) lang.Object {
-	return nil
+// Use a trailing underscore to override multiple overloaded methods.
+func (_ *Future) Get_(_ int64, _ concurrent.TimeUnit) (lang.Object, error) {
+	return nil, nil
 }
 
 type Object struct {
@@ -51,7 +52,7 @@
 func innerClassTypes() {
 	// java.util.Spliterators.iterator use inner class types
 	// for the return value as well as parameters.
-	Spliterators.Iterator_Ljava_util_Spliterator_00024OfInt_2(nil)
+	Spliterators.Iterator(nil)
 }
 
 func returnType() {
diff --git a/bind/testdata/classes.go.golden b/bind/testdata/classes.go.golden
index c0c20cd..5690ea6 100644
--- a/bind/testdata/classes.go.golden
+++ b/bind/testdata/classes.go.golden
@@ -131,7 +131,7 @@
 )
 
 var (
-	Iterator_Ljava_util_Spliterator_00024OfInt_2 func(a0 Java.Java_util_Spliterator_OfInt) Java.Java_util_PrimitiveIterator_OfInt
+	Iterator func(a0 Java.Java_util_Spliterator) Java.Java_util_Iterator
 	// Cast takes a proxy for a Java object and converts it to a java.util.Spliterators proxy.
 	// Cast panics if the argument is not a proxy or if the underlying object does
 	// not extend or implement java.util.Spliterators.
@@ -147,6 +147,8 @@
 const _ = Java.Dummy
 
 type Spliterators Java.Java_util_Spliterators
+type Iterator Java.Java_util_Iterator
+type Spliterator Java.Java_util_Spliterator
 // File is generated by gobind. Do not edit.
 
 package System
@@ -252,6 +254,53 @@
 
 // File is generated by gobind. Do not edit.
 
+package Iterator
+
+import "Java"
+
+const _ = Java.Dummy
+
+const (
+)
+
+var (
+	// Cast takes a proxy for a Java object and converts it to a java.util.Iterator proxy.
+	// Cast panics if the argument is not a proxy or if the underlying object does
+	// not extend or implement java.util.Iterator.
+	Cast func(v interface{}) Java.Java_util_Iterator
+)
+
+// File is generated by gobind. Do not edit.
+
+package Spliterator
+
+import "Java"
+
+const _ = Java.Dummy
+
+type OfInt Java.Java_util_Spliterator_OfInt
+type OfLong Java.Java_util_Spliterator_OfLong
+type OfDouble Java.Java_util_Spliterator_OfDouble
+const (
+	ORDERED = 16
+	DISTINCT = 1
+	SORTED = 4
+	SIZED = 64
+	NONNULL = 256
+	IMMUTABLE = 1024
+	CONCURRENT = 4096
+	SUBSIZED = 16384
+)
+
+var (
+	// Cast takes a proxy for a Java object and converts it to a java.util.Spliterator proxy.
+	// Cast panics if the argument is not a proxy or if the underlying object does
+	// not extend or implement java.util.Spliterator.
+	Cast func(v interface{}) Java.Java_util_Spliterator
+)
+
+// File is generated by gobind. Do not edit.
+
 package OfInt
 
 import "Java"
@@ -277,6 +326,8 @@
 const _ = Java.Dummy
 
 type OfInt Java.Java_util_PrimitiveIterator_OfInt
+type OfLong Java.Java_util_PrimitiveIterator_OfLong
+type OfDouble Java.Java_util_PrimitiveIterator_OfDouble
 // File is generated by gobind. Do not edit.
 
 package OfInt
@@ -297,13 +348,76 @@
 
 // File is generated by gobind. Do not edit.
 
-package Spliterator
+package OfLong
 
 import "Java"
 
 const _ = Java.Dummy
 
-type OfInt Java.Java_util_Spliterator_OfInt
+const (
+)
+
+var (
+	// Cast takes a proxy for a Java object and converts it to a java.util.PrimitiveIterator.OfLong proxy.
+	// Cast panics if the argument is not a proxy or if the underlying object does
+	// not extend or implement java.util.PrimitiveIterator.OfLong.
+	Cast func(v interface{}) Java.Java_util_PrimitiveIterator_OfLong
+)
+
+// File is generated by gobind. Do not edit.
+
+package OfLong
+
+import "Java"
+
+const _ = Java.Dummy
+
+const (
+)
+
+var (
+	// Cast takes a proxy for a Java object and converts it to a java.util.Spliterator.OfLong proxy.
+	// Cast panics if the argument is not a proxy or if the underlying object does
+	// not extend or implement java.util.Spliterator.OfLong.
+	Cast func(v interface{}) Java.Java_util_Spliterator_OfLong
+)
+
+// File is generated by gobind. Do not edit.
+
+package OfDouble
+
+import "Java"
+
+const _ = Java.Dummy
+
+const (
+)
+
+var (
+	// Cast takes a proxy for a Java object and converts it to a java.util.PrimitiveIterator.OfDouble proxy.
+	// Cast panics if the argument is not a proxy or if the underlying object does
+	// not extend or implement java.util.PrimitiveIterator.OfDouble.
+	Cast func(v interface{}) Java.Java_util_PrimitiveIterator_OfDouble
+)
+
+// File is generated by gobind. Do not edit.
+
+package OfDouble
+
+import "Java"
+
+const _ = Java.Dummy
+
+const (
+)
+
+var (
+	// Cast takes a proxy for a Java object and converts it to a java.util.Spliterator.OfDouble proxy.
+	// Cast panics if the argument is not a proxy or if the underlying object does
+	// not extend or implement java.util.Spliterator.OfDouble.
+	Cast func(v interface{}) Java.Java_util_Spliterator_OfDouble
+)
+
 // File is generated by gobind. Do not edit.
 
 package Console
@@ -334,12 +448,11 @@
 }
 
 type Java_io_InputStream interface {
-	Read() (int32, error)
+	Read(a0 ...interface{}) (int32, error)
 }
 
 type Java_util_concurrent_Future interface {
-	Get() (Java_lang_Object, error)
-	Get2(a0 int64, a1 Java_util_concurrent_TimeUnit) (Java_lang_Object, error)
+	Get(a0 ...interface{}) (Java_lang_Object, error)
 }
 
 type Java_lang_Object interface {
@@ -355,13 +468,12 @@
 }
 
 type Java_Future interface {
-	Get() (Java_lang_Object, error)
-	Get2(a0 int64, a1 Java_util_concurrent_TimeUnit) (Java_lang_Object, error)
+	Get(a0 ...interface{}) (Java_lang_Object, error)
 	Super() Java_Future
 }
 
 type Java_InputStream interface {
-	Read() (int32, error)
+	Read(a0 ...interface{}) (int32, error)
 	Super() Java_InputStream
 }
 
@@ -374,12 +486,30 @@
 	Super() Java_Runnable
 }
 
+type Java_util_Iterator interface {
+}
+
+type Java_util_Spliterator interface {
+}
+
 type Java_util_PrimitiveIterator_OfInt interface {
 }
 
 type Java_util_Spliterator_OfInt interface {
 }
 
+type Java_util_PrimitiveIterator_OfLong interface {
+}
+
+type Java_util_Spliterator_OfLong interface {
+}
+
+type Java_util_PrimitiveIterator_OfDouble interface {
+}
+
+type Java_util_Spliterator_OfDouble interface {
+}
+
 type Java_io_Console interface {
 	Flush() error
 }
@@ -412,8 +542,14 @@
 import "Java/java/InputStream"
 import "Java/java/Object"
 import "Java/java/Runnable"
+import "Java/java/util/Iterator"
+import "Java/java/util/Spliterator"
 import "Java/java/util/PrimitiveIterator/OfInt"
 import "Java/java/util/Spliterator/OfInt"
+import "Java/java/util/PrimitiveIterator/OfLong"
+import "Java/java/util/Spliterator/OfLong"
+import "Java/java/util/PrimitiveIterator/OfDouble"
+import "Java/java/util/Spliterator/OfDouble"
 import "Java/java/io/Console"
 import "unsafe"
 
@@ -442,8 +578,14 @@
 	init_java_InputStream()
 	init_java_Object()
 	init_java_Runnable()
+	init_java_util_Iterator()
+	init_java_util_Spliterator()
 	init_java_util_PrimitiveIterator_OfInt()
 	init_java_util_Spliterator_OfInt()
+	init_java_util_PrimitiveIterator_OfLong()
+	init_java_util_Spliterator_OfLong()
+	init_java_util_PrimitiveIterator_OfDouble()
+	init_java_util_Spliterator_OfDouble()
 	init_java_io_Console()
 }
 
@@ -511,19 +653,61 @@
 
 func (p *proxy_class_java_io_InputStream) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
-func (p *proxy_class_java_io_InputStream) Read() (int32, error) {
-	res := C.cproxy_java_io_InputStream_read__(C.jint(p.Bind_proxy_refnum__()))
-	_res := int32(res.res)
-	var _exc error
-	_exc_ref := _seq.FromRefNum(int32(res.exc))
-	if _exc_ref != nil {
-		if res.exc < 0 { // go object
-			_exc = _exc_ref.Get().(error)
-		} else { // foreign object
-			_exc = (*proxy_error)(_exc_ref)
+func (p *proxy_class_java_io_InputStream) Read(a0 ...interface{}) (int32, error) {
+	switch 0 + len(a0) {
+	case 0:
+		res := C.cproxy_java_io_InputStream_read__(C.jint(p.Bind_proxy_refnum__()))
+		_res := int32(res.res)
+		var _exc error
+		_exc_ref := _seq.FromRefNum(int32(res.exc))
+		if _exc_ref != nil {
+			if res.exc < 0 { // go object
+				_exc = _exc_ref.Get().(error)
+			} else { // foreign object
+				_exc = (*proxy_error)(_exc_ref)
+			}
+		}
+		return _res, _exc
+	case 1:
+		_a0, ok0 := a0[0].([]byte)
+		if ok0 {
+			__a0 := fromSlice(_a0, false)
+			res := C.cproxy_java_io_InputStream_read___3B(C.jint(p.Bind_proxy_refnum__()), __a0)
+			_res := int32(res.res)
+			var _exc error
+			_exc_ref := _seq.FromRefNum(int32(res.exc))
+			if _exc_ref != nil {
+				if res.exc < 0 { // go object
+					_exc = _exc_ref.Get().(error)
+				} else { // foreign object
+					_exc = (*proxy_error)(_exc_ref)
+				}
+			}
+			return _res, _exc
+		}
+	case 3:
+		_a0, ok0 := a0[0].([]byte)
+		_a1, ok1 := a0[1].(int32)
+		_a2, ok2 := a0[2].(int32)
+		if ok0 && ok1 && ok2 {
+			__a0 := fromSlice(_a0, false)
+			__a1 := C.jint(_a1)
+			__a2 := C.jint(_a2)
+			res := C.cproxy_java_io_InputStream_read___3BII(C.jint(p.Bind_proxy_refnum__()), __a0, __a1, __a2)
+			_res := int32(res.res)
+			var _exc error
+			_exc_ref := _seq.FromRefNum(int32(res.exc))
+			if _exc_ref != nil {
+				if res.exc < 0 { // go object
+					_exc = _exc_ref.Get().(error)
+				} else { // foreign object
+					_exc = (*proxy_error)(_exc_ref)
+				}
+			}
+			return _res, _exc
 		}
 	}
-	return _res, _exc
+	panic("no overloaded method found for java.io.InputStream.read that matched the arguments")
 }
 
 var class_java_util_concurrent_Future C.jclass
@@ -551,55 +735,61 @@
 
 func (p *proxy_class_java_util_concurrent_Future) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
-func (p *proxy_class_java_util_concurrent_Future) Get() (Java.Java_lang_Object, error) {
-	res := C.cproxy_java_util_concurrent_Future_get__(C.jint(p.Bind_proxy_refnum__()))
-	var _res Java.Java_lang_Object
-	_res_ref := _seq.FromRefNum(int32(res.res))
-	if _res_ref != nil {
-		if res.res < 0 { // go object
-			_res = _res_ref.Get().(Java.Java_lang_Object)
-		} else { // foreign object
-			_res = (*proxy_class_java_lang_Object)(_res_ref)
+func (p *proxy_class_java_util_concurrent_Future) Get(a0 ...interface{}) (Java.Java_lang_Object, error) {
+	switch 0 + len(a0) {
+	case 0:
+		res := C.cproxy_java_util_concurrent_Future_get__(C.jint(p.Bind_proxy_refnum__()))
+		var _res Java.Java_lang_Object
+		_res_ref := _seq.FromRefNum(int32(res.res))
+		if _res_ref != nil {
+			if res.res < 0 { // go object
+				_res = _res_ref.Get().(Java.Java_lang_Object)
+			} else { // foreign object
+				_res = (*proxy_class_java_lang_Object)(_res_ref)
+			}
+		}
+		var _exc error
+		_exc_ref := _seq.FromRefNum(int32(res.exc))
+		if _exc_ref != nil {
+			if res.exc < 0 { // go object
+				_exc = _exc_ref.Get().(error)
+			} else { // foreign object
+				_exc = (*proxy_error)(_exc_ref)
+			}
+		}
+		return _res, _exc
+	case 2:
+		_a0, ok0 := a0[0].(int64)
+		_a1, ok1 := a0[1].(Java.Java_util_concurrent_TimeUnit)
+		if ok0 && ok1 {
+			__a0 := C.jlong(_a0)
+			var __a1 C.jint = _seq.NullRefNum
+			if _a1 != nil {
+				__a1 = C.jint(_seq.ToRefNum(_a1))
+			}
+			res := C.cproxy_java_util_concurrent_Future_get__JLjava_util_concurrent_TimeUnit_2(C.jint(p.Bind_proxy_refnum__()), __a0, __a1)
+			var _res Java.Java_lang_Object
+			_res_ref := _seq.FromRefNum(int32(res.res))
+			if _res_ref != nil {
+				if res.res < 0 { // go object
+					_res = _res_ref.Get().(Java.Java_lang_Object)
+				} else { // foreign object
+					_res = (*proxy_class_java_lang_Object)(_res_ref)
+				}
+			}
+			var _exc error
+			_exc_ref := _seq.FromRefNum(int32(res.exc))
+			if _exc_ref != nil {
+				if res.exc < 0 { // go object
+					_exc = _exc_ref.Get().(error)
+				} else { // foreign object
+					_exc = (*proxy_error)(_exc_ref)
+				}
+			}
+			return _res, _exc
 		}
 	}
-	var _exc error
-	_exc_ref := _seq.FromRefNum(int32(res.exc))
-	if _exc_ref != nil {
-		if res.exc < 0 { // go object
-			_exc = _exc_ref.Get().(error)
-		} else { // foreign object
-			_exc = (*proxy_error)(_exc_ref)
-		}
-	}
-	return _res, _exc
-}
-
-func (p *proxy_class_java_util_concurrent_Future) Get2(a0 int64, a1 Java.Java_util_concurrent_TimeUnit) (Java.Java_lang_Object, error) {
-	_a0 := C.jlong(a0)
-	var _a1 C.jint = _seq.NullRefNum
-	if a1 != nil {
-		_a1 = C.jint(_seq.ToRefNum(a1))
-	}
-	res := C.cproxy_java_util_concurrent_Future_get__JLjava_util_concurrent_TimeUnit_2(C.jint(p.Bind_proxy_refnum__()), _a0, _a1)
-	var _res Java.Java_lang_Object
-	_res_ref := _seq.FromRefNum(int32(res.res))
-	if _res_ref != nil {
-		if res.res < 0 { // go object
-			_res = _res_ref.Get().(Java.Java_lang_Object)
-		} else { // foreign object
-			_res = (*proxy_class_java_lang_Object)(_res_ref)
-		}
-	}
-	var _exc error
-	_exc_ref := _seq.FromRefNum(int32(res.exc))
-	if _exc_ref != nil {
-		if res.exc < 0 { // go object
-			_exc = _exc_ref.Get().(error)
-		} else { // foreign object
-			_exc = (*proxy_error)(_exc_ref)
-		}
-	}
-	return _res, _exc
+	panic("no overloaded method found for java.util.concurrent.Future.get that matched the arguments")
 }
 
 var class_java_lang_Object C.jclass
@@ -662,32 +852,125 @@
 		return
 	}
 	class_java_util_Spliterators = clazz
-	Spliterators.Iterator_Ljava_util_Spliterator_00024OfInt_2 = func(a0 Java.Java_util_Spliterator_OfInt) Java.Java_util_PrimitiveIterator_OfInt {
-		var _a0 C.jint = _seq.NullRefNum
-		if a0 != nil {
-			_a0 = C.jint(_seq.ToRefNum(a0))
+	Spliterators.Iterator = func(a0 Java.Java_util_Spliterator) Java.Java_util_Iterator {
+		{
+			var __a0 C.jint = _seq.NullRefNum
+			if a0 != nil {
+				__a0 = C.jint(_seq.ToRefNum(a0))
+			}
+			res := C.cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2(__a0)
+			var _res Java.Java_util_Iterator
+			_res_ref := _seq.FromRefNum(int32(res.res))
+			if _res_ref != nil {
+				if res.res < 0 { // go object
+					_res = _res_ref.Get().(Java.Java_util_Iterator)
+				} else { // foreign object
+					_res = (*proxy_class_java_util_Iterator)(_res_ref)
+				}
+			}
+			var _exc error
+			_exc_ref := _seq.FromRefNum(int32(res.exc))
+			if _exc_ref != nil {
+				if res.exc < 0 { // go object
+					_exc = _exc_ref.Get().(error)
+				} else { // foreign object
+					_exc = (*proxy_error)(_exc_ref)
+				}
+			}
+			if (_exc != nil) { panic(_exc) }
+			return _res
 		}
-		res := C.cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2(_a0)
-		var _res Java.Java_util_PrimitiveIterator_OfInt
-		_res_ref := _seq.FromRefNum(int32(res.res))
-		if _res_ref != nil {
-			if res.res < 0 { // go object
-				_res = _res_ref.Get().(Java.Java_util_PrimitiveIterator_OfInt)
-			} else { // foreign object
-				_res = (*proxy_class_java_util_PrimitiveIterator_OfInt)(_res_ref)
+		{
+			_a0, ok0 := a0.(Java.Java_util_Spliterator_OfInt)
+			if ok0 {
+				var __a0 C.jint = _seq.NullRefNum
+				if _a0 != nil {
+					__a0 = C.jint(_seq.ToRefNum(_a0))
+				}
+				res := C.cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2(__a0)
+				var _res Java.Java_util_PrimitiveIterator_OfInt
+				_res_ref := _seq.FromRefNum(int32(res.res))
+				if _res_ref != nil {
+					if res.res < 0 { // go object
+						_res = _res_ref.Get().(Java.Java_util_PrimitiveIterator_OfInt)
+					} else { // foreign object
+						_res = (*proxy_class_java_util_PrimitiveIterator_OfInt)(_res_ref)
+					}
+				}
+				var _exc error
+				_exc_ref := _seq.FromRefNum(int32(res.exc))
+				if _exc_ref != nil {
+					if res.exc < 0 { // go object
+						_exc = _exc_ref.Get().(error)
+					} else { // foreign object
+						_exc = (*proxy_error)(_exc_ref)
+					}
+				}
+				if (_exc != nil) { panic(_exc) }
+				return _res
 			}
 		}
-		var _exc error
-		_exc_ref := _seq.FromRefNum(int32(res.exc))
-		if _exc_ref != nil {
-			if res.exc < 0 { // go object
-				_exc = _exc_ref.Get().(error)
-			} else { // foreign object
-				_exc = (*proxy_error)(_exc_ref)
+		{
+			_a0, ok0 := a0.(Java.Java_util_Spliterator_OfLong)
+			if ok0 {
+				var __a0 C.jint = _seq.NullRefNum
+				if _a0 != nil {
+					__a0 = C.jint(_seq.ToRefNum(_a0))
+				}
+				res := C.cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2(__a0)
+				var _res Java.Java_util_PrimitiveIterator_OfLong
+				_res_ref := _seq.FromRefNum(int32(res.res))
+				if _res_ref != nil {
+					if res.res < 0 { // go object
+						_res = _res_ref.Get().(Java.Java_util_PrimitiveIterator_OfLong)
+					} else { // foreign object
+						_res = (*proxy_class_java_util_PrimitiveIterator_OfLong)(_res_ref)
+					}
+				}
+				var _exc error
+				_exc_ref := _seq.FromRefNum(int32(res.exc))
+				if _exc_ref != nil {
+					if res.exc < 0 { // go object
+						_exc = _exc_ref.Get().(error)
+					} else { // foreign object
+						_exc = (*proxy_error)(_exc_ref)
+					}
+				}
+				if (_exc != nil) { panic(_exc) }
+				return _res
 			}
 		}
-		if (_exc != nil) { panic(_exc) }
-		return _res
+		{
+			_a0, ok0 := a0.(Java.Java_util_Spliterator_OfDouble)
+			if ok0 {
+				var __a0 C.jint = _seq.NullRefNum
+				if _a0 != nil {
+					__a0 = C.jint(_seq.ToRefNum(_a0))
+				}
+				res := C.cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2(__a0)
+				var _res Java.Java_util_PrimitiveIterator_OfDouble
+				_res_ref := _seq.FromRefNum(int32(res.res))
+				if _res_ref != nil {
+					if res.res < 0 { // go object
+						_res = _res_ref.Get().(Java.Java_util_PrimitiveIterator_OfDouble)
+					} else { // foreign object
+						_res = (*proxy_class_java_util_PrimitiveIterator_OfDouble)(_res_ref)
+					}
+				}
+				var _exc error
+				_exc_ref := _seq.FromRefNum(int32(res.exc))
+				if _exc_ref != nil {
+					if res.exc < 0 { // go object
+						_exc = _exc_ref.Get().(error)
+					} else { // foreign object
+						_exc = (*proxy_error)(_exc_ref)
+					}
+				}
+				if (_exc != nil) { panic(_exc) }
+				return _res
+			}
+		}
+		panic("no overloaded method found for java.util.Spliterators.iterator that matched the arguments")
 	}
 	
 	Spliterators.Cast = func(v interface{}) Java.Java_util_Spliterators {
@@ -779,55 +1062,61 @@
 
 func (p *proxy_class_java_Future) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
-func (p *proxy_class_java_Future) Get() (Java.Java_lang_Object, error) {
-	res := C.cproxy_java_Future_get__(C.jint(p.Bind_proxy_refnum__()))
-	var _res Java.Java_lang_Object
-	_res_ref := _seq.FromRefNum(int32(res.res))
-	if _res_ref != nil {
-		if res.res < 0 { // go object
-			_res = _res_ref.Get().(Java.Java_lang_Object)
-		} else { // foreign object
-			_res = (*proxy_class_java_lang_Object)(_res_ref)
+func (p *proxy_class_java_Future) Get(a0 ...interface{}) (Java.Java_lang_Object, error) {
+	switch 0 + len(a0) {
+	case 0:
+		res := C.cproxy_java_Future_get__(C.jint(p.Bind_proxy_refnum__()))
+		var _res Java.Java_lang_Object
+		_res_ref := _seq.FromRefNum(int32(res.res))
+		if _res_ref != nil {
+			if res.res < 0 { // go object
+				_res = _res_ref.Get().(Java.Java_lang_Object)
+			} else { // foreign object
+				_res = (*proxy_class_java_lang_Object)(_res_ref)
+			}
+		}
+		var _exc error
+		_exc_ref := _seq.FromRefNum(int32(res.exc))
+		if _exc_ref != nil {
+			if res.exc < 0 { // go object
+				_exc = _exc_ref.Get().(error)
+			} else { // foreign object
+				_exc = (*proxy_error)(_exc_ref)
+			}
+		}
+		return _res, _exc
+	case 2:
+		_a0, ok0 := a0[0].(int64)
+		_a1, ok1 := a0[1].(Java.Java_util_concurrent_TimeUnit)
+		if ok0 && ok1 {
+			__a0 := C.jlong(_a0)
+			var __a1 C.jint = _seq.NullRefNum
+			if _a1 != nil {
+				__a1 = C.jint(_seq.ToRefNum(_a1))
+			}
+			res := C.cproxy_java_Future_get__JLjava_util_concurrent_TimeUnit_2(C.jint(p.Bind_proxy_refnum__()), __a0, __a1)
+			var _res Java.Java_lang_Object
+			_res_ref := _seq.FromRefNum(int32(res.res))
+			if _res_ref != nil {
+				if res.res < 0 { // go object
+					_res = _res_ref.Get().(Java.Java_lang_Object)
+				} else { // foreign object
+					_res = (*proxy_class_java_lang_Object)(_res_ref)
+				}
+			}
+			var _exc error
+			_exc_ref := _seq.FromRefNum(int32(res.exc))
+			if _exc_ref != nil {
+				if res.exc < 0 { // go object
+					_exc = _exc_ref.Get().(error)
+				} else { // foreign object
+					_exc = (*proxy_error)(_exc_ref)
+				}
+			}
+			return _res, _exc
 		}
 	}
-	var _exc error
-	_exc_ref := _seq.FromRefNum(int32(res.exc))
-	if _exc_ref != nil {
-		if res.exc < 0 { // go object
-			_exc = _exc_ref.Get().(error)
-		} else { // foreign object
-			_exc = (*proxy_error)(_exc_ref)
-		}
-	}
-	return _res, _exc
-}
-
-func (p *proxy_class_java_Future) Get2(a0 int64, a1 Java.Java_util_concurrent_TimeUnit) (Java.Java_lang_Object, error) {
-	_a0 := C.jlong(a0)
-	var _a1 C.jint = _seq.NullRefNum
-	if a1 != nil {
-		_a1 = C.jint(_seq.ToRefNum(a1))
-	}
-	res := C.cproxy_java_Future_get__JLjava_util_concurrent_TimeUnit_2(C.jint(p.Bind_proxy_refnum__()), _a0, _a1)
-	var _res Java.Java_lang_Object
-	_res_ref := _seq.FromRefNum(int32(res.res))
-	if _res_ref != nil {
-		if res.res < 0 { // go object
-			_res = _res_ref.Get().(Java.Java_lang_Object)
-		} else { // foreign object
-			_res = (*proxy_class_java_lang_Object)(_res_ref)
-		}
-	}
-	var _exc error
-	_exc_ref := _seq.FromRefNum(int32(res.exc))
-	if _exc_ref != nil {
-		if res.exc < 0 { // go object
-			_exc = _exc_ref.Get().(error)
-		} else { // foreign object
-			_exc = (*proxy_error)(_exc_ref)
-		}
-	}
-	return _res, _exc
+	panic("no overloaded method found for java.Future.get that matched the arguments")
 }
 
 func (p *proxy_class_java_Future) Super() Java.Java_Future {
@@ -836,55 +1125,61 @@
 
 type super_java_Future struct {*proxy_class_java_Future}
 
-func (p *super_java_Future) Get() (Java.Java_lang_Object, error) {
-	res := C.csuper_java_Future_get__(C.jint(p.Bind_proxy_refnum__()))
-	var _res Java.Java_lang_Object
-	_res_ref := _seq.FromRefNum(int32(res.res))
-	if _res_ref != nil {
-		if res.res < 0 { // go object
-			_res = _res_ref.Get().(Java.Java_lang_Object)
-		} else { // foreign object
-			_res = (*proxy_class_java_lang_Object)(_res_ref)
+func (p *super_java_Future) Get(a0 ...interface{}) (Java.Java_lang_Object, error) {
+	switch 0 + len(a0) {
+	case 0:
+		res := C.csuper_java_Future_get__(C.jint(p.Bind_proxy_refnum__()))
+		var _res Java.Java_lang_Object
+		_res_ref := _seq.FromRefNum(int32(res.res))
+		if _res_ref != nil {
+			if res.res < 0 { // go object
+				_res = _res_ref.Get().(Java.Java_lang_Object)
+			} else { // foreign object
+				_res = (*proxy_class_java_lang_Object)(_res_ref)
+			}
+		}
+		var _exc error
+		_exc_ref := _seq.FromRefNum(int32(res.exc))
+		if _exc_ref != nil {
+			if res.exc < 0 { // go object
+				_exc = _exc_ref.Get().(error)
+			} else { // foreign object
+				_exc = (*proxy_error)(_exc_ref)
+			}
+		}
+		return _res, _exc
+	case 2:
+		_a0, ok0 := a0[0].(int64)
+		_a1, ok1 := a0[1].(Java.Java_util_concurrent_TimeUnit)
+		if ok0 && ok1 {
+			__a0 := C.jlong(_a0)
+			var __a1 C.jint = _seq.NullRefNum
+			if _a1 != nil {
+				__a1 = C.jint(_seq.ToRefNum(_a1))
+			}
+			res := C.csuper_java_Future_get__JLjava_util_concurrent_TimeUnit_2(C.jint(p.Bind_proxy_refnum__()), __a0, __a1)
+			var _res Java.Java_lang_Object
+			_res_ref := _seq.FromRefNum(int32(res.res))
+			if _res_ref != nil {
+				if res.res < 0 { // go object
+					_res = _res_ref.Get().(Java.Java_lang_Object)
+				} else { // foreign object
+					_res = (*proxy_class_java_lang_Object)(_res_ref)
+				}
+			}
+			var _exc error
+			_exc_ref := _seq.FromRefNum(int32(res.exc))
+			if _exc_ref != nil {
+				if res.exc < 0 { // go object
+					_exc = _exc_ref.Get().(error)
+				} else { // foreign object
+					_exc = (*proxy_error)(_exc_ref)
+				}
+			}
+			return _res, _exc
 		}
 	}
-	var _exc error
-	_exc_ref := _seq.FromRefNum(int32(res.exc))
-	if _exc_ref != nil {
-		if res.exc < 0 { // go object
-			_exc = _exc_ref.Get().(error)
-		} else { // foreign object
-			_exc = (*proxy_error)(_exc_ref)
-		}
-	}
-	return _res, _exc
-}
-
-func (p *super_java_Future) Get2(a0 int64, a1 Java.Java_util_concurrent_TimeUnit) (Java.Java_lang_Object, error) {
-	_a0 := C.jlong(a0)
-	var _a1 C.jint = _seq.NullRefNum
-	if a1 != nil {
-		_a1 = C.jint(_seq.ToRefNum(a1))
-	}
-	res := C.csuper_java_Future_get__JLjava_util_concurrent_TimeUnit_2(C.jint(p.Bind_proxy_refnum__()), _a0, _a1)
-	var _res Java.Java_lang_Object
-	_res_ref := _seq.FromRefNum(int32(res.res))
-	if _res_ref != nil {
-		if res.res < 0 { // go object
-			_res = _res_ref.Get().(Java.Java_lang_Object)
-		} else { // foreign object
-			_res = (*proxy_class_java_lang_Object)(_res_ref)
-		}
-	}
-	var _exc error
-	_exc_ref := _seq.FromRefNum(int32(res.exc))
-	if _exc_ref != nil {
-		if res.exc < 0 { // go object
-			_exc = _exc_ref.Get().(error)
-		} else { // foreign object
-			_exc = (*proxy_error)(_exc_ref)
-		}
-	}
-	return _res, _exc
+	panic("no overloaded method found for java.Future.get that matched the arguments")
 }
 
 var class_java_InputStream C.jclass
@@ -912,19 +1207,61 @@
 
 func (p *proxy_class_java_InputStream) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
-func (p *proxy_class_java_InputStream) Read() (int32, error) {
-	res := C.cproxy_java_InputStream_read__(C.jint(p.Bind_proxy_refnum__()))
-	_res := int32(res.res)
-	var _exc error
-	_exc_ref := _seq.FromRefNum(int32(res.exc))
-	if _exc_ref != nil {
-		if res.exc < 0 { // go object
-			_exc = _exc_ref.Get().(error)
-		} else { // foreign object
-			_exc = (*proxy_error)(_exc_ref)
+func (p *proxy_class_java_InputStream) Read(a0 ...interface{}) (int32, error) {
+	switch 0 + len(a0) {
+	case 0:
+		res := C.cproxy_java_InputStream_read__(C.jint(p.Bind_proxy_refnum__()))
+		_res := int32(res.res)
+		var _exc error
+		_exc_ref := _seq.FromRefNum(int32(res.exc))
+		if _exc_ref != nil {
+			if res.exc < 0 { // go object
+				_exc = _exc_ref.Get().(error)
+			} else { // foreign object
+				_exc = (*proxy_error)(_exc_ref)
+			}
+		}
+		return _res, _exc
+	case 1:
+		_a0, ok0 := a0[0].([]byte)
+		if ok0 {
+			__a0 := fromSlice(_a0, false)
+			res := C.cproxy_java_InputStream_read___3B(C.jint(p.Bind_proxy_refnum__()), __a0)
+			_res := int32(res.res)
+			var _exc error
+			_exc_ref := _seq.FromRefNum(int32(res.exc))
+			if _exc_ref != nil {
+				if res.exc < 0 { // go object
+					_exc = _exc_ref.Get().(error)
+				} else { // foreign object
+					_exc = (*proxy_error)(_exc_ref)
+				}
+			}
+			return _res, _exc
+		}
+	case 3:
+		_a0, ok0 := a0[0].([]byte)
+		_a1, ok1 := a0[1].(int32)
+		_a2, ok2 := a0[2].(int32)
+		if ok0 && ok1 && ok2 {
+			__a0 := fromSlice(_a0, false)
+			__a1 := C.jint(_a1)
+			__a2 := C.jint(_a2)
+			res := C.cproxy_java_InputStream_read___3BII(C.jint(p.Bind_proxy_refnum__()), __a0, __a1, __a2)
+			_res := int32(res.res)
+			var _exc error
+			_exc_ref := _seq.FromRefNum(int32(res.exc))
+			if _exc_ref != nil {
+				if res.exc < 0 { // go object
+					_exc = _exc_ref.Get().(error)
+				} else { // foreign object
+					_exc = (*proxy_error)(_exc_ref)
+				}
+			}
+			return _res, _exc
 		}
 	}
-	return _res, _exc
+	panic("no overloaded method found for java.InputStream.read that matched the arguments")
 }
 
 func (p *proxy_class_java_InputStream) Super() Java.Java_InputStream {
@@ -933,19 +1270,61 @@
 
 type super_java_InputStream struct {*proxy_class_java_InputStream}
 
-func (p *super_java_InputStream) Read() (int32, error) {
-	res := C.csuper_java_InputStream_read__(C.jint(p.Bind_proxy_refnum__()))
-	_res := int32(res.res)
-	var _exc error
-	_exc_ref := _seq.FromRefNum(int32(res.exc))
-	if _exc_ref != nil {
-		if res.exc < 0 { // go object
-			_exc = _exc_ref.Get().(error)
-		} else { // foreign object
-			_exc = (*proxy_error)(_exc_ref)
+func (p *super_java_InputStream) Read(a0 ...interface{}) (int32, error) {
+	switch 0 + len(a0) {
+	case 0:
+		res := C.csuper_java_InputStream_read__(C.jint(p.Bind_proxy_refnum__()))
+		_res := int32(res.res)
+		var _exc error
+		_exc_ref := _seq.FromRefNum(int32(res.exc))
+		if _exc_ref != nil {
+			if res.exc < 0 { // go object
+				_exc = _exc_ref.Get().(error)
+			} else { // foreign object
+				_exc = (*proxy_error)(_exc_ref)
+			}
+		}
+		return _res, _exc
+	case 1:
+		_a0, ok0 := a0[0].([]byte)
+		if ok0 {
+			__a0 := fromSlice(_a0, false)
+			res := C.csuper_java_InputStream_read___3B(C.jint(p.Bind_proxy_refnum__()), __a0)
+			_res := int32(res.res)
+			var _exc error
+			_exc_ref := _seq.FromRefNum(int32(res.exc))
+			if _exc_ref != nil {
+				if res.exc < 0 { // go object
+					_exc = _exc_ref.Get().(error)
+				} else { // foreign object
+					_exc = (*proxy_error)(_exc_ref)
+				}
+			}
+			return _res, _exc
+		}
+	case 3:
+		_a0, ok0 := a0[0].([]byte)
+		_a1, ok1 := a0[1].(int32)
+		_a2, ok2 := a0[2].(int32)
+		if ok0 && ok1 && ok2 {
+			__a0 := fromSlice(_a0, false)
+			__a1 := C.jint(_a1)
+			__a2 := C.jint(_a2)
+			res := C.csuper_java_InputStream_read___3BII(C.jint(p.Bind_proxy_refnum__()), __a0, __a1, __a2)
+			_res := int32(res.res)
+			var _exc error
+			_exc_ref := _seq.FromRefNum(int32(res.exc))
+			if _exc_ref != nil {
+				if res.exc < 0 { // go object
+					_exc = _exc_ref.Get().(error)
+				} else { // foreign object
+					_exc = (*proxy_error)(_exc_ref)
+				}
+			}
+			return _res, _exc
 		}
 	}
-	return _res, _exc
+	panic("no overloaded method found for java.InputStream.read that matched the arguments")
 }
 
 var class_java_Object C.jclass
@@ -1038,6 +1417,56 @@
 	if (_exc != nil) { panic(_exc) }
 }
 
+var class_java_util_Iterator C.jclass
+
+func init_java_util_Iterator() {
+	cls := C.CString("java/util/Iterator")
+	clazz := C.go_seq_find_class(cls)
+	C.free(unsafe.Pointer(cls))
+	if clazz == nil {
+		return
+	}
+	class_java_util_Iterator = clazz
+	Iterator.Cast = func(v interface{}) Java.Java_util_Iterator {
+		t := reflect.TypeOf((*proxy_class_java_util_Iterator)(nil))
+		cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_util_Iterator)
+		ref := C.jint(_seq.ToRefNum(cv))
+		if C.go_seq_isinstanceof(ref, class_java_util_Iterator) != 1 {
+			panic(fmt.Errorf("%T is not an instance of %s", v, "java.util.Iterator"))
+		}
+		return cv
+	}
+}
+
+type proxy_class_java_util_Iterator _seq.Ref
+
+func (p *proxy_class_java_util_Iterator) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
+
+var class_java_util_Spliterator C.jclass
+
+func init_java_util_Spliterator() {
+	cls := C.CString("java/util/Spliterator")
+	clazz := C.go_seq_find_class(cls)
+	C.free(unsafe.Pointer(cls))
+	if clazz == nil {
+		return
+	}
+	class_java_util_Spliterator = clazz
+	Spliterator.Cast = func(v interface{}) Java.Java_util_Spliterator {
+		t := reflect.TypeOf((*proxy_class_java_util_Spliterator)(nil))
+		cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_util_Spliterator)
+		ref := C.jint(_seq.ToRefNum(cv))
+		if C.go_seq_isinstanceof(ref, class_java_util_Spliterator) != 1 {
+			panic(fmt.Errorf("%T is not an instance of %s", v, "java.util.Spliterator"))
+		}
+		return cv
+	}
+}
+
+type proxy_class_java_util_Spliterator _seq.Ref
+
+func (p *proxy_class_java_util_Spliterator) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
+
 var class_java_util_PrimitiveIterator_OfInt C.jclass
 
 func init_java_util_PrimitiveIterator_OfInt() {
@@ -1088,6 +1517,106 @@
 
 func (p *proxy_class_java_util_Spliterator_OfInt) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
 
+var class_java_util_PrimitiveIterator_OfLong C.jclass
+
+func init_java_util_PrimitiveIterator_OfLong() {
+	cls := C.CString("java/util/PrimitiveIterator$OfLong")
+	clazz := C.go_seq_find_class(cls)
+	C.free(unsafe.Pointer(cls))
+	if clazz == nil {
+		return
+	}
+	class_java_util_PrimitiveIterator_OfLong = clazz
+	OfLong.Cast = func(v interface{}) Java.Java_util_PrimitiveIterator_OfLong {
+		t := reflect.TypeOf((*proxy_class_java_util_PrimitiveIterator_OfLong)(nil))
+		cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_util_PrimitiveIterator_OfLong)
+		ref := C.jint(_seq.ToRefNum(cv))
+		if C.go_seq_isinstanceof(ref, class_java_util_PrimitiveIterator_OfLong) != 1 {
+			panic(fmt.Errorf("%T is not an instance of %s", v, "java.util.PrimitiveIterator.OfLong"))
+		}
+		return cv
+	}
+}
+
+type proxy_class_java_util_PrimitiveIterator_OfLong _seq.Ref
+
+func (p *proxy_class_java_util_PrimitiveIterator_OfLong) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
+
+var class_java_util_Spliterator_OfLong C.jclass
+
+func init_java_util_Spliterator_OfLong() {
+	cls := C.CString("java/util/Spliterator$OfLong")
+	clazz := C.go_seq_find_class(cls)
+	C.free(unsafe.Pointer(cls))
+	if clazz == nil {
+		return
+	}
+	class_java_util_Spliterator_OfLong = clazz
+	OfLong.Cast = func(v interface{}) Java.Java_util_Spliterator_OfLong {
+		t := reflect.TypeOf((*proxy_class_java_util_Spliterator_OfLong)(nil))
+		cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_util_Spliterator_OfLong)
+		ref := C.jint(_seq.ToRefNum(cv))
+		if C.go_seq_isinstanceof(ref, class_java_util_Spliterator_OfLong) != 1 {
+			panic(fmt.Errorf("%T is not an instance of %s", v, "java.util.Spliterator.OfLong"))
+		}
+		return cv
+	}
+}
+
+type proxy_class_java_util_Spliterator_OfLong _seq.Ref
+
+func (p *proxy_class_java_util_Spliterator_OfLong) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
+
+var class_java_util_PrimitiveIterator_OfDouble C.jclass
+
+func init_java_util_PrimitiveIterator_OfDouble() {
+	cls := C.CString("java/util/PrimitiveIterator$OfDouble")
+	clazz := C.go_seq_find_class(cls)
+	C.free(unsafe.Pointer(cls))
+	if clazz == nil {
+		return
+	}
+	class_java_util_PrimitiveIterator_OfDouble = clazz
+	OfDouble.Cast = func(v interface{}) Java.Java_util_PrimitiveIterator_OfDouble {
+		t := reflect.TypeOf((*proxy_class_java_util_PrimitiveIterator_OfDouble)(nil))
+		cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_util_PrimitiveIterator_OfDouble)
+		ref := C.jint(_seq.ToRefNum(cv))
+		if C.go_seq_isinstanceof(ref, class_java_util_PrimitiveIterator_OfDouble) != 1 {
+			panic(fmt.Errorf("%T is not an instance of %s", v, "java.util.PrimitiveIterator.OfDouble"))
+		}
+		return cv
+	}
+}
+
+type proxy_class_java_util_PrimitiveIterator_OfDouble _seq.Ref
+
+func (p *proxy_class_java_util_PrimitiveIterator_OfDouble) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
+
+var class_java_util_Spliterator_OfDouble C.jclass
+
+func init_java_util_Spliterator_OfDouble() {
+	cls := C.CString("java/util/Spliterator$OfDouble")
+	clazz := C.go_seq_find_class(cls)
+	C.free(unsafe.Pointer(cls))
+	if clazz == nil {
+		return
+	}
+	class_java_util_Spliterator_OfDouble = clazz
+	OfDouble.Cast = func(v interface{}) Java.Java_util_Spliterator_OfDouble {
+		t := reflect.TypeOf((*proxy_class_java_util_Spliterator_OfDouble)(nil))
+		cv := reflect.ValueOf(v).Convert(t).Interface().(*proxy_class_java_util_Spliterator_OfDouble)
+		ref := C.jint(_seq.ToRefNum(cv))
+		if C.go_seq_isinstanceof(ref, class_java_util_Spliterator_OfDouble) != 1 {
+			panic(fmt.Errorf("%T is not an instance of %s", v, "java.util.Spliterator.OfDouble"))
+		}
+		return cv
+	}
+}
+
+type proxy_class_java_util_Spliterator_OfDouble _seq.Ref
+
+func (p *proxy_class_java_util_Spliterator_OfDouble) Bind_proxy_refnum__() int32 { return (*_seq.Ref)(p).Bind_IncNum() }
+
 var class_java_io_Console C.jclass
 
 func init_java_io_Console() {
@@ -1181,19 +1710,23 @@
 }
 
 //export proxyjava_Future_Get
-func proxyjava_Future_Get(refnum C.int32_t) C.int32_t {
+func proxyjava_Future_Get(refnum C.int32_t) (C.int32_t, C.int32_t) {
 	ref := _seq.FromRefNum(int32(refnum))
 	v := ref.Get().(*java.Future)
-	res_0 := v.Get()
+	res_0, res_1 := v.Get()
 	var _res_0 C.int32_t = _seq.NullRefNum
 	if res_0 != nil {
 		_res_0 = C.int32_t(_seq.ToRefNum(res_0))
 	}
-	return _res_0
+	var _res_1 C.int32_t = _seq.NullRefNum
+	if res_1 != nil {
+		_res_1 = C.int32_t(_seq.ToRefNum(res_1))
+	}
+	return _res_0, _res_1
 }
 
-//export proxyjava_Future_Get2
-func proxyjava_Future_Get2(refnum C.int32_t, param_p0 C.int64_t, param_p1 C.int32_t) C.int32_t {
+//export proxyjava_Future_Get_
+func proxyjava_Future_Get_(refnum C.int32_t, param_p0 C.int64_t, param_p1 C.int32_t) (C.int32_t, C.int32_t) {
 	ref := _seq.FromRefNum(int32(refnum))
 	v := ref.Get().(*java.Future)
 	_param_p0 := int64(param_p0)
@@ -1206,12 +1739,16 @@
 			_param_p1 = (*proxy_class_java_util_concurrent_TimeUnit)(_param_p1_ref)
 		}
 	}
-	res_0 := v.Get2(_param_p0, _param_p1)
+	res_0, res_1 := v.Get_(_param_p0, _param_p1)
 	var _res_0 C.int32_t = _seq.NullRefNum
 	if res_0 != nil {
 		_res_0 = C.int32_t(_seq.ToRefNum(res_0))
 	}
-	return _res_0
+	var _res_1 C.int32_t = _seq.NullRefNum
+	if res_1 != nil {
+		_res_1 = C.int32_t(_seq.ToRefNum(res_1))
+	}
+	return _res_0, _res_1
 }
 
 //export new_java_Future
diff --git a/bind/testdata/classes.java.c.golden b/bind/testdata/classes.java.c.golden
index a5d7a46..c1469b2 100644
--- a/bind/testdata/classes.java.c.golden
+++ b/bind/testdata/classes.java.c.golden
@@ -8,13 +8,33 @@
 static jmethodID m_java_lang_Runnable_run;
 static jclass class_java_io_InputStream;
 static jmethodID m_java_io_InputStream_read__;
+static jmethodID m_java_io_InputStream_read___3B;
+static jmethodID m_java_io_InputStream_read___3BII;
 static jclass class_java_util_concurrent_Future;
 static jmethodID m_java_util_concurrent_Future_get__;
 static jmethodID m_java_util_concurrent_Future_get__JLjava_util_concurrent_TimeUnit_2;
 static jclass class_java_lang_Object;
 static jclass class_java_util_concurrent_TimeUnit;
 static jclass class_java_util_Spliterators;
+static jmethodID m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2;
 static jmethodID m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2;
+static jmethodID m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2;
+static jmethodID m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2;
+ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2(jint a0) {
+	JNIEnv *env = go_seq_push_local_frame(1);
+	jobject _a0 = go_seq_from_refnum(env, a0, NULL, NULL);
+	jobject res = (*env)->CallStaticObjectMethod(env, class_java_util_Spliterators, m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2, _a0);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = NULL;
+	}
+	jint _res = go_seq_to_refnum(env, res);
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
 ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2(jint a0) {
 	JNIEnv *env = go_seq_push_local_frame(1);
 	jobject _a0 = go_seq_from_refnum(env, a0, NULL, NULL);
@@ -30,6 +50,36 @@
 	return __res;
 }
 
+ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2(jint a0) {
+	JNIEnv *env = go_seq_push_local_frame(1);
+	jobject _a0 = go_seq_from_refnum(env, a0, NULL, NULL);
+	jobject res = (*env)->CallStaticObjectMethod(env, class_java_util_Spliterators, m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2, _a0);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = NULL;
+	}
+	jint _res = go_seq_to_refnum(env, res);
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
+ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2(jint a0) {
+	JNIEnv *env = go_seq_push_local_frame(1);
+	jobject _a0 = go_seq_from_refnum(env, a0, NULL, NULL);
+	jobject res = (*env)->CallStaticObjectMethod(env, class_java_util_Spliterators, m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2, _a0);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = NULL;
+	}
+	jint _res = go_seq_to_refnum(env, res);
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
 static jclass class_java_lang_System;
 static jmethodID m_s_java_lang_System_console;
 ret_jint cproxy_s_java_lang_System_console() {
@@ -56,19 +106,29 @@
 static jclass sclass_java_InputStream;
 static jmethodID m_java_InputStream_read__;
 static jmethodID sm_java_InputStream_read__;
+static jmethodID m_java_InputStream_read___3B;
+static jmethodID sm_java_InputStream_read___3B;
+static jmethodID m_java_InputStream_read___3BII;
+static jmethodID sm_java_InputStream_read___3BII;
 static jclass class_java_Object;
 static jclass sclass_java_Object;
 static jclass class_java_Runnable;
 static jclass sclass_java_Runnable;
 static jmethodID m_java_Runnable_run;
 static jmethodID sm_java_Runnable_run;
+static jclass class_java_util_Iterator;
+static jclass class_java_util_Spliterator;
 static jclass class_java_util_PrimitiveIterator_OfInt;
 static jclass class_java_util_Spliterator_OfInt;
+static jclass class_java_util_PrimitiveIterator_OfLong;
+static jclass class_java_util_Spliterator_OfLong;
+static jclass class_java_util_PrimitiveIterator_OfDouble;
+static jclass class_java_util_Spliterator_OfDouble;
 static jclass class_java_io_Console;
 static jmethodID m_java_io_Console_flush;
 
 void init_proxies() {
-	JNIEnv *env = go_seq_push_local_frame(14);
+	JNIEnv *env = go_seq_push_local_frame(20);
 	jclass clazz;
 	clazz = (*env)->FindClass(env, "java/lang/Runnable");
 	class_java_lang_Runnable = (*env)->NewGlobalRef(env, clazz);
@@ -76,6 +136,8 @@
 	clazz = (*env)->FindClass(env, "java/io/InputStream");
 	class_java_io_InputStream = (*env)->NewGlobalRef(env, clazz);
 	m_java_io_InputStream_read__ = go_seq_get_method_id(clazz, "read", "()I");
+	m_java_io_InputStream_read___3B = go_seq_get_method_id(clazz, "read", "([B)I");
+	m_java_io_InputStream_read___3BII = go_seq_get_method_id(clazz, "read", "([BII)I");
 	clazz = (*env)->FindClass(env, "java/util/concurrent/Future");
 	class_java_util_concurrent_Future = (*env)->NewGlobalRef(env, clazz);
 	m_java_util_concurrent_Future_get__ = go_seq_get_method_id(clazz, "get", "()Ljava/lang/Object;");
@@ -86,7 +148,10 @@
 	class_java_util_concurrent_TimeUnit = (*env)->NewGlobalRef(env, clazz);
 	clazz = (*env)->FindClass(env, "java/util/Spliterators");
 	class_java_util_Spliterators = (*env)->NewGlobalRef(env, clazz);
+	m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator;)Ljava/util/Iterator;");
 	m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator$OfInt;)Ljava/util/PrimitiveIterator$OfInt;");
+	m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator$OfLong;)Ljava/util/PrimitiveIterator$OfLong;");
+	m_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2 = go_seq_get_static_method_id(clazz, "iterator", "(Ljava/util/Spliterator$OfDouble;)Ljava/util/PrimitiveIterator$OfDouble;");
 	clazz = (*env)->FindClass(env, "java/lang/System");
 	class_java_lang_System = (*env)->NewGlobalRef(env, clazz);
 	m_s_java_lang_System_console = go_seq_get_static_method_id(clazz, "console", "()Ljava/io/Console;");
@@ -104,6 +169,10 @@
 	sclass_java_InputStream = (*env)->NewGlobalRef(env, sclass_java_InputStream);
 	m_java_InputStream_read__ = go_seq_get_method_id(clazz, "read", "()I");
 	sm_java_InputStream_read__ = go_seq_get_method_id(sclass_java_InputStream, "read", "()I");
+	m_java_InputStream_read___3B = go_seq_get_method_id(clazz, "read", "([B)I");
+	sm_java_InputStream_read___3B = go_seq_get_method_id(sclass_java_InputStream, "read", "([B)I");
+	m_java_InputStream_read___3BII = go_seq_get_method_id(clazz, "read", "([BII)I");
+	sm_java_InputStream_read___3BII = go_seq_get_method_id(sclass_java_InputStream, "read", "([BII)I");
 	clazz = (*env)->FindClass(env, "java/Object");
 	class_java_Object = (*env)->NewGlobalRef(env, clazz);
 	sclass_java_Object = (*env)->GetSuperclass(env, clazz);
@@ -114,10 +183,22 @@
 	sclass_java_Runnable = (*env)->NewGlobalRef(env, sclass_java_Runnable);
 	m_java_Runnable_run = go_seq_get_method_id(clazz, "run", "()V");
 	sm_java_Runnable_run = go_seq_get_method_id(sclass_java_Runnable, "run", "()V");
+	clazz = (*env)->FindClass(env, "java/util/Iterator");
+	class_java_util_Iterator = (*env)->NewGlobalRef(env, clazz);
+	clazz = (*env)->FindClass(env, "java/util/Spliterator");
+	class_java_util_Spliterator = (*env)->NewGlobalRef(env, clazz);
 	clazz = (*env)->FindClass(env, "java/util/PrimitiveIterator$OfInt");
 	class_java_util_PrimitiveIterator_OfInt = (*env)->NewGlobalRef(env, clazz);
 	clazz = (*env)->FindClass(env, "java/util/Spliterator$OfInt");
 	class_java_util_Spliterator_OfInt = (*env)->NewGlobalRef(env, clazz);
+	clazz = (*env)->FindClass(env, "java/util/PrimitiveIterator$OfLong");
+	class_java_util_PrimitiveIterator_OfLong = (*env)->NewGlobalRef(env, clazz);
+	clazz = (*env)->FindClass(env, "java/util/Spliterator$OfLong");
+	class_java_util_Spliterator_OfLong = (*env)->NewGlobalRef(env, clazz);
+	clazz = (*env)->FindClass(env, "java/util/PrimitiveIterator$OfDouble");
+	class_java_util_PrimitiveIterator_OfDouble = (*env)->NewGlobalRef(env, clazz);
+	clazz = (*env)->FindClass(env, "java/util/Spliterator$OfDouble");
+	class_java_util_Spliterator_OfDouble = (*env)->NewGlobalRef(env, clazz);
 	clazz = (*env)->FindClass(env, "java/io/Console");
 	class_java_io_Console = (*env)->NewGlobalRef(env, clazz);
 	m_java_io_Console_flush = go_seq_get_method_id(clazz, "flush", "()V");
@@ -151,6 +232,42 @@
 	return __res;
 }
 
+ret_jint cproxy_java_io_InputStream_read___3B(jint this, nbyteslice a0) {
+	JNIEnv *env = go_seq_push_local_frame(2);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	jbyteArray _a0 = go_seq_to_java_bytearray(env, a0, 0);
+	jint res = (*env)->CallIntMethod(env, _this, m_java_io_InputStream_read___3B, _a0);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = 0;
+	}
+	jint _res = res;
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
+ret_jint cproxy_java_io_InputStream_read___3BII(jint this, nbyteslice a0, jint a1, jint a2) {
+	JNIEnv *env = go_seq_push_local_frame(4);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	jbyteArray _a0 = go_seq_to_java_bytearray(env, a0, 0);
+	jint _a1 = a1;
+	jint _a2 = a2;
+	jint res = (*env)->CallIntMethod(env, _this, m_java_io_InputStream_read___3BII, _a0, _a1, _a2);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = 0;
+	}
+	jint _res = res;
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
 ret_jint cproxy_java_util_concurrent_Future_get__(jint this) {
 	JNIEnv *env = go_seq_push_local_frame(1);
 	// Must be a Java object
@@ -285,6 +402,78 @@
 	return __res;
 }
 
+ret_jint cproxy_java_InputStream_read___3B(jint this, nbyteslice a0) {
+	JNIEnv *env = go_seq_push_local_frame(2);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	jbyteArray _a0 = go_seq_to_java_bytearray(env, a0, 0);
+	jint res = (*env)->CallIntMethod(env, _this, m_java_InputStream_read___3B, _a0);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = 0;
+	}
+	jint _res = res;
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
+ret_jint csuper_java_InputStream_read___3B(jint this, nbyteslice a0) {
+	JNIEnv *env = go_seq_push_local_frame(2);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	jbyteArray _a0 = go_seq_to_java_bytearray(env, a0, 0);
+	jint res = (*env)->CallNonvirtualIntMethod(env, _this, sclass_java_InputStream, sm_java_InputStream_read___3B, _a0);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = 0;
+	}
+	jint _res = res;
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
+ret_jint cproxy_java_InputStream_read___3BII(jint this, nbyteslice a0, jint a1, jint a2) {
+	JNIEnv *env = go_seq_push_local_frame(4);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	jbyteArray _a0 = go_seq_to_java_bytearray(env, a0, 0);
+	jint _a1 = a1;
+	jint _a2 = a2;
+	jint res = (*env)->CallIntMethod(env, _this, m_java_InputStream_read___3BII, _a0, _a1, _a2);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = 0;
+	}
+	jint _res = res;
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
+ret_jint csuper_java_InputStream_read___3BII(jint this, nbyteslice a0, jint a1, jint a2) {
+	JNIEnv *env = go_seq_push_local_frame(4);
+	// Must be a Java object
+	jobject _this = go_seq_from_refnum(env, this, NULL, NULL);
+	jbyteArray _a0 = go_seq_to_java_bytearray(env, a0, 0);
+	jint _a1 = a1;
+	jint _a2 = a2;
+	jint res = (*env)->CallNonvirtualIntMethod(env, _this, sclass_java_InputStream, sm_java_InputStream_read___3BII, _a0, _a1, _a2);
+	jobject _exc = go_seq_get_exception(env);
+	int32_t _exc_ref = go_seq_to_refnum(env, _exc);
+	if (_exc != NULL) {
+		res = 0;
+	}
+	jint _res = res;
+	go_seq_pop_local_frame(env);
+	ret_jint __res = {_res, _exc_ref};
+	return __res;
+}
+
 jint cproxy_java_Runnable_run(jint this) {
 	JNIEnv *env = go_seq_push_local_frame(1);
 	// Must be a Java object
@@ -368,8 +557,10 @@
 JNIEXPORT jobject JNICALL
 Java_java_Future_get__(JNIEnv* env, jobject __this__) {
     int32_t o = go_seq_to_refnum_go(env, __this__);
-    int32_t r0 = proxyjava_Future_Get(o);
-    jobject _r0 = go_seq_from_refnum(env, r0, NULL, NULL);
+    struct proxyjava_Future_Get_return res = proxyjava_Future_Get(o);
+    jobject _r0 = go_seq_from_refnum(env, res.r0, NULL, NULL);
+    jobject _r1 = go_seq_from_refnum(env, res.r1, proxy_class__error, proxy_class__error_cons);
+    go_seq_maybe_throw_exception(env, _r1);
     return _r0;
 }
 
@@ -378,8 +569,10 @@
     int32_t o = go_seq_to_refnum_go(env, __this__);
     int64_t _p0 = (int64_t)p0;
     int32_t _p1 = go_seq_to_refnum(env, p1);
-    int32_t r0 = proxyjava_Future_Get2(o, _p0, _p1);
-    jobject _r0 = go_seq_from_refnum(env, r0, NULL, NULL);
+    struct proxyjava_Future_Get__return res = proxyjava_Future_Get_(o, _p0, _p1);
+    jobject _r0 = go_seq_from_refnum(env, res.r0, NULL, NULL);
+    jobject _r1 = go_seq_from_refnum(env, res.r1, proxy_class__error, proxy_class__error_cons);
+    go_seq_maybe_throw_exception(env, _r1);
     return _r0;
 }
 
diff --git a/bind/testdata/classes.java.golden b/bind/testdata/classes.java.golden
index b9d602c..a746c17 100644
--- a/bind/testdata/classes.java.golden
+++ b/bind/testdata/classes.java.golden
@@ -26,8 +26,8 @@
     public final native java.util.concurrent.Future getFuture();
     public final native void setFuture(java.util.concurrent.Future v);
     
-    @Override public native java.lang.Object get();
-    @Override public native java.lang.Object get(long p0, java.util.concurrent.TimeUnit p1);
+    @Override public native java.lang.Object get() throws java.lang.InterruptedException, java.util.concurrent.ExecutionException;
+    @Override public native java.lang.Object get(long p0, java.util.concurrent.TimeUnit p1) throws java.lang.InterruptedException, java.util.concurrent.ExecutionException, java.util.concurrent.TimeoutException;
 }
 
 // Java class java.InputStream is a proxy for talking to a Go program.
diff --git a/bind/testdata/classes.java.h.golden b/bind/testdata/classes.java.h.golden
index 16362ed..61aefed 100644
--- a/bind/testdata/classes.java.h.golden
+++ b/bind/testdata/classes.java.h.golden
@@ -48,6 +48,8 @@
 
 extern jint cproxy_java_lang_Runnable_run(jint this);
 extern ret_jint cproxy_java_io_InputStream_read__(jint this);
+extern ret_jint cproxy_java_io_InputStream_read___3B(jint this, nbyteslice a0);
+extern ret_jint cproxy_java_io_InputStream_read___3BII(jint this, nbyteslice a0, jint a1, jint a2);
 extern ret_jint cproxy_java_util_concurrent_Future_get__(jint this);
 extern ret_jint cproxy_java_util_concurrent_Future_get__JLjava_util_concurrent_TimeUnit_2(jint this, jlong a0, jint a1);
 extern ret_jint cproxy_java_Future_get__(jint this);
@@ -56,10 +58,17 @@
 extern ret_jint csuper_java_Future_get__JLjava_util_concurrent_TimeUnit_2(jint this, jlong a0, jint a1);
 extern ret_jint cproxy_java_InputStream_read__(jint this);
 extern ret_jint csuper_java_InputStream_read__(jint this);
+extern ret_jint cproxy_java_InputStream_read___3B(jint this, nbyteslice a0);
+extern ret_jint csuper_java_InputStream_read___3B(jint this, nbyteslice a0);
+extern ret_jint cproxy_java_InputStream_read___3BII(jint this, nbyteslice a0, jint a1, jint a2);
+extern ret_jint csuper_java_InputStream_read___3BII(jint this, nbyteslice a0, jint a1, jint a2);
 extern jint cproxy_java_Runnable_run(jint this);
 extern jint csuper_java_Runnable_run(jint this);
 extern jint cproxy_java_io_Console_flush(jint this);
+extern ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_2(jint a0);
 extern ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfInt_2(jint a0);
+extern ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfLong_2(jint a0);
+extern ret_jint cproxy_s_java_util_Spliterators_iterator__Ljava_util_Spliterator_00024OfDouble_2(jint a0);
 extern ret_jint cproxy_s_java_lang_System_console();
 // JNI function headers for the Go <=> Java bridge.
 //   gobind -lang=java classes
diff --git a/bind/testpkg/javapkg/classes.go b/bind/testpkg/javapkg/classes.go
index 5ce80ca..b4fea5a 100644
--- a/bind/testpkg/javapkg/classes.go
+++ b/bind/testpkg/javapkg/classes.go
@@ -51,7 +51,7 @@
 }
 
 func (_ *GoInputStream) Read() (int32, error) {
-	return 0, IOException.New_Ljava_lang_String_2(IOExceptionMessage)
+	return 0, IOException.New(IOExceptionMessage)
 }
 
 func NewGoInputStream() *GoInputStream {
@@ -66,12 +66,13 @@
 	return false
 }
 
-func (_ *GoFuture) Get() lang.Object {
-	return nil
+func (_ *GoFuture) Get() (lang.Object, error) {
+	return nil, nil
 }
 
-func (_ *GoFuture) Get2(_ int64, _ concurrent.TimeUnit) lang.Object {
-	return nil
+// Use a trailing underscore to override multiple overloaded methods.
+func (_ *GoFuture) Get_(_ int64, _ concurrent.TimeUnit) (lang.Object, error) {
+	return nil, nil
 }
 
 func (_ *GoFuture) IsCancelled() bool {
@@ -153,7 +154,8 @@
 }
 
 func NewJavaInteger() lang.Integer {
-	return Integer.New_I(42)
+	i, _ := Integer.New(int32(42))
+	return i
 }
 
 type NoargConstructor struct {
diff --git a/bind/testpkg/javapkg/java.go b/bind/testpkg/javapkg/java.go
index fa2a3c6..948f13a 100644
--- a/bind/testpkg/javapkg/java.go
+++ b/bind/testpkg/javapkg/java.go
@@ -30,7 +30,7 @@
 }
 
 func IntegerValueOf(v int32) int32 {
-	i := Integer.ValueOf_I(v)
+	i, _ := Integer.ValueOf(v)
 	return i.IntValue()
 }
 
@@ -45,7 +45,7 @@
 }
 
 func IntegerParseInt(v string, radix int32) (int32, error) {
-	return Integer.ParseInt2(v, radix)
+	return Integer.ParseInt(v, radix)
 }
 
 func ProvokeRuntimeException() (err error) {
diff --git a/example/reverse/reverse/reverse.go b/example/reverse/reverse/reverse.go
index 4ec6d99..501be71 100644
--- a/example/reverse/reverse/reverse.go
+++ b/example/reverse/reverse/reverse.go
@@ -18,9 +18,9 @@
 	app.AppCompatActivity
 }
 
-func (a *MainActivity) OnCreate1(this gopkg.MainActivity, b os.Bundle) {
-	this.Super().OnCreate1(b)
-	db := DataBindingUtil.SetContentView2(this, rlayout.Activity_main)
+func (a *MainActivity) OnCreate(this gopkg.MainActivity, b os.Bundle) {
+	this.Super().OnCreate(b)
+	db := DataBindingUtil.SetContentView(this, rlayout.Activity_main)
 	mainBind := ActivityMainBinding.Cast(db)
 	mainBind.SetAct(this)
 }
diff --git a/internal/importers/java/java.go b/internal/importers/java/java.go
index 2b9b063..89617c8 100644
--- a/internal/importers/java/java.go
+++ b/internal/importers/java/java.go
@@ -16,6 +16,7 @@
 	"errors"
 	"fmt"
 	"os/exec"
+	"reflect"
 	"strings"
 	"unicode"
 	"unicode/utf8"
@@ -35,11 +36,15 @@
 	JNIName string
 	// "Inner"
 	PkgName string
-	Funcs   []*Func
-	Methods []*Func
+	Funcs   []*FuncSet
+	Methods []*FuncSet
+	// funcMap maps function names.
+	funcMap map[string]*FuncSet
+	// FuncMap maps method names.
+	methodMap map[string]*FuncSet
 	// All methods, including methods from
 	// supers.
-	AllMethods []*Func
+	AllMethods []*FuncSet
 	Vars       []*Var
 	Supers     []string
 	Final      bool
@@ -50,11 +55,34 @@
 	HasNoArgCon bool
 }
 
+// FuncSet is the set of overloaded variants of a function.
+// If the function is not overloaded, its FuncSet contains
+// one entry.
+type FuncSet struct {
+	Name   string
+	GoName string
+	Funcs  []*Func
+	CommonSig
+}
+
+// CommonSig is a signature compatible with every
+// overloaded variant of a FuncSet.
+type CommonSig struct {
+	// Variadic is set if the signature covers variants
+	// with varying number of parameters.
+	Variadic bool
+	// HasRet is true if at least one variant returns a
+	// value.
+	HasRet bool
+	Throws bool
+	Params []*Type
+	Ret    *Type
+}
+
 // Func is a Java static function or method or constructor.
 type Func struct {
 	FuncSig
 	ArgDesc string
-	GoName  string
 	// Mangled JNI name
 	JNIName     string
 	Static      bool
@@ -102,6 +130,13 @@
 	clsMap map[string]*Class
 }
 
+// funcRef is a reference to a Java function (static method).
+// It is used as a key to filter unused Java functions.
+type funcRef struct {
+	clsName string
+	goName  string
+}
+
 const (
 	Int TypeKind = iota
 	Boolean
@@ -167,10 +202,21 @@
 			}
 		}
 	}
+	funcRefs := make(map[funcRef]struct{})
+	for _, ref := range refs.Refs {
+		pkgName := strings.Replace(ref.Pkg, "/", ".", -1)
+		funcRefs[funcRef{pkgName, ref.Name}] = struct{}{}
+	}
 	classes, err := j.importClasses(names, true)
 	if err != nil {
 		return nil, err
 	}
+	j.filterReferences(classes, refs, funcRefs)
+	supers, err := j.importReferencedClasses(classes)
+	if err != nil {
+		return nil, err
+	}
+	j.filterReferences(supers, refs, funcRefs)
 	// Embedders refer to every exported Go struct that will have its class
 	// generated. Allow Go code to reverse bind to those classes by synthesizing
 	// their class descriptors.
@@ -182,6 +228,7 @@
 		if _, exists := j.clsMap[n]; exists {
 			continue
 		}
+		clsSet[n] = struct{}{}
 		cls := &Class{
 			Name:     n,
 			FindName: n,
@@ -199,52 +246,193 @@
 		classes = append(classes, cls)
 		j.clsMap[cls.Name] = cls
 	}
-	j.initClasses(classes, refs)
 	// Include implicit classes that are used in parameter or return values.
-	newClasses := classes
-	for len(newClasses) > 0 {
-		var impNames []string
-		var impClasses []*Class
-		for _, cls := range newClasses {
-			for _, funcs := range [][]*Func{cls.Funcs, cls.AllMethods} {
-				for _, f := range funcs {
+	for _, cls := range classes {
+		for _, fsets := range [][]*FuncSet{cls.Funcs, cls.Methods} {
+			for _, fs := range fsets {
+				for _, f := range fs.Funcs {
 					names := j.implicitFuncTypes(f)
 					for _, name := range names {
 						if _, exists := clsSet[name]; exists {
 							continue
 						}
 						clsSet[name] = struct{}{}
-						if cls, exists := j.clsMap[name]; exists {
-							impClasses = append(impClasses, cls)
-						} else {
-							impNames = append(impNames, name)
-						}
+						classes = append(classes, j.clsMap[name])
 					}
 				}
 			}
 		}
-		imports, err := j.importClasses(impNames, false)
-		if err != nil {
-			return nil, err
+	}
+	for _, cls := range j.clsMap {
+		j.fillFuncSigs(cls.Funcs)
+		j.fillFuncSigs(cls.Methods)
+		for _, m := range cls.Methods {
+			j.fillSuperSigs(cls, m)
 		}
-		impClasses = append(impClasses, imports...)
-		j.initClasses(impClasses, refs)
-		classes = append(classes, impClasses...)
-		newClasses = impClasses
+	}
+	for _, cls := range j.clsMap {
+		j.fillAllMethods(cls)
+	}
+	// Include classes that appear as ancestor types for overloaded signatures.
+	for _, cls := range classes {
+		for _, funcs := range [][]*FuncSet{cls.Funcs, cls.AllMethods} {
+			for _, f := range funcs {
+				for _, p := range f.Params {
+					if p == nil || p.Kind != Object {
+						continue
+					}
+					if _, exists := clsSet[p.Class]; !exists {
+						clsSet[p.Class] = struct{}{}
+						classes = append(classes, j.clsMap[p.Class])
+					}
+				}
+				if t := f.Ret; t != nil && t.Kind == Object {
+					if _, exists := clsSet[t.Class]; !exists {
+						clsSet[t.Class] = struct{}{}
+						classes = append(classes, j.clsMap[t.Class])
+					}
+				}
+			}
+		}
+	}
+	for _, cls := range classes {
+		j.fillJNINames(cls.Funcs)
+		j.fillJNINames(cls.AllMethods)
 	}
 	j.fillThrowables(classes)
 	return classes, nil
 }
 
-func (j *Importer) initClasses(classes []*Class, refs *importers.References) {
-	for _, cls := range classes {
-		j.fillAllMethods(cls)
+func (j *Importer) fillJNINames(funcs []*FuncSet) {
+	for _, fs := range funcs {
+		for _, f := range fs.Funcs {
+			f.JNIName = JNIMangle(f.Name)
+			if len(fs.Funcs) > 1 {
+				f.JNIName += "__" + JNIMangle(f.ArgDesc)
+			}
+		}
 	}
-	for _, cls := range classes {
-		j.mangleOverloads(cls.AllMethods)
-		j.mangleOverloads(cls.Funcs)
+}
+
+// commonType finds the most specific type common to t1 and t2.
+// If t1 and t2 are both Java classes, the most specific ancestor
+// class is returned.
+// Else if the types are equal, their type is returned.
+// Finally, nil is returned, indicating no common type.
+func commonType(clsMap map[string]*Class, t1, t2 *Type) *Type {
+	if t1 == nil || t2 == nil {
+		return nil
 	}
-	j.filterReferences(classes, refs)
+	if reflect.DeepEqual(t1, t2) {
+		return t1
+	}
+	if t1.Kind != Object || t2.Kind != Object {
+		// The types are fundamentally incompatible
+		return nil
+	}
+	superSet := make(map[string]struct{})
+	supers := []string{t1.Class}
+	for len(supers) > 0 {
+		var newSupers []string
+		for _, s := range supers {
+			cls := clsMap[s]
+			superSet[s] = struct{}{}
+			newSupers = append(newSupers, cls.Supers...)
+		}
+		supers = newSupers
+	}
+	supers = []string{t2.Class}
+	for len(supers) > 0 {
+		var newSupers []string
+		for _, s := range supers {
+			if _, exists := superSet[s]; exists {
+				return &Type{Kind: Object, Class: s}
+			}
+			cls := clsMap[s]
+			newSupers = append(newSupers, cls.Supers...)
+		}
+		supers = newSupers
+	}
+	return &Type{Kind: Object, Class: "java.lang.Object"}
+}
+
+// combineSigs finds the most specific function signature
+// that covers all its overload variants.
+// If a function has only one variant, its common signature
+// is the signature of that variant.
+func combineSigs(clsMap map[string]*Class, sigs ...CommonSig) CommonSig {
+	var common CommonSig
+	minp := len(sigs[0].Params)
+	for i := 1; i < len(sigs); i++ {
+		sig := sigs[i]
+		n := len(sig.Params)
+		common.Variadic = common.Variadic || sig.Variadic || n != minp
+		if n < minp {
+			minp = n
+		}
+	}
+	for i, sig := range sigs {
+		for j, p := range sig.Params {
+			idx := j
+			// If the common signature is variadic, combine all parameters in the
+			// last parameter type of the shortest parameter list.
+			if idx > minp {
+				idx = minp
+			}
+			if idx < len(common.Params) {
+				common.Params[idx] = commonType(clsMap, common.Params[idx], p)
+			} else {
+				common.Params = append(common.Params, p)
+			}
+		}
+		common.Throws = common.Throws || sig.Throws
+		common.HasRet = common.HasRet || sig.HasRet
+		if i > 0 {
+			common.Ret = commonType(clsMap, common.Ret, sig.Ret)
+		} else {
+			common.Ret = sig.Ret
+		}
+	}
+	return common
+}
+
+// fillSuperSigs combines methods signatures with super class signatures,
+// to preserve the assignability of classes to their super classes.
+//
+// For example, the class
+//
+// class A {
+//   void f();
+// }
+//
+// is by itself represented by the Go interface
+//
+// type A interface {
+//   f()
+// }
+//
+// However, if class
+//
+// class B extends A {
+//   void f(int);
+// }
+//
+// is also imported, it will be represented as
+//
+// type B interface {
+//   f(...int32)
+// }
+//
+// To make Go B assignable to Go A, the signature of A's f must
+// be updated to f(...int32) as well.
+func (j *Importer) fillSuperSigs(cls *Class, m *FuncSet) {
+	for _, s := range cls.Supers {
+		sup := j.clsMap[s]
+		if sm, exists := sup.methodMap[m.GoName]; exists {
+			sm.CommonSig = combineSigs(j.clsMap, sm.CommonSig, m.CommonSig)
+		}
+		j.fillSuperSigs(sup, m)
+	}
 }
 
 func (v *Var) Constant() bool {
@@ -377,20 +565,11 @@
 	}
 }
 
-func (j *Importer) filterReferences(classes []*Class, refs *importers.References) {
-	refFuncs := make(map[[2]string]struct{})
-	for _, ref := range refs.Refs {
-		pkgName := strings.Replace(ref.Pkg, "/", ".", -1)
-		cls := j.clsMap[pkgName]
-		if cls == nil {
-			continue
-		}
-		refFuncs[[...]string{pkgName, ref.Name}] = struct{}{}
-	}
+func (j *Importer) filterReferences(classes []*Class, refs *importers.References, funcRefs map[funcRef]struct{}) {
 	for _, cls := range classes {
-		var filtered []*Func
+		var filtered []*FuncSet
 		for _, f := range cls.Funcs {
-			if _, exists := refFuncs[[...]string{cls.Name, f.GoName}]; exists {
+			if _, exists := funcRefs[funcRef{cls.Name, f.GoName}]; exists {
 				filtered = append(filtered, f)
 			}
 		}
@@ -402,16 +581,10 @@
 			}
 		}
 		cls.Methods = filtered
-		filtered = nil
-		for _, m := range cls.AllMethods {
-			if _, exists := refs.Names[m.GoName]; exists {
-				filtered = append(filtered, m)
-			}
-		}
-		cls.AllMethods = filtered
 	}
 }
 
+// importClasses imports the named classes from the classpaths of the Importer.
 func (j *Importer) importClasses(names []string, allowMissingClasses bool) ([]*Class, error) {
 	if len(names) == 0 {
 		return nil, nil
@@ -442,31 +615,54 @@
 	for _, name := range names {
 		cls, err := j.scanClass(s, name)
 		if err != nil {
-			if allowMissingClasses && err == errClsNotFound {
+			if err == errClsNotFound && allowMissingClasses {
 				continue
 			}
-			return nil, err
+			if err == errClsNotFound && name != "android.databinding.DataBindingComponent" {
+				return nil, err
+			}
+			// The Android Databinding library generates android.databinding.DataBindingComponent
+			// too late in the build process for the gobind plugin to import it. Synthesize a class
+			// for it instead.
+			cls = &Class{
+				Name:      name,
+				FindName:  name,
+				Interface: true,
+				PkgName:   "databinding",
+				JNIName:   JNIMangle(name),
+			}
 		}
 		classes = append(classes, cls)
 		j.clsMap[name] = cls
 	}
+	return classes, nil
+}
+
+// importReferencedClasses imports all implicit classes (super types, parameter and
+// return types) for the given classes not already imported.
+func (j *Importer) importReferencedClasses(classes []*Class) ([]*Class, error) {
+	var allCls []*Class
 	// Include methods from extended or implemented classes.
-	unkCls := classes
 	for {
-		var unknown []string
-		for _, cls := range unkCls {
-			unknown = j.unknownSuperClasses(cls, unknown)
+		set := make(map[string]struct{})
+		for _, cls := range classes {
+			j.unknownImplicitClasses(cls, set)
 		}
-		if len(unknown) == 0 {
+		if len(set) == 0 {
 			break
 		}
-		newCls, err := j.importClasses(unknown, false)
+		var names []string
+		for n := range set {
+			names = append(names, n)
+		}
+		newCls, err := j.importClasses(names, false)
 		if err != nil {
 			return nil, err
 		}
-		unkCls = newCls
+		allCls = append(allCls, newCls...)
+		classes = newCls
 	}
-	return classes, nil
+	return allCls, nil
 }
 
 func (j *Importer) implicitFuncTypes(f *Func) []string {
@@ -482,21 +678,43 @@
 	return unk
 }
 
-func (j *Importer) unknownSuperClasses(cls *Class, unk []string) []string {
-loop:
-	for _, n := range cls.Supers {
-		if s, exists := j.clsMap[n]; exists {
-			unk = j.unknownSuperClasses(s, unk)
-		} else {
-			for _, u := range unk {
-				if u == n {
-					continue loop
+func (j *Importer) unknownImplicitClasses(cls *Class, set map[string]struct{}) {
+	for _, fsets := range [][]*FuncSet{cls.Funcs, cls.Methods} {
+		for _, fs := range fsets {
+			for _, f := range fs.Funcs {
+				names := j.implicitFuncTypes(f)
+				for _, name := range names {
+					if _, exists := j.clsMap[name]; !exists {
+						set[name] = struct{}{}
+					}
 				}
 			}
-			unk = append(unk, n)
 		}
 	}
-	return unk
+	for _, n := range cls.Supers {
+		if s, exists := j.clsMap[n]; exists {
+			j.unknownImplicitClasses(s, set)
+		} else {
+			set[n] = struct{}{}
+		}
+	}
+}
+
+func (j *Importer) implicitFuncClasses(funcs []*FuncSet, impl []string) []string {
+	var l []string
+	for _, fs := range funcs {
+		for _, f := range fs.Funcs {
+			if rt := f.Ret; rt != nil && rt.Kind == Object {
+				l = append(l, rt.Class)
+			}
+			for _, t := range f.Params {
+				if t.Kind == Object {
+					l = append(l, t.Class)
+				}
+			}
+		}
+	}
+	return impl
 }
 
 func (j *Importer) scanClass(s *bufio.Scanner, name string) (*Class, error) {
@@ -522,13 +740,6 @@
 	if err != nil {
 		return nil, err
 	}
-	if len(cls.Supers) == 0 {
-		if name == "java.lang.Object" {
-			cls.HasNoArgCon = true
-		} else if !cls.Interface {
-			cls.Supers = append(cls.Supers, "java.lang.Object")
-		}
-	}
 	cls.JNIName = JNIMangle(cls.Name)
 	clsElems := strings.Split(cls.Name, ".")
 	cls.PkgName = clsElems[len(clsElems)-1]
@@ -606,18 +817,37 @@
 		}
 	}
 	for _, f := range funcs {
+		var m map[string]*FuncSet
+		var l *[]*FuncSet
+		goName := initialUpper(f.Name)
 		if f.Static || f.Constructor {
-			cls.Funcs = append(cls.Funcs, f)
+			m = cls.funcMap
+			l = &cls.Funcs
 		} else {
-			cls.Methods = append(cls.Methods, f)
+			m = cls.methodMap
+			l = &cls.Methods
 		}
+		fs, exists := m[goName]
+		if !exists {
+			fs = &FuncSet{
+				Name:   f.Name,
+				GoName: goName,
+			}
+			m[goName] = fs
+			*l = append(*l, fs)
+		}
+		fs.Funcs = append(fs.Funcs, f)
 	}
 	return cls, nil
 }
 
 func (j *Importer) scanClassDecl(name string, decl string) (*Class, error) {
+	isRoot := name == "java.lang.Object"
 	cls := &Class{
-		Name: name,
+		Name:        name,
+		funcMap:     make(map[string]*FuncSet),
+		methodMap:   make(map[string]*FuncSet),
+		HasNoArgCon: isRoot,
 	}
 	const (
 		stMod = iota
@@ -625,6 +855,7 @@
 		stExt
 		stImpl
 	)
+	superClsDecl := isRoot
 	st := stMod
 	var w []byte
 	// if > 0, we're inside a generics declaration
@@ -641,6 +872,9 @@
 		case '<':
 			gennest++
 		case '{':
+			if !superClsDecl && !cls.Interface {
+				cls.Supers = append(cls.Supers, "java.lang.Object")
+			}
 			return cls, nil
 		case ' ', ',':
 			if gennest > 0 {
@@ -655,6 +889,7 @@
 					}
 					cls.FindName = w
 				case stExt:
+					superClsDecl = true
 					cls.Supers = append(cls.Supers, w)
 				case stImpl:
 					if !cls.Interface {
@@ -800,68 +1035,61 @@
 	}
 }
 
+func commonSig(f *Func) CommonSig {
+	return CommonSig{
+		Params: f.Params,
+		Ret:    f.Ret,
+		HasRet: f.Ret != nil,
+		Throws: f.Throws != "",
+	}
+}
+
+func (j *Importer) fillFuncSigs(funcs []*FuncSet) {
+	for _, fs := range funcs {
+		var sigs []CommonSig
+		for _, f := range fs.Funcs {
+			sigs = append(sigs, commonSig(f))
+		}
+		fs.CommonSig = combineSigs(j.clsMap, sigs...)
+	}
+}
+
 func (j *Importer) fillAllMethods(cls *Class) {
 	if len(cls.AllMethods) > 0 {
 		return
 	}
-	if len(cls.Supers) == 0 {
-		cls.AllMethods = cls.Methods
-		return
-	}
 	for _, supName := range cls.Supers {
 		super := j.clsMap[supName]
 		j.fillAllMethods(super)
 	}
-	methods := make(map[FuncSig]struct{})
+	var fsets []*FuncSet
+	fsets = append(fsets, cls.Methods...)
 	for _, supName := range cls.Supers {
 		super := j.clsMap[supName]
-		for _, f := range super.AllMethods {
-			if _, exists := methods[f.FuncSig]; !exists {
-				methods[f.FuncSig] = struct{}{}
-				cls.AllMethods = append(cls.AllMethods, f)
+		fsets = append(fsets, super.AllMethods...)
+	}
+	sigs := make(map[FuncSig]struct{})
+	methods := make(map[string]*FuncSet)
+	for _, fs := range fsets {
+		clsFs, exists := methods[fs.Name]
+		if !exists {
+			clsFs = &FuncSet{
+				Name:      fs.Name,
+				GoName:    fs.GoName,
+				CommonSig: fs.CommonSig,
 			}
+			cls.AllMethods = append(cls.AllMethods, clsFs)
+			methods[fs.Name] = clsFs
+		} else {
+			// Combine the (overloaded) signature with the other variants.
+			clsFs.CommonSig = combineSigs(j.clsMap, clsFs.CommonSig, fs.CommonSig)
 		}
-	}
-	for _, f := range cls.Methods {
-		if _, exists := methods[f.FuncSig]; !exists {
-			cls.AllMethods = append(cls.AllMethods, f)
-		}
-	}
-}
-
-// mangleOverloads assigns unique names to overloaded Java functions by appending
-// the argument count. If multiple methods have the same name and argument count,
-// the method signature is appended in JNI mangled form.
-func (j *Importer) mangleOverloads(allFuncs []*Func) {
-	overloads := make(map[string][]*Func)
-	for i, f := range allFuncs {
-		// Name mangling is per class so copy the function first.
-		f := *f
-		allFuncs[i] = &f
-		overloads[f.Name] = append(overloads[f.Name], &f)
-	}
-	for _, funcs := range overloads {
-		for _, f := range funcs {
-			f.GoName = initialUpper(f.Name)
-			f.JNIName = JNIMangle(f.Name)
-		}
-		if len(funcs) == 1 {
-			continue
-		}
-		lengths := make(map[int]int)
-		for _, f := range funcs {
-			f.JNIName += "__" + JNIMangle(f.ArgDesc)
-			lengths[len(f.Params)]++
-		}
-		for _, f := range funcs {
-			n := len(f.Params)
-			if lengths[n] > 1 {
-				f.GoName += "_" + JNIMangle(f.ArgDesc)
+		for _, f := range fs.Funcs {
+			if _, exists := sigs[f.FuncSig]; exists {
 				continue
 			}
-			if n > 0 {
-				f.GoName = fmt.Sprintf("%s%d", f.GoName, n)
-			}
+			sigs[f.FuncSig] = struct{}{}
+			clsFs.Funcs = append(clsFs.Funcs, f)
 		}
 	}
 }
diff --git a/internal/importers/java/java_test.go b/internal/importers/java/java_test.go
index c970a5a..7d25d21 100644
--- a/internal/importers/java/java_test.go
+++ b/internal/importers/java/java_test.go
@@ -18,20 +18,32 @@
 	tests := []struct {
 		ref     importers.PkgRef
 		name    string
-		methods []*Func
+		methods []*FuncSet
 	}{
 		{
 			ref:  importers.PkgRef{"java/lang/Object", "equals"},
 			name: "java.lang.Object",
-			methods: []*Func{
-				&Func{FuncSig: FuncSig{Name: "equals", Desc: "(Ljava/lang/Object;)Z"}, ArgDesc: "Ljava/lang/Object;", GoName: "Equals", JNIName: "equals", Public: true, Params: []*Type{&Type{Kind: Object, Class: "java.lang.Object"}}, Ret: &Type{Kind: Boolean}},
+			methods: []*FuncSet{
+				&FuncSet{
+					Name:   "equals",
+					GoName: "Equals",
+					CommonSig: CommonSig{
+						Params: []*Type{&Type{Kind: Object, Class: "java.lang.Object"}}, Ret: &Type{Kind: Boolean}, HasRet: true,
+					},
+					Funcs: []*Func{&Func{FuncSig: FuncSig{Name: "equals", Desc: "(Ljava/lang/Object;)Z"}, ArgDesc: "Ljava/lang/Object;", JNIName: "equals", Public: true, Params: []*Type{&Type{Kind: Object, Class: "java.lang.Object"}}, Ret: &Type{Kind: Boolean}}},
+				},
 			},
 		},
 		{
 			ref:  importers.PkgRef{"java/lang/Runnable", "run"},
 			name: "java.lang.Runnable",
-			methods: []*Func{
-				&Func{FuncSig: FuncSig{Name: "run", Desc: "()V"}, ArgDesc: "", GoName: "Run", JNIName: "run", Public: true, Abstract: true},
+			methods: []*FuncSet{
+				&FuncSet{
+					Name:      "run",
+					GoName:    "Run",
+					CommonSig: CommonSig{},
+					Funcs:     []*Func{&Func{FuncSig: FuncSig{Name: "run", Desc: "()V"}, ArgDesc: "", JNIName: "run", Public: true, Abstract: true}},
+				},
 			},
 		},
 	}
@@ -56,7 +68,7 @@
 		}
 	loop:
 		for _, exp := range test.methods {
-			for _, got := range cls.Methods {
+			for _, got := range cls.AllMethods {
 				if reflect.DeepEqual(exp, got) {
 					continue loop
 				}
@@ -65,3 +77,118 @@
 		}
 	}
 }
+
+func testClsMap() map[string]*Class {
+	//
+	//    A--
+	//   / \  \
+	//  B   C  \
+	//   \ / \  \
+	//    D   E  F
+	//
+	return map[string]*Class{
+		"A": &Class{},
+		"B": &Class{
+			Supers: []string{"A"},
+		},
+		"C": &Class{
+			Supers: []string{"A"},
+		},
+		"D": &Class{
+			Supers: []string{"B", "C"},
+		},
+		"E": &Class{
+			Supers: []string{"C"},
+		},
+		"F": &Class{
+			Supers: []string{"A"},
+		},
+	}
+}
+
+func TestCommonTypes(t *testing.T) {
+	clsMap := testClsMap()
+	tests := [][3]*Type{
+		{nil, nil, nil},
+		{&Type{Kind: Int}, nil, nil},
+		{&Type{Kind: Int}, &Type{Kind: Float}, nil},
+		{&Type{Kind: Int}, &Type{Kind: Int}, &Type{Kind: Int}},
+		{&Type{Kind: Object, Class: "D"}, &Type{Kind: Object, Class: "D"}, &Type{Kind: Object, Class: "D"}},
+		{&Type{Kind: Object, Class: "D"}, &Type{Kind: Object, Class: "E"}, &Type{Kind: Object, Class: "C"}},
+		{&Type{Kind: Object, Class: "D"}, &Type{Kind: Object, Class: "F"}, &Type{Kind: Object, Class: "A"}},
+		{&Type{Kind: Object, Class: "B"}, &Type{Kind: Object, Class: "E"}, &Type{Kind: Object, Class: "A"}},
+	}
+	for _, test := range tests {
+		t1, t2, exp := test[0], test[1], test[2]
+		got := commonType(clsMap, t1, t2)
+		if !reflect.DeepEqual(got, exp) {
+			t.Errorf("commonType(%+v, %+v) = %+v, expected %+v", t1, t2, got, exp)
+		}
+	}
+}
+
+func TestCommonSig(t *testing.T) {
+	tests := []struct {
+		Sigs []CommonSig
+		CommonSig
+	}{
+		{
+			Sigs: []CommonSig{
+				CommonSig{}, // f()
+			},
+			CommonSig: CommonSig{}, // f()
+		},
+		{
+			Sigs: []CommonSig{
+				CommonSig{Throws: true, HasRet: true, Ret: &Type{Kind: Int}}, // int f() throws
+			},
+			// int f() throws
+			CommonSig: CommonSig{Throws: true, HasRet: true, Ret: &Type{Kind: Int}},
+		},
+		{
+			Sigs: []CommonSig{
+				CommonSig{}, // f()
+				CommonSig{Params: []*Type{&Type{Kind: Int}}}, // f(int)
+			},
+			CommonSig: CommonSig{ // f(int...)
+				Variadic: true,
+				Params:   []*Type{&Type{Kind: Int}},
+			},
+		},
+		{
+			Sigs: []CommonSig{
+				CommonSig{Params: []*Type{&Type{Kind: Int}}},   // f(int)
+				CommonSig{Params: []*Type{&Type{Kind: Float}}}, // f(float)
+			},
+			CommonSig: CommonSig{ // f(interface{})
+				Params: []*Type{nil},
+			},
+		},
+		{
+			Sigs: []CommonSig{
+				CommonSig{Params: []*Type{&Type{Kind: Int}}},                   // f(int)
+				CommonSig{Params: []*Type{&Type{Kind: Int}, &Type{Kind: Int}}}, // f(int, int)
+			},
+			CommonSig: CommonSig{ // f(int, int...)
+				Variadic: true,
+				Params:   []*Type{&Type{Kind: Int}, &Type{Kind: Int}},
+			},
+		},
+		{
+			Sigs: []CommonSig{
+				CommonSig{Params: []*Type{&Type{Kind: Object, Class: "A"}}}, // f(A)
+				CommonSig{Params: []*Type{&Type{Kind: Object, Class: "B"}}}, // f(B)
+			},
+			CommonSig: CommonSig{ // f(A)
+				Params: []*Type{&Type{Kind: Object, Class: "A"}},
+			},
+		},
+	}
+	clsMap := testClsMap()
+	for _, test := range tests {
+		got := combineSigs(clsMap, test.Sigs...)
+		if !reflect.DeepEqual(got, test.CommonSig) {
+			t.Errorf("commonSig(%+v) = %+v, expected %+v", test.Sigs, got, test.CommonSig)
+		}
+	}
+}