mobile/bind: move generated Java classes to package level

Before this CL, generated Java classes or interfaces were inner
classes to the top package class. That is both unnecessary and creates
ugly class names. Instead, move every generated class and interface to its
own package level class.

NOTE: This is a backwards incompatible change and requires every client
of gomobile APIs to be updated to leave out the package class in the
type names. For example, the Go type

package pkg

type S struct {
}

now generates (with the default java package name go) a Java class named
go.pkg.S. The name before this CL was go.pkg.Pkg.S.

Also, change the custom java package to specify the package prefix and
not the full package as before. This is an unfortunate change needed
to avoid name clashes between two bound packages. On the plus side,
the change brings the custom package case closer to the default behaviour,
which is a commen prefix, "go.", and a distinct java package for every
Go package bound.

Change-Id: Iadfaad56e101d1caf7e2a05006f4d384859a20fe
Reviewed-on: https://go-review.googlesource.com/27436
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/bind/bind.go b/bind/bind.go
index 63ca719..1b844e1 100644
--- a/bind/bind.go
+++ b/bind/bind.go
@@ -32,58 +32,23 @@
 )
 
 const (
-	Java fileType = iota
-	JavaC
-	JavaH
-
-	ObjcM
+	ObjcM = iota
 	ObjcH
 	ObjcGoH
 )
 
-// GenJava generates a Java API from a Go package.
-func GenJava(conf *GeneratorConfig, javaPkg string, ft fileType) error {
-	buf := new(bytes.Buffer)
-	g := &javaGen{
-		javaPkg: javaPkg,
-		generator: &generator{
-			printer: &printer{buf: buf, indentEach: []byte("    ")},
-			fset:    conf.Fset,
-			allPkg:  conf.AllPkg,
-			pkg:     conf.Pkg,
-		},
-	}
-	g.init()
-	var err error
-	switch ft {
-	case Java:
-		err = g.genJava()
-	case JavaC:
-		err = g.genC()
-	case JavaH:
-		err = g.genH()
-	default:
-		panic("invalid fileType")
-	}
-	if err != nil {
-		return err
-	}
-	_, err = io.Copy(conf.Writer, buf)
-	return err
-}
-
 // GenGo generates a Go stub to support foreign language APIs.
 func GenGo(conf *GeneratorConfig) error {
 	buf := new(bytes.Buffer)
 	g := &goGen{
-		generator: &generator{
-			printer: &printer{buf: buf, indentEach: []byte("\t")},
-			fset:    conf.Fset,
-			allPkg:  conf.AllPkg,
-			pkg:     conf.Pkg,
+		Generator: &Generator{
+			Printer: &Printer{Buf: buf, IndentEach: []byte("\t")},
+			Fset:    conf.Fset,
+			AllPkg:  conf.AllPkg,
+			Pkg:     conf.Pkg,
 		},
 	}
-	g.init()
+	g.Init()
 	if err := g.gen(); err != nil {
 		return err
 	}
@@ -101,11 +66,11 @@
 func GenObjc(conf *GeneratorConfig, prefix string, ft fileType) error {
 	buf := new(bytes.Buffer)
 	g := &objcGen{
-		generator: &generator{
-			printer: &printer{buf: buf, indentEach: []byte("\t")},
-			fset:    conf.Fset,
-			allPkg:  conf.AllPkg,
-			pkg:     conf.Pkg,
+		Generator: &Generator{
+			Printer: &Printer{Buf: buf, IndentEach: []byte("\t")},
+			Fset:    conf.Fset,
+			AllPkg:  conf.AllPkg,
+			Pkg:     conf.Pkg,
 		},
 		prefix: prefix,
 	}
diff --git a/bind/bind_test.go b/bind/bind_test.go
index e4b7621..afbdaf7 100644
--- a/bind/bind_test.go
+++ b/bind/bind_test.go
@@ -129,29 +129,52 @@
 }
 
 func TestGenJava(t *testing.T) {
-	var suffixes = map[fileType]string{
-		Java:  ".java.golden",
-		JavaC: ".java.c.golden",
-		JavaH: ".java.h.golden",
-	}
-
 	for _, filename := range tests {
 		pkg := typeCheck(t, filename)
-		for typ, suffix := range suffixes {
-			var buf bytes.Buffer
-			conf := &GeneratorConfig{
-				Writer: &buf,
-				Fset:   fset,
-				Pkg:    pkg,
-				AllPkg: []*types.Package{pkg},
-			}
-			if err := GenJava(conf, "", typ); err != nil {
+		var buf bytes.Buffer
+		g := &JavaGen{
+			Generator: &Generator{
+				Printer: &Printer{Buf: &buf, IndentEach: []byte("    ")},
+				Fset:    fset,
+				AllPkg:  []*types.Package{pkg},
+				Pkg:     pkg,
+			},
+		}
+		g.Init()
+		testCases := []struct {
+			suffix string
+			gen    func() error
+		}{
+			{
+				".java.golden",
+				func() error {
+					for i := range g.ClassNames() {
+						if err := g.GenClass(i); err != nil {
+							return err
+						}
+					}
+					return g.GenJava()
+				},
+			},
+			{
+				".java.c.golden",
+				func() error { return g.GenC() },
+			},
+			{
+				".java.h.golden",
+				func() error { return g.GenH() },
+			},
+		}
+
+		for _, tc := range testCases {
+			buf.Reset()
+			if err := tc.gen(); err != nil {
 				t.Errorf("%s: %v", filename, err)
 				continue
 			}
-			out := writeTempFile(t, "generated"+suffix, buf.Bytes())
+			out := writeTempFile(t, "generated"+tc.suffix, buf.Bytes())
 			defer os.Remove(out)
-			golden := filename[:len(filename)-len(".go")] + suffix
+			golden := filename[:len(filename)-len(".go")] + tc.suffix
 			if diffstr := diff(golden, out); diffstr != "" {
 				t.Errorf("%s: does not match Java golden:\n%s", filename, diffstr)
 
@@ -207,21 +230,58 @@
 		Pkg:    pkg,
 		AllPkg: []*types.Package{pkg},
 	}
+	var buf bytes.Buffer
+	g := &JavaGen{
+		JavaPkg: "com.example",
+		Generator: &Generator{
+			Printer: &Printer{Buf: &buf, IndentEach: []byte("    ")},
+			Fset:    fset,
+			AllPkg:  []*types.Package{pkg},
+			Pkg:     pkg,
+		},
+	}
+	g.Init()
 	testCases := []struct {
 		golden string
 		gen    func(w io.Writer) error
 	}{
 		{
 			"testdata/customprefix.java.golden",
-			func(w io.Writer) error { conf.Writer = w; return GenJava(conf, "com.example", Java) },
+			func(w io.Writer) error {
+				buf.Reset()
+				for i := range g.ClassNames() {
+					if err := g.GenClass(i); err != nil {
+						return err
+					}
+				}
+				if err := g.GenJava(); err != nil {
+					return err
+				}
+				_, err := io.Copy(w, &buf)
+				return err
+			},
 		},
 		{
 			"testdata/customprefix.java.h.golden",
-			func(w io.Writer) error { conf.Writer = w; return GenJava(conf, "com.example", JavaH) },
+			func(w io.Writer) error {
+				buf.Reset()
+				if err := g.GenH(); err != nil {
+					return err
+				}
+				_, err := io.Copy(w, &buf)
+				return err
+			},
 		},
 		{
 			"testdata/customprefix.java.c.golden",
-			func(w io.Writer) error { conf.Writer = w; return GenJava(conf, "com.example", JavaC) },
+			func(w io.Writer) error {
+				buf.Reset()
+				if err := g.GenC(); err != nil {
+					return err
+				}
+				_, err := io.Copy(w, &buf)
+				return err
+			},
 		},
 		{
 			"testdata/customprefix.objc.go.h.golden",
diff --git a/bind/gen.go b/bind/gen.go
index 3d618c3..dcf3a9a 100644
--- a/bind/gen.go
+++ b/bind/gen.go
@@ -50,14 +50,20 @@
 	return buf.String()
 }
 
-type generator struct {
-	*printer
-	fset   *token.FileSet
-	allPkg []*types.Package
-	pkg    *types.Package
+// Generator contains the common Go package information
+// needed for the specific Go, Java, ObjC generators.
+//
+// After setting Printer, Fset, AllPkg, Pkg, the Init
+// method is used to initialize the auxiliary information
+// about the package to be generated, Pkg.
+type Generator struct {
+	*Printer
+	Fset   *token.FileSet
+	AllPkg []*types.Package
+	Pkg    *types.Package
 	err    ErrorList
 
-	// fields set by init.
+	// fields set by Init.
 	pkgName   string
 	pkgPrefix string
 	funcs     []*types.Func
@@ -85,14 +91,14 @@
 	return pkg.Name()
 }
 
-func (g *generator) init() {
-	if g.pkg != nil {
-		g.pkgName = g.pkg.Name()
+func (g *Generator) Init() {
+	if g.Pkg != nil {
+		g.pkgName = g.Pkg.Name()
 	}
-	g.pkgPrefix = pkgPrefix(g.pkg)
+	g.pkgPrefix = pkgPrefix(g.Pkg)
 
-	if g.pkg != nil {
-		scope := g.pkg.Scope()
+	if g.Pkg != nil {
+		scope := g.Pkg.Scope()
 		hasExported := false
 		for _, name := range scope.Names() {
 			obj := scope.Lookup(name)
@@ -124,7 +130,7 @@
 			}
 		}
 		if !hasExported {
-			g.errorf("no exported names in the package %q", g.pkg.Path())
+			g.errorf("no exported names in the package %q", g.Pkg.Path())
 		}
 	} else {
 		// Bind the single supported type from the universe scope, error.
@@ -132,7 +138,7 @@
 		t := errType.Type().Underlying().(*types.Interface)
 		g.interfaces = append(g.interfaces, interfaceInfo{errType, t, makeIfaceSummary(t)})
 	}
-	for _, p := range g.allPkg {
+	for _, p := range g.AllPkg {
 		scope := p.Scope()
 		for _, name := range scope.Names() {
 			obj := scope.Lookup(name)
@@ -149,20 +155,20 @@
 	}
 }
 
-func (_ *generator) toCFlag(v bool) int {
+func (_ *Generator) toCFlag(v bool) int {
 	if v {
 		return 1
 	}
 	return 0
 }
 
-func (g *generator) errorf(format string, args ...interface{}) {
+func (g *Generator) errorf(format string, args ...interface{}) {
 	g.err = append(g.err, fmt.Errorf(format, args...))
 }
 
 // cgoType returns the name of a Cgo type suitable for converting a value of
 // the given type.
-func (g *generator) cgoType(t types.Type) string {
+func (g *Generator) cgoType(t types.Type) string {
 	switch t := t.(type) {
 	case *types.Basic:
 		switch t.Kind() {
@@ -215,7 +221,7 @@
 	return "TODO"
 }
 
-func (g *generator) genInterfaceMethodSignature(m *types.Func, iName string, header bool) {
+func (g *Generator) genInterfaceMethodSignature(m *types.Func, iName string, header bool) {
 	sig := m.Type().(*types.Signature)
 	params := sig.Params()
 	res := sig.Results()
@@ -252,8 +258,8 @@
 	}
 }
 
-func (g *generator) validPkg(pkg *types.Package) bool {
-	for _, p := range g.allPkg {
+func (g *Generator) validPkg(pkg *types.Package) bool {
+	for _, p := range g.AllPkg {
 		if p == pkg {
 			return true
 		}
@@ -263,7 +269,7 @@
 
 // isSigSupported returns whether the generators can handle a given
 // function signature
-func (g *generator) isSigSupported(t types.Type) bool {
+func (g *Generator) isSigSupported(t types.Type) bool {
 	sig := t.(*types.Signature)
 	params := sig.Params()
 	for i := 0; i < params.Len(); i++ {
@@ -281,7 +287,7 @@
 }
 
 // isSupported returns whether the generators can handle the type.
-func (g *generator) isSupported(t types.Type) bool {
+func (g *Generator) isSupported(t types.Type) bool {
 	if isErrorType(t) {
 		return true
 	}
diff --git a/bind/gengo.go b/bind/gengo.go
index 6353a39..b4200f3 100644
--- a/bind/gengo.go
+++ b/bind/gengo.go
@@ -12,7 +12,7 @@
 )
 
 type goGen struct {
-	*generator
+	*Generator
 	imports map[string]struct{}
 }
 
@@ -176,7 +176,7 @@
 	}
 	g.genFuncSignature(o, "")
 	g.Indent()
-	g.genFuncBody(o, g.pkgName(g.pkg))
+	g.genFuncBody(o, g.pkgName(g.Pkg))
 	g.Outdent()
 	g.Printf("}\n\n")
 }
@@ -195,7 +195,7 @@
 		g.Indent()
 		g.Printf("ref := _seq.FromRefNum(int32(refnum))\n")
 		g.genRead("_v", "v", f.Type(), modeRetained)
-		g.Printf("ref.Get().(*%s%s).%s = _v\n", g.pkgName(g.pkg), obj.Name(), f.Name())
+		g.Printf("ref.Get().(*%s%s).%s = _v\n", g.pkgName(g.Pkg), obj.Name(), f.Name())
 		g.Outdent()
 		g.Printf("}\n\n")
 
@@ -203,7 +203,7 @@
 		g.Printf("func proxy%s_%s_%s_Get(refnum C.int32_t) C.%s {\n", g.pkgPrefix, obj.Name(), f.Name(), g.cgoType(f.Type()))
 		g.Indent()
 		g.Printf("ref := _seq.FromRefNum(int32(refnum))\n")
-		g.Printf("v := ref.Get().(*%s%s).%s\n", g.pkgName(g.pkg), obj.Name(), f.Name())
+		g.Printf("v := ref.Get().(*%s%s).%s\n", g.pkgName(g.Pkg), obj.Name(), f.Name())
 		g.genWrite("_v", "v", f.Type(), modeRetained)
 		g.Printf("return _v\n")
 		g.Outdent()
@@ -218,7 +218,7 @@
 		g.genFuncSignature(m, obj.Name())
 		g.Indent()
 		g.Printf("ref := _seq.FromRefNum(int32(refnum))\n")
-		g.Printf("v := ref.Get().(*%s%s)\n", g.pkgName(g.pkg), obj.Name())
+		g.Printf("v := ref.Get().(*%s%s)\n", g.pkgName(g.Pkg), obj.Name())
 		g.genFuncBody(m, "v.")
 		g.Outdent()
 		g.Printf("}\n\n")
@@ -232,7 +232,7 @@
 	}
 	// TODO(hyangah): non-struct pointer types (*int), struct type.
 
-	v := fmt.Sprintf("%s%s", g.pkgName(g.pkg), o.Name())
+	v := fmt.Sprintf("%s%s", g.pkgName(g.Pkg), o.Name())
 
 	// var I int
 	//
@@ -270,7 +270,7 @@
 		g.genFuncSignature(m, obj.Name())
 		g.Indent()
 		g.Printf("ref := _seq.FromRefNum(int32(refnum))\n")
-		g.Printf("v := ref.Get().(%s%s)\n", g.pkgName(g.pkg), obj.Name())
+		g.Printf("v := ref.Get().(%s%s)\n", g.pkgName(g.Pkg), obj.Name())
 		g.genFuncBody(m, "v.")
 		g.Outdent()
 		g.Printf("}\n\n")
@@ -425,7 +425,7 @@
 }
 
 func (g *goGen) typeString(typ types.Type) string {
-	pkg := g.pkg
+	pkg := g.Pkg
 
 	switch t := typ.(type) {
 	case *types.Named:
@@ -463,9 +463,9 @@
 func (g *goGen) genPreamble() {
 	pkgName := ""
 	pkgPath := ""
-	if g.pkg != nil {
-		pkgName = g.pkg.Name()
-		pkgPath = g.pkg.Path()
+	if g.Pkg != nil {
+		pkgName = g.Pkg.Name()
+		pkgPath = g.Pkg.Path()
 	} else {
 		pkgName = "universe"
 	}
@@ -485,9 +485,9 @@
 
 	// Switch to a temporary buffer so the preamble can be
 	// written last.
-	oldBuf := g.printer.buf
+	oldBuf := g.Printer.Buf
 	newBuf := new(bytes.Buffer)
-	g.printer.buf = newBuf
+	g.Printer.Buf = newBuf
 	g.Printf("// suppress the error if seq ends up unused\n")
 	g.Printf("var _ = _seq.FromRefNum\n")
 
@@ -505,9 +505,9 @@
 	}
 	// Switch to the original buffer, write the preamble
 	// and append the rest of the file.
-	g.printer.buf = oldBuf
+	g.Printer.Buf = oldBuf
 	g.genPreamble()
-	g.printer.buf.Write(newBuf.Bytes())
+	g.Printer.Buf.Write(newBuf.Bytes())
 	if len(g.err) > 0 {
 		return g.err
 	}
diff --git a/bind/genjava.go b/bind/genjava.go
index 8632e66..7053abb 100644
--- a/bind/genjava.go
+++ b/bind/genjava.go
@@ -15,15 +15,48 @@
 // TODO(crawshaw): disallow basic android java type names in exported symbols.
 // TODO(crawshaw): consider introducing Java functions for casting to and from interfaces at runtime.
 
-type javaGen struct {
+type JavaGen struct {
 	// javaPkg is the custom name of the Java pkg that contains the generated classes. If empty,
 	// use a package name generated from the Go package name.
-	javaPkg string
+	JavaPkg string
 
-	*generator
+	*Generator
 }
 
-func (g *javaGen) genStruct(obj *types.TypeName, T *types.Struct) {
+// ClassNames returns the list of names of the generated Java classes and interfaces.
+func (g *JavaGen) ClassNames() []string {
+	var names []string
+	for _, s := range g.structs {
+		names = append(names, s.obj.Name())
+	}
+	for _, iface := range g.interfaces {
+		names = append(names, iface.obj.Name())
+	}
+	return names
+}
+
+func (g *JavaGen) GenClass(idx int) error {
+	ns := len(g.structs)
+	if idx < ns {
+		s := g.structs[idx]
+		g.genStruct(s.obj, s.t)
+	} else {
+		iface := g.interfaces[idx-ns]
+		g.genInterface(iface)
+	}
+	if len(g.err) > 0 {
+		return g.err
+	}
+	return nil
+}
+
+func (g *JavaGen) genStruct(obj *types.TypeName, T *types.Struct) {
+	pkgPath := ""
+	if g.Pkg != nil {
+		pkgPath = g.Pkg.Path()
+	}
+	g.Printf(javaPreamble, g.javaPkgName(g.Pkg), obj.Name(), g.gobindOpts(), pkgPath)
+
 	fields := exportedFields(T)
 	methods := exportedMethodSet(types.NewPointer(obj.Type()))
 
@@ -32,13 +65,13 @@
 	for _, iface := range g.allIntf {
 		if types.AssignableTo(pT, iface.obj.Type()) {
 			n := iface.obj.Name()
-			if p := iface.obj.Pkg(); p != g.pkg {
-				n = fmt.Sprintf("%s.%s.%s", g.javaPkgName(p), className(p), n)
+			if p := iface.obj.Pkg(); p != g.Pkg {
+				n = fmt.Sprintf("%s.%s", g.javaPkgName(p), n)
 			}
 			impls = append(impls, n)
 		}
 	}
-	g.Printf("public static final class %s extends Seq.Proxy", obj.Name())
+	g.Printf("public final class %s extends Seq.Proxy", obj.Name())
 	if len(impls) > 0 {
 		g.Printf(" implements %s", strings.Join(impls, ", "))
 	}
@@ -137,15 +170,21 @@
 	g.Printf("}\n\n")
 }
 
-func (g *javaGen) genInterface(iface interfaceInfo) {
+func (g *JavaGen) genInterface(iface interfaceInfo) {
+	pkgPath := ""
+	if g.Pkg != nil {
+		pkgPath = g.Pkg.Path()
+	}
+	g.Printf(javaPreamble, g.javaPkgName(g.Pkg), iface.obj.Name(), g.gobindOpts(), pkgPath)
+
 	var exts []string
 	numM := iface.t.NumMethods()
 	for _, other := range g.allIntf {
 		// Only extend interfaces with fewer methods to avoid circular references
 		if other.t.NumMethods() < numM && types.AssignableTo(iface.t, other.t) {
 			n := other.obj.Name()
-			if p := other.obj.Pkg(); p != g.pkg {
-				n = fmt.Sprintf("%s.%s.%s", g.javaPkgName(p), className(p), n)
+			if p := other.obj.Pkg(); p != g.Pkg {
+				n = fmt.Sprintf("%s.%s", g.javaPkgName(p), n)
 			}
 			exts = append(exts, n)
 		}
@@ -165,20 +204,7 @@
 		g.genFuncSignature(m, false, true)
 	}
 
-	g.Outdent()
-	g.Printf("}\n")
-
 	g.Printf("\n")
-	g.Printf(javaProxyPreamble, iface.obj.Name())
-	g.Indent()
-
-	for _, m := range iface.summary.callable {
-		if !g.isSigSupported(m.Type()) {
-			g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", iface.obj.Name(), m.Name())
-			continue
-		}
-		g.genFuncSignature(m, false, false)
-	}
 
 	g.Outdent()
 	g.Printf("}\n\n")
@@ -198,7 +224,7 @@
 }
 
 // jniType returns a string that can be used as a JNI type.
-func (g *javaGen) jniType(T types.Type) string {
+func (g *JavaGen) jniType(T types.Type) string {
 	switch T := T.(type) {
 	case *types.Basic:
 		switch T.Kind() {
@@ -245,7 +271,7 @@
 	return "TODO"
 }
 
-func (g *javaGen) javaBasicType(T *types.Basic) string {
+func (g *JavaGen) javaBasicType(T *types.Basic) string {
 	switch T.Kind() {
 	case types.Bool, types.UntypedBool:
 		return "boolean"
@@ -277,7 +303,7 @@
 }
 
 // javaType returns a string that can be used as a Java type.
-func (g *javaGen) javaType(T types.Type) string {
+func (g *JavaGen) javaType(T types.Type) string {
 	if isErrorType(T) {
 		// The error type is usually translated into an exception in
 		// Java, however the type can be exposed in other ways, such
@@ -304,8 +330,8 @@
 			break
 		}
 		// TODO(crawshaw): more checking here
-		if nPkg != g.pkg {
-			return fmt.Sprintf("%s.%s.%s", g.javaPkgName(nPkg), className(nPkg), n.Name())
+		if nPkg != g.Pkg {
+			return fmt.Sprintf("%s.%s", g.javaPkgName(nPkg), n.Name())
 		} else {
 			return n.Name()
 		}
@@ -315,7 +341,7 @@
 	return "TODO"
 }
 
-func (g *javaGen) genJNIFuncSignature(o *types.Func, sName string, proxy bool) {
+func (g *JavaGen) genJNIFuncSignature(o *types.Func, sName string, proxy bool) {
 	sig := o.Type().(*types.Signature)
 	res := sig.Results()
 
@@ -337,14 +363,17 @@
 	}
 
 	g.Printf("JNIEXPORT %s JNICALL\n", ret)
-	g.Printf("Java_%s_%s", g.jniPkgName(), g.className())
+	g.Printf("Java_%s_", g.jniPkgName())
 	if sName != "" {
-		// 0024 is the mangled form of $, for naming inner classes.
-		g.Printf("_00024")
 		if proxy {
+			g.Printf(g.className())
+			// 0024 is the mangled form of $, for naming inner classes.
+			g.Printf("_00024")
 			g.Printf("proxy")
 		}
 		g.Printf("%s", sName)
+	} else {
+		g.Printf(g.className())
 	}
 	g.Printf("_%s(JNIEnv* env, ", o.Name())
 	if sName != "" {
@@ -363,11 +392,11 @@
 	g.Printf(")")
 }
 
-func (g *javaGen) jniPkgName() string {
-	return strings.Replace(g.javaPkgName(g.pkg), ".", "_", -1)
+func (g *JavaGen) jniPkgName() string {
+	return strings.Replace(g.javaPkgName(g.Pkg), ".", "_", -1)
 }
 
-func (g *javaGen) genFuncSignature(o *types.Func, static, header bool) {
+func (g *JavaGen) genFuncSignature(o *types.Func, static, header bool) {
 	sig := o.Type().(*types.Signature)
 	res := sig.Results()
 
@@ -421,7 +450,7 @@
 	g.Printf(";\n")
 }
 
-func (g *javaGen) genVar(o *types.Var) {
+func (g *JavaGen) genVar(o *types.Var) {
 	if t := o.Type(); !g.isSupported(t) {
 		g.Printf("// skipped variable %s with unsupported type: %T\n\n", o.Name(), t)
 		return
@@ -435,7 +464,7 @@
 	g.Printf("public static native %s get%s();\n\n", jType, o.Name())
 }
 
-func (g *javaGen) genJavaToC(varName string, t types.Type, mode varMode) {
+func (g *JavaGen) genJavaToC(varName string, t types.Type, mode varMode) {
 	switch t := t.(type) {
 	case *types.Basic:
 		switch t.Kind() {
@@ -470,7 +499,7 @@
 	}
 }
 
-func (g *javaGen) genCToJava(toName, fromName string, t types.Type, mode varMode) {
+func (g *JavaGen) genCToJava(toName, fromName string, t types.Type, mode varMode) {
 	switch t := t.(type) {
 	case *types.Basic:
 		switch t.Kind() {
@@ -514,7 +543,7 @@
 	}
 }
 
-func (g *javaGen) genFromRefnum(toName, fromName string, t types.Type, o *types.TypeName) {
+func (g *JavaGen) genFromRefnum(toName, fromName string, t types.Type, o *types.TypeName) {
 	oPkg := o.Pkg()
 	if !isErrorType(o.Type()) && !g.validPkg(oPkg) {
 		g.errorf("type %s is defined in package %s, which is not bound", t, oPkg)
@@ -524,10 +553,10 @@
 	g.Printf("jobject %s = go_seq_from_refnum(env, %s, proxy_class_%s_%s, proxy_class_%s_%s_cons);\n", toName, fromName, p, o.Name(), p, o.Name())
 }
 
-func (g *javaGen) gobindOpts() string {
+func (g *JavaGen) gobindOpts() string {
 	opts := []string{"-lang=java"}
-	if g.javaPkg != "" {
-		opts = append(opts, "-javapkg="+g.javaPkg)
+	if g.JavaPkg != "" {
+		opts = append(opts, "-javapkg="+g.JavaPkg)
 	}
 	return strings.Join(opts, " ")
 }
@@ -537,13 +566,10 @@
 	".", "_",
 )
 
-func (g *javaGen) javaPkgName(pkg *types.Package) string {
+func (g *JavaGen) javaPkgName(pkg *types.Package) string {
 	if pkg == nil {
 		return "go"
 	}
-	if g.javaPkg != "" {
-		return g.javaPkg
-	}
 	s := javaNameReplacer.Replace(pkg.Name())
 	// Look for Java keywords that are not Go keywords, and avoid using
 	// them as a package name.
@@ -562,11 +588,15 @@
 		"void", "volatile", "while":
 		s += "_"
 	}
-	return "go." + s
+	if g.JavaPkg != "" {
+		return g.JavaPkg + "." + s
+	} else {
+		return "go." + s
+	}
 }
 
-func (g *javaGen) className() string {
-	return className(g.pkg)
+func (g *JavaGen) className() string {
+	return className(g.Pkg)
 }
 
 func className(pkg *types.Package) string {
@@ -576,7 +606,7 @@
 	return strings.Title(javaNameReplacer.Replace(pkg.Name()))
 }
 
-func (g *javaGen) genConst(o *types.Const) {
+func (g *JavaGen) genConst(o *types.Const) {
 	if _, ok := o.Type().(*types.Basic); !ok {
 		g.Printf("// skipped const %s with unsupported type: %T\n\n", o.Name(), o)
 		return
@@ -609,14 +639,14 @@
 	g.Printf("public static final %s %s = %s;\n", g.javaType(o.Type()), o.Name(), val)
 }
 
-func (g *javaGen) genJNIField(o *types.TypeName, f *types.Var) {
+func (g *JavaGen) genJNIField(o *types.TypeName, f *types.Var) {
 	if t := f.Type(); !g.isSupported(t) {
 		g.Printf("// skipped field %s with unsupported type: %T\n\n", o.Name(), t)
 		return
 	}
 	// setter
 	g.Printf("JNIEXPORT void JNICALL\n")
-	g.Printf("Java_%s_%s_00024%s_set%s(JNIEnv *env, jobject this, %s v) {\n", g.jniPkgName(), g.className(), o.Name(), f.Name(), g.jniType(f.Type()))
+	g.Printf("Java_%s_%s_set%s(JNIEnv *env, jobject this, %s v) {\n", g.jniPkgName(), o.Name(), f.Name(), g.jniType(f.Type()))
 	g.Indent()
 	g.Printf("int32_t o = go_seq_to_refnum(env, this);\n")
 	g.genJavaToC("v", f.Type(), modeRetained)
@@ -627,7 +657,7 @@
 
 	// getter
 	g.Printf("JNIEXPORT %s JNICALL\n", g.jniType(f.Type()))
-	g.Printf("Java_%s_%s_00024%s_get%s(JNIEnv *env, jobject this) {\n", g.jniPkgName(), g.className(), o.Name(), f.Name())
+	g.Printf("Java_%s_%s_get%s(JNIEnv *env, jobject this) {\n", g.jniPkgName(), o.Name(), f.Name())
 	g.Indent()
 	g.Printf("int32_t o = go_seq_to_refnum(env, this);\n")
 	g.Printf("%s r0 = ", g.cgoType(f.Type()))
@@ -638,7 +668,7 @@
 	g.Printf("}\n\n")
 }
 
-func (g *javaGen) genJNIVar(o *types.Var) {
+func (g *JavaGen) genJNIVar(o *types.Var) {
 	if t := o.Type(); !g.isSupported(t) {
 		g.Printf("// skipped variable %s with unsupported type: %T\n\n", o.Name(), t)
 		return
@@ -665,7 +695,7 @@
 	g.Printf("}\n\n")
 }
 
-func (g *javaGen) genJNIFunc(o *types.Func, sName string, proxy bool) {
+func (g *JavaGen) genJNIFunc(o *types.Func, sName string, proxy bool) {
 	if !g.isSigSupported(o.Type()) {
 		n := o.Name()
 		if sName != "" {
@@ -732,7 +762,7 @@
 }
 
 // genRelease cleans up arguments that weren't copied in genJavaToC.
-func (g *javaGen) genRelease(varName string, t types.Type, mode varMode) {
+func (g *JavaGen) genRelease(varName string, t types.Type, mode varMode) {
 	switch t := t.(type) {
 	case *types.Basic:
 	case *types.Slice:
@@ -750,7 +780,7 @@
 	}
 }
 
-func (g *javaGen) genMethodInterfaceProxy(oName string, m *types.Func) {
+func (g *JavaGen) genMethodInterfaceProxy(oName string, m *types.Func) {
 	if !g.isSigSupported(m.Type()) {
 		g.Printf("// skipped method %s with unsupported parameter or return types\n\n", oName)
 		return
@@ -811,10 +841,10 @@
 	g.Printf("}\n\n")
 }
 
-func (g *javaGen) genH() error {
+func (g *JavaGen) GenH() error {
 	pkgPath := ""
-	if g.pkg != nil {
-		pkgPath = g.pkg.Path()
+	if g.Pkg != nil {
+		pkgPath = g.Pkg.Path()
 	}
 	g.Printf(hPreamble, g.gobindOpts(), pkgPath, g.className())
 	for _, iface := range g.interfaces {
@@ -841,7 +871,7 @@
 	return nil
 }
 
-func (g *javaGen) jniCallType(t types.Type) string {
+func (g *JavaGen) jniCallType(t types.Type) string {
 	switch t := t.(type) {
 	case *types.Basic:
 		switch t.Kind() {
@@ -881,11 +911,11 @@
 	return "TODO"
 }
 
-func (g *javaGen) jniClassSigPrefix(pkg *types.Package) string {
-	return strings.Replace(g.javaPkgName(pkg), ".", "/", -1) + "/" + className(pkg) + "$"
+func (g *JavaGen) jniClassSigPrefix(pkg *types.Package) string {
+	return strings.Replace(g.javaPkgName(pkg), ".", "/", -1) + "/"
 }
 
-func (g *javaGen) jniSigType(T types.Type) string {
+func (g *JavaGen) jniSigType(T types.Type) string {
 	switch T := T.(type) {
 	case *types.Basic:
 		switch T.Kind() {
@@ -928,18 +958,18 @@
 	return "TODO"
 }
 
-func (g *javaGen) genC() error {
+func (g *JavaGen) GenC() error {
 	var pkgName, pkgPath string
-	if g.pkg != nil {
-		pkgName = g.pkg.Name()
-		pkgPath = g.pkg.Path()
+	if g.Pkg != nil {
+		pkgName = g.Pkg.Name()
+		pkgPath = g.Pkg.Path()
 	} else {
 		pkgName = "universe"
 	}
 	g.Printf(cPreamble, g.gobindOpts(), pkgPath)
 	g.Printf("#include %q\n", pkgName+".h")
-	if g.pkg != nil {
-		for _, pkg := range g.pkg.Imports() {
+	if g.Pkg != nil {
+		for _, pkg := range g.Pkg.Imports() {
 			if g.validPkg(pkg) {
 				g.Printf("#include \"%s.h\"\n", pkg.Name())
 			}
@@ -973,10 +1003,11 @@
 		g.Printf("proxy_class_%s_%s_cons = (*env)->GetMethodID(env, clazz, \"<init>\", \"(Lgo/Seq$Ref;)V\");\n", g.pkgPrefix, s.obj.Name())
 	}
 	for _, iface := range g.interfaces {
-		g.Printf("clazz = (*env)->FindClass(env, %q);\n", g.jniClassSigPrefix(iface.obj.Pkg())+"proxy"+iface.obj.Name())
+		pkg := iface.obj.Pkg()
+		g.Printf("clazz = (*env)->FindClass(env, %q);\n", g.jniClassSigPrefix(pkg)+className(pkg)+"$proxy"+iface.obj.Name())
 		g.Printf("proxy_class_%s_%s = (*env)->NewGlobalRef(env, clazz);\n", g.pkgPrefix, iface.obj.Name())
 		g.Printf("proxy_class_%s_%s_cons = (*env)->GetMethodID(env, clazz, \"<init>\", \"(Lgo/Seq$Ref;)V\");\n", g.pkgPrefix, iface.obj.Name())
-		g.Printf("clazz = (*env)->FindClass(env, %q);\n", g.jniClassSigPrefix(iface.obj.Pkg())+iface.obj.Name())
+		g.Printf("clazz = (*env)->FindClass(env, %q);\n", g.jniClassSigPrefix(pkg)+iface.obj.Name())
 		for _, m := range iface.summary.callable {
 			if !g.isSigSupported(m.Type()) {
 				g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", iface.obj.Name(), m.Name())
@@ -1029,20 +1060,20 @@
 	return nil
 }
 
-func (g *javaGen) genJava() error {
+func (g *JavaGen) GenJava() error {
 	pkgPath := ""
-	if g.pkg != nil {
-		pkgPath = g.pkg.Path()
+	if g.Pkg != nil {
+		pkgPath = g.Pkg.Path()
 	}
-	g.Printf(javaPreamble, g.javaPkgName(g.pkg), g.className(), g.gobindOpts(), pkgPath)
+	g.Printf(javaPreamble, g.javaPkgName(g.Pkg), g.className(), g.gobindOpts(), pkgPath)
 
 	g.Printf("public abstract class %s {\n", g.className())
 	g.Indent()
 	g.Printf("static {\n")
 	g.Indent()
 	g.Printf("Seq.touch(); // for loading the native library\n")
-	if g.pkg != nil {
-		for _, p := range g.pkg.Imports() {
+	if g.Pkg != nil {
+		for _, p := range g.Pkg.Imports() {
 			if g.validPkg(p) {
 				g.Printf("%s.%s.touch();\n", g.javaPkgName(p), className(p))
 			}
@@ -1056,12 +1087,24 @@
 	g.Printf("public static void touch() {}\n\n")
 	g.Printf("private static native void init();\n\n")
 
-	for _, s := range g.structs {
-		g.genStruct(s.obj, s.t)
-	}
 	for _, iface := range g.interfaces {
-		g.genInterface(iface)
+		g.Printf(javaProxyPreamble, iface.obj.Name())
+		g.Indent()
+
+		for _, m := range iface.summary.callable {
+			if !g.isSigSupported(m.Type()) {
+				g.Printf("// skipped method %s.%s with unsupported parameter or return types\n\n", iface.obj.Name(), m.Name())
+				continue
+			}
+			g.genFuncSignature(m, false, false)
+		}
+
+		g.Outdent()
+		g.Printf("}\n")
 	}
+
+	g.Printf("\n")
+
 	for _, c := range g.constants {
 		g.genConst(c)
 	}
diff --git a/bind/genobjc.go b/bind/genobjc.go
index d743c0e..f067c03 100644
--- a/bind/genobjc.go
+++ b/bind/genobjc.go
@@ -27,7 +27,7 @@
 	// fields set by init.
 	namePrefix string
 
-	*generator
+	*Generator
 }
 
 type interfaceInfo struct {
@@ -42,8 +42,8 @@
 }
 
 func (g *objcGen) init() {
-	g.generator.init()
-	g.namePrefix = g.namePrefixOf(g.pkg)
+	g.Generator.Init()
+	g.namePrefix = g.namePrefixOf(g.Pkg)
 }
 
 func (g *objcGen) namePrefixOf(pkg *types.Package) string {
@@ -59,8 +59,8 @@
 
 func (g *objcGen) genGoH() error {
 	var pkgPath string
-	if g.pkg != nil {
-		pkgPath = g.pkg.Path()
+	if g.Pkg != nil {
+		pkgPath = g.Pkg.Path()
 	}
 	g.Printf(objcPreamble, pkgPath, g.gobindOpts(), pkgPath)
 	g.Printf("#ifndef __%s_H__\n", g.pkgName)
@@ -92,8 +92,8 @@
 
 func (g *objcGen) genH() error {
 	var pkgPath string
-	if g.pkg != nil {
-		pkgPath = g.pkg.Path()
+	if g.Pkg != nil {
+		pkgPath = g.Pkg.Path()
 	}
 	g.Printf(objcPreamble, pkgPath, g.gobindOpts(), pkgPath)
 	g.Printf("#ifndef __%s_H__\n", g.namePrefix)
@@ -101,8 +101,8 @@
 	g.Printf("\n")
 	g.Printf("#include <Foundation/Foundation.h>\n")
 	g.Printf("#include \"GoUniverse.h\"\n")
-	if g.pkg != nil {
-		for _, pkg := range g.pkg.Imports() {
+	if g.Pkg != nil {
+		for _, pkg := range g.Pkg.Imports() {
 			if g.validPkg(pkg) {
 				g.Printf("#include %q\n", g.namePrefixOf(pkg)+".h")
 			}
@@ -207,8 +207,8 @@
 func (g *objcGen) genM() error {
 	var pkgPath string
 	var errDomain string
-	if g.pkg != nil {
-		pkgPath = g.pkg.Path()
+	if g.Pkg != nil {
+		pkgPath = g.Pkg.Path()
 		errDomain = "go." + pkgPath
 	} else {
 		errDomain = "go"
diff --git a/bind/java/CustomPkgTest.java b/bind/java/CustomPkgTest.java
index 723389c..5dd6abc 100644
--- a/bind/java/CustomPkgTest.java
+++ b/bind/java/CustomPkgTest.java
@@ -6,7 +6,7 @@
 
 import android.test.InstrumentationTestCase;
 
-import org.golang.custompkg.Testpkg;
+import org.golang.custompkg.testpkg.Testpkg;
 
 public class CustomPkgTest extends InstrumentationTestCase {
   public void testHi() {
diff --git a/bind/java/Seq.java b/bind/java/Seq.java
index 564dd08..40012c7 100644
--- a/bind/java/Seq.java
+++ b/bind/java/Seq.java
@@ -9,6 +9,7 @@
 import java.util.logging.Logger;
 
 import go.Universe;
+import go.error;
 
 // Seq is a sequence of machine-dependent encoded values.
 // Used by automatically generated language bindings to talk to Go.
@@ -47,12 +48,12 @@
 	private Seq() {
 	}
 
-	private static void throwException(Universe.error err) throws Exception {
+	private static void throwException(error err) throws Exception {
 		throw new Exception(err.Error());
 	}
 
-	private static Universe.error wrapThrowable(final Throwable t) {
-		return new Universe.error() {
+	private static error wrapThrowable(final Throwable t) {
+		return new error() {
 			@Override public String Error() {
 				return t.getMessage();
 			}
diff --git a/bind/java/SeqBench.java b/bind/java/SeqBench.java
index e28754f..d06964d 100644
--- a/bind/java/SeqBench.java
+++ b/bind/java/SeqBench.java
@@ -13,16 +13,16 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.ExecutorService;
 
-import go.benchmark.Benchmark;
+import go.benchmark.*;
 
 public class SeqBench extends InstrumentationTestCase {
 
-  public static class AnI implements Benchmark.I {
+  public static class AnI implements I {
     @Override public void F() {
     }
   }
 
-  private static class Benchmarks implements Benchmark.Benchmarks {
+  private static class Benchmarks implements go.benchmark.Benchmarks {
     private static Map<String, Runnable> benchmarks;
     private static ExecutorService executor = Executors.newSingleThreadExecutor();
 
@@ -52,13 +52,13 @@
           Benchmark.Oneret();
         }
       });
-      final Benchmark.I javaRef = new AnI();
+      final I javaRef = new AnI();
       benchmarks.put("Refforeign", new Runnable() {
         @Override public void run() {
           Benchmark.Ref(javaRef);
         }
       });
-      final Benchmark.I goRef = Benchmark.NewI();
+      final I goRef = Benchmark.NewI();
       benchmarks.put("Refgo", new Runnable() {
         @Override public void run() {
           Benchmark.Ref(goRef);
@@ -130,10 +130,10 @@
       }
     }
 
-    @Override public Benchmark.I NewI() {
+    @Override public I NewI() {
       return new AnI();
     }
-    @Override public void Ref(Benchmark.I i) {
+    @Override public void Ref(I i) {
     }
     @Override public void Noargs() {
     }
diff --git a/bind/java/SeqTest.java b/bind/java/SeqTest.java
index bab54cf..340b93e 100644
--- a/bind/java/SeqTest.java
+++ b/bind/java/SeqTest.java
@@ -10,7 +10,7 @@
 import java.util.Arrays;
 import java.util.Random;
 
-import go.testpkg.Testpkg;
+import go.testpkg.*;
 import go.secondpkg.Secondpkg;
 
 public class SeqTest extends InstrumentationTestCase {
@@ -63,9 +63,9 @@
     Testpkg.setIntVar(newIntVar);
     assertEquals("var IntVar", newIntVar, Testpkg.getIntVar());
 
-    Testpkg.S s0 = Testpkg.getStructVar();
+    S s0 = Testpkg.getStructVar();
     assertEquals("var StructVar", "a struct var", s0.String());
-    Testpkg.S s1 = Testpkg.New();
+    S s1 = Testpkg.New();
     Testpkg.setStructVar(s1);
     assertEquals("var StructVar", s1.String(), Testpkg.getStructVar().String());
 
@@ -205,7 +205,7 @@
   }
 
   public void testGoRefGC() {
-    Testpkg.S s = Testpkg.New();
+    S s = Testpkg.New();
     runGC();
     long collected = Testpkg.NumSCollected();
     assertEquals("Only S should be pinned", 0, collected);
@@ -216,7 +216,7 @@
     assertEquals("S should be collected", 1, collected);
   }
 
-  private class AnI implements Testpkg.I {
+  private class AnI implements I {
     public void E() throws Exception {
       throw new Exception("my exception from E");
     }
@@ -226,15 +226,15 @@
       calledF = true;
     }
 
-    public Testpkg.I I() {
+    public I I() {
       return this;
     }
 
-    public Testpkg.S S() {
+    public S S() {
       return Testpkg.New();
     }
 
-    public String StoString(Testpkg.S s) {
+    public String StoString(S s) {
       return s.String();
     }
 
@@ -275,7 +275,7 @@
   public void testInterfaceMethodReturnsInterface() {
     AnI obj = new AnI();
     obj.name = "testing AnI.I";
-    Testpkg.I i = Testpkg.CallI(obj);
+    I i = Testpkg.CallI(obj);
     assertEquals("Want AnI.I to return itself", i.String(), obj.String());
 
     runGC();
@@ -287,14 +287,14 @@
   public void testInterfaceMethodReturnsStructPointer() {
     final AnI obj = new AnI();
     for (int i = 0; i < 5; i++) {
-    	Testpkg.S s = Testpkg.CallS(obj);
+    	S s = Testpkg.CallS(obj);
 	runGC();
     }
   }
 
   public void testInterfaceMethodTakesStructPointer() {
     final AnI obj = new AnI();
-    Testpkg.S s = Testpkg.CallS(obj);
+    S s = Testpkg.CallS(obj);
     String got = obj.StoString(s);
     String want = s.String();
     assertEquals("Want AnI.StoString(s) to call s's String", want, got);
@@ -355,13 +355,13 @@
 
   private int countI = 0;
 
-  private class CountI implements Testpkg.I {
+  private class CountI implements I {
     public void F() { countI++; }
 
     public void E() throws Exception {}
-    public Testpkg.I I() { return null; }
-    public Testpkg.S S() { return null; }
-    public String StoString(Testpkg.S s) { return ""; }
+    public I I() { return null; }
+    public S S() { return null; }
+    public String StoString(S s) { return ""; }
     public long V() { return 0; }
     public long VE() throws Exception { return 0; }
     public String String() { return ""; }
@@ -404,20 +404,20 @@
   }
 
   public void testPointerToStructAsField() {
-    Testpkg.Node a = Testpkg.NewNode("A");
-    Testpkg.Node b = Testpkg.NewNode("B");
+    Node a = Testpkg.NewNode("A");
+    Node b = Testpkg.NewNode("B");
     a.setNext(b);
     String got = a.String();
     assertEquals("want Node A points to Node B", "A:B:<end>", got);
   }
 
   public void testImplementsInterface() {
-    Testpkg.Interface intf = Testpkg.NewConcrete();
+    Interface intf = Testpkg.NewConcrete();
   }
 
   public void testErrorField() {
     final String want = "an error message";
-    Testpkg.Node n = Testpkg.NewNode("ErrTest");
+    Node n = Testpkg.NewNode("ErrTest");
     n.setErr(want);
     String got = n.getErr();
     assertEquals("want back the error message we set", want, got);
@@ -425,7 +425,7 @@
 
   //test if we have JNI local reference table overflow error
   public void testLocalReferenceOverflow() {
-    Testpkg.CallWithCallback(new Testpkg.GoCallback() {
+    Testpkg.CallWithCallback(new GoCallback() {
 
       @Override
       public void VarUpdate() {
@@ -435,8 +435,8 @@
   }
 
   public void testNullReferences() {
-    assertTrue(Testpkg.CallWithNull(null, new Testpkg.NullTest() {
-      public Testpkg.NullTest Null() {
+    assertTrue(Testpkg.CallWithNull(null, new NullTest() {
+      public NullTest Null() {
         return null;
       }
     }));
@@ -445,7 +445,7 @@
   }
 
   public void testPassByteArray() {
-    Testpkg.PassByteArray(new Testpkg.B() {
+    Testpkg.PassByteArray(new B() {
       @Override public void B(byte[] b) {
         byte[] want = new byte[]{1, 2, 3, 4};
         MoreAsserts.assertEquals("bytes should match", want, b);
@@ -468,28 +468,28 @@
   }
 
   public void testGoroutineCallback() {
-    Testpkg.GoroutineCallback(new Testpkg.Receiver() {
+    Testpkg.GoroutineCallback(new Receiver() {
       @Override public void Hello(String msg) {
       }
     });
   }
 
   public void testImportedPkg() {
-    Testpkg.CallImportedI(new Secondpkg.I() {
+    Testpkg.CallImportedI(new go.secondpkg.I() {
       @Override public long F(long i) {
         return i;
       }
     });
     assertEquals("imported string should match", Secondpkg.HelloString, Secondpkg.Hello());
-    Secondpkg.I i = Testpkg.NewImportedI();
-    Secondpkg.S s = Testpkg.NewImportedS();
+    go.secondpkg.I i = Testpkg.NewImportedI();
+    go.secondpkg.S s = Testpkg.NewImportedS();
     i = Testpkg.getImportedVarI();
     s = Testpkg.getImportedVarS();
     assertEquals("numbers should match", 8, i.F(8));
     assertEquals("numbers should match", 8, s.F(8));
     Testpkg.setImportedVarI(i);
     Testpkg.setImportedVarS(s);
-    Testpkg.ImportedFields fields = Testpkg.NewImportedFields();
+    ImportedFields fields = Testpkg.NewImportedFields();
     i = fields.getI();
     s = fields.getS();
     fields.setI(i);
@@ -497,22 +497,22 @@
     Testpkg.WithImportedI(i);
     Testpkg.WithImportedS(s);
 
-    Secondpkg.IF f = new AnI();
+    go.secondpkg.IF f = new AnI();
     f = Testpkg.New();
-    Secondpkg.Ser ser = Testpkg.NewSer();
+    go.secondpkg.Ser ser = Testpkg.NewSer();
   }
 
   public void testRoundtripEquality() {
-    Testpkg.I want = new AnI();
+    I want = new AnI();
     assertTrue("java object passed through Go should not be wrapped", want == Testpkg.IDup(want));
-    Testpkg.InterfaceDupper idup = new Testpkg.InterfaceDupper(){
-      @Override public Testpkg.Interface IDup(Testpkg.Interface i) {
+    InterfaceDupper idup = new InterfaceDupper(){
+      @Override public Interface IDup(Interface i) {
         return i;
       }
     };
     assertTrue("Go interface passed through Java should not be wrapped", Testpkg.CallIDupper(idup));
-    Testpkg.ConcreteDupper cdup = new Testpkg.ConcreteDupper(){
-      @Override public Testpkg.Concrete CDup(Testpkg.Concrete c) {
+    ConcreteDupper cdup = new ConcreteDupper(){
+      @Override public Concrete CDup(Concrete c) {
         return c;
       }
     };
@@ -525,7 +525,7 @@
       fail("Empty error wasn't caught");
     } catch (Exception e) {
     }
-    Testpkg.EmptyErrorer empty = new Testpkg.EmptyErrorer() {
+    EmptyErrorer empty = new EmptyErrorer() {
       @Override public void EmptyError() throws Exception {
         throw new Exception("");
       }
diff --git a/bind/java/seq_android.c.support b/bind/java/seq_android.c.support
index 2bc1c6a..4539f9c 100644
--- a/bind/java/seq_android.c.support
+++ b/bind/java/seq_android.c.support
@@ -291,7 +291,7 @@
 	}
 
 	seq_class = (*env)->NewGlobalRef(env, clazz);
-	seq_throw_exc = (*env)->GetStaticMethodID(env, seq_class, "throwException", "(Lgo/Universe$error;)V");
+	seq_throw_exc = (*env)->GetStaticMethodID(env, seq_class, "throwException", "(Lgo/error;)V");
 	if (seq_throw_exc == NULL) {
 		LOG_FATAL("failed to find method Seq.throwException");
 	}
@@ -312,7 +312,7 @@
 	if (seq_incRef == NULL) {
 		LOG_FATAL("failed to find method Seq.incRef");
 	}
-	seq_wrapThrowable = (*env)->GetStaticMethodID(env, seq_class, "wrapThrowable", "(Ljava/lang/Throwable;)Lgo/Universe$error;");
+	seq_wrapThrowable = (*env)->GetStaticMethodID(env, seq_class, "wrapThrowable", "(Ljava/lang/Throwable;)Lgo/error;");
 	if (seq_wrapThrowable == NULL) {
 		LOG_FATAL("failed to find method Seq.wrapThrowable");
 	}
diff --git a/bind/printer.go b/bind/printer.go
index 39cb809..3d24ea0 100644
--- a/bind/printer.go
+++ b/bind/printer.go
@@ -9,23 +9,23 @@
 	"fmt"
 )
 
-type printer struct {
-	buf        *bytes.Buffer
-	indentEach []byte
+type Printer struct {
+	Buf        *bytes.Buffer
+	IndentEach []byte
 	indentText []byte
 	needIndent bool
 }
 
-func (p *printer) writeIndent() error {
+func (p *Printer) writeIndent() error {
 	if !p.needIndent {
 		return nil
 	}
 	p.needIndent = false
-	_, err := p.buf.Write(p.indentText)
+	_, err := p.Buf.Write(p.indentText)
 	return err
 }
 
-func (p *printer) Write(b []byte) (n int, err error) {
+func (p *Printer) Write(b []byte) (n int, err error) {
 	wrote := 0
 	for len(b) > 0 {
 		if err := p.writeIndent(); err != nil {
@@ -35,7 +35,7 @@
 		if i < 0 {
 			break
 		}
-		n, err = p.buf.Write(b[0 : i+1])
+		n, err = p.Buf.Write(b[0 : i+1])
 		wrote += n
 		if err != nil {
 			return wrote, err
@@ -44,24 +44,24 @@
 		p.needIndent = true
 	}
 	if len(b) > 0 {
-		n, err = p.buf.Write(b)
+		n, err = p.Buf.Write(b)
 		wrote += n
 	}
 	return wrote, err
 }
 
-func (p *printer) Printf(format string, args ...interface{}) {
+func (p *Printer) Printf(format string, args ...interface{}) {
 	if _, err := fmt.Fprintf(p, format, args...); err != nil {
 		panic(fmt.Sprintf("printer: %v", err))
 	}
 }
 
-func (p *printer) Indent() {
-	p.indentText = append(p.indentText, p.indentEach...)
+func (p *Printer) Indent() {
+	p.indentText = append(p.indentText, p.IndentEach...)
 }
 
-func (p *printer) Outdent() {
-	if len(p.indentText) > len(p.indentEach)-1 {
-		p.indentText = p.indentText[len(p.indentEach):]
+func (p *Printer) Outdent() {
+	if len(p.indentText) > len(p.IndentEach)-1 {
+		p.indentText = p.indentText[len(p.IndentEach):]
 	}
 }
diff --git a/bind/testdata/basictypes.java.golden b/bind/testdata/basictypes.java.golden
index 887acca..d8986a2 100644
--- a/bind/testdata/basictypes.java.golden
+++ b/bind/testdata/basictypes.java.golden
@@ -19,6 +19,7 @@
     
     private static native void init();
     
+    
     public static final boolean ABool = true;
     public static final double AFloat = 0.2015;
     public static final String ALongString = "LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString,LongString";
diff --git a/bind/testdata/customprefix.java.c.golden b/bind/testdata/customprefix.java.c.golden
index 00074b9..456fcf7986 100644
--- a/bind/testdata/customprefix.java.c.golden
+++ b/bind/testdata/customprefix.java.c.golden
@@ -11,12 +11,12 @@
 
 
 JNIEXPORT void JNICALL
-Java_com_example_Customprefix_init(JNIEnv *env, jclass _unused) {
+Java_com_example_customprefix_Customprefix_init(JNIEnv *env, jclass _unused) {
     jclass clazz;
 }
 
 JNIEXPORT void JNICALL
-Java_com_example_Customprefix_F(JNIEnv* env, jclass clazz) {
+Java_com_example_customprefix_Customprefix_F(JNIEnv* env, jclass clazz) {
     proxycustomprefix__F();
 }
 
diff --git a/bind/testdata/customprefix.java.golden b/bind/testdata/customprefix.java.golden
index 04693ed..05f2282 100644
--- a/bind/testdata/customprefix.java.golden
+++ b/bind/testdata/customprefix.java.golden
@@ -1,8 +1,8 @@
-// Java class com.example.Customprefix is a proxy for talking to a Go program.
+// Java class com.example.customprefix.Customprefix is a proxy for talking to a Go program.
 //   gobind -lang=java -javapkg=com.example customprefix
 //
 // File is generated by gobind. Do not edit.
-package com.example;
+package com.example.customprefix;
 
 import go.Seq;
 
@@ -20,5 +20,6 @@
     private static native void init();
     
     
+    
     public static native void F();
 }
diff --git a/bind/testdata/ignore.java.c.golden b/bind/testdata/ignore.java.c.golden
index 8b3c5a5..6ef3b36 100644
--- a/bind/testdata/ignore.java.c.golden
+++ b/bind/testdata/ignore.java.c.golden
@@ -21,13 +21,13 @@
 JNIEXPORT void JNICALL
 Java_go_ignore_Ignore_init(JNIEnv *env, jclass _unused) {
     jclass clazz;
-    clazz = (*env)->FindClass(env, "go/ignore/Ignore$S");
+    clazz = (*env)->FindClass(env, "go/ignore/S");
     proxy_class_ignore_S = (*env)->NewGlobalRef(env, clazz);
     proxy_class_ignore_S_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
     clazz = (*env)->FindClass(env, "go/ignore/Ignore$proxyI");
     proxy_class_ignore_I = (*env)->NewGlobalRef(env, clazz);
     proxy_class_ignore_I_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/ignore/Ignore$I");
+    clazz = (*env)->FindClass(env, "go/ignore/I");
     // skipped method I.Argument with unsupported parameter or return types
     
     // skipped method I.Result with unsupported parameter or return types
diff --git a/bind/testdata/ignore.java.golden b/bind/testdata/ignore.java.golden
index ef385ff..065b7d7 100644
--- a/bind/testdata/ignore.java.golden
+++ b/bind/testdata/ignore.java.golden
@@ -1,3 +1,57 @@
+// Java class go.ignore.S is a proxy for talking to a Go program.
+//   gobind -lang=java ignore
+//
+// File is generated by gobind. Do not edit.
+package go.ignore;
+
+import go.Seq;
+
+public final class S extends Seq.Proxy implements I {
+    private S(go.Seq.Ref ref) { super(ref); }
+    
+    // skipped field S.F with unsupported type: *types.Interface
+    
+    // skipped method S.Argument with unsupported parameter or return types
+    
+    // skipped method S.Result with unsupported parameter or return types
+    
+    @Override public boolean equals(Object o) {
+        if (o == null || !(o instanceof S)) {
+            return false;
+        }
+        S that = (S)o;
+        // skipped field S.F with unsupported type: *types.Interface
+        
+        return true;
+    }
+    
+    @Override public int hashCode() {
+        return java.util.Arrays.hashCode(new Object[] {});
+    }
+    
+    @Override public String toString() {
+        StringBuilder b = new StringBuilder();
+        b.append("S").append("{");
+        return b.append("}").toString();
+    }
+}
+
+// Java class go.ignore.I is a proxy for talking to a Go program.
+//   gobind -lang=java ignore
+//
+// File is generated by gobind. Do not edit.
+package go.ignore;
+
+import go.Seq;
+
+public interface I {
+    // skipped method I.Argument with unsupported parameter or return types
+    
+    // skipped method I.Result with unsupported parameter or return types
+    
+    
+}
+
 // Java class go.ignore.Ignore is a proxy for talking to a Go program.
 //   gobind -lang=java ignore
 //
@@ -19,43 +73,6 @@
     
     private static native void init();
     
-    public static final class S extends Seq.Proxy implements I {
-        private S(go.Seq.Ref ref) { super(ref); }
-        
-        // skipped field S.F with unsupported type: *types.Interface
-        
-        // skipped method S.Argument with unsupported parameter or return types
-        
-        // skipped method S.Result with unsupported parameter or return types
-        
-        @Override public boolean equals(Object o) {
-            if (o == null || !(o instanceof S)) {
-                return false;
-            }
-            S that = (S)o;
-            // skipped field S.F with unsupported type: *types.Interface
-            
-            return true;
-        }
-        
-        @Override public int hashCode() {
-            return java.util.Arrays.hashCode(new Object[] {});
-        }
-        
-        @Override public String toString() {
-            StringBuilder b = new StringBuilder();
-            b.append("S").append("{");
-            return b.append("}").toString();
-        }
-    }
-    
-    public interface I {
-        // skipped method I.Argument with unsupported parameter or return types
-        
-        // skipped method I.Result with unsupported parameter or return types
-        
-    }
-    
     private static final class proxyI extends Seq.Proxy implements I {
         proxyI(Seq.Ref ref) { super(ref); }
     
diff --git a/bind/testdata/interfaces.java.c.golden b/bind/testdata/interfaces.java.c.golden
index f151ded..444f86a 100644
--- a/bind/testdata/interfaces.java.c.golden
+++ b/bind/testdata/interfaces.java.c.golden
@@ -41,50 +41,50 @@
     clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$proxyError");
     proxy_class_interfaces_Error = (*env)->NewGlobalRef(env, clazz);
     proxy_class_interfaces_Error_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$Error");
+    clazz = (*env)->FindClass(env, "go/interfaces/Error");
     mid_Error_Err = (*env)->GetMethodID(env, clazz, "Err", "()V");
     
     clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$proxyI");
     proxy_class_interfaces_I = (*env)->NewGlobalRef(env, clazz);
     proxy_class_interfaces_I_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$I");
+    clazz = (*env)->FindClass(env, "go/interfaces/I");
     mid_I_Rand = (*env)->GetMethodID(env, clazz, "Rand", "()I");
     
     clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$proxyI1");
     proxy_class_interfaces_I1 = (*env)->NewGlobalRef(env, clazz);
     proxy_class_interfaces_I1_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$I1");
+    clazz = (*env)->FindClass(env, "go/interfaces/I1");
     mid_I1_J = (*env)->GetMethodID(env, clazz, "J", "()V");
     
     clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$proxyI2");
     proxy_class_interfaces_I2 = (*env)->NewGlobalRef(env, clazz);
     proxy_class_interfaces_I2_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$I2");
+    clazz = (*env)->FindClass(env, "go/interfaces/I2");
     mid_I2_G = (*env)->GetMethodID(env, clazz, "G", "()V");
     
     clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$proxyI3");
     proxy_class_interfaces_I3 = (*env)->NewGlobalRef(env, clazz);
     proxy_class_interfaces_I3_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$I3");
-    mid_I3_F = (*env)->GetMethodID(env, clazz, "F", "()Lgo/interfaces/Interfaces$I1;");
+    clazz = (*env)->FindClass(env, "go/interfaces/I3");
+    mid_I3_F = (*env)->GetMethodID(env, clazz, "F", "()Lgo/interfaces/I1;");
     
     clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$proxyLargerI");
     proxy_class_interfaces_LargerI = (*env)->NewGlobalRef(env, clazz);
     proxy_class_interfaces_LargerI_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$LargerI");
+    clazz = (*env)->FindClass(env, "go/interfaces/LargerI");
     mid_LargerI_AnotherFunc = (*env)->GetMethodID(env, clazz, "AnotherFunc", "()V");
     mid_LargerI_Rand = (*env)->GetMethodID(env, clazz, "Rand", "()I");
     
     clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$proxySameI");
     proxy_class_interfaces_SameI = (*env)->NewGlobalRef(env, clazz);
     proxy_class_interfaces_SameI_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$SameI");
+    clazz = (*env)->FindClass(env, "go/interfaces/SameI");
     mid_SameI_Rand = (*env)->GetMethodID(env, clazz, "Rand", "()I");
     
     clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$proxyWithParam");
     proxy_class_interfaces_WithParam = (*env)->NewGlobalRef(env, clazz);
     proxy_class_interfaces_WithParam_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/interfaces/Interfaces$WithParam");
+    clazz = (*env)->FindClass(env, "go/interfaces/WithParam");
     mid_WithParam_HasParam = (*env)->GetMethodID(env, clazz, "HasParam", "(Z)V");
     
 }
diff --git a/bind/testdata/interfaces.java.golden b/bind/testdata/interfaces.java.golden
index a5717f9..9d55c30 100644
--- a/bind/testdata/interfaces.java.golden
+++ b/bind/testdata/interfaces.java.golden
@@ -1,3 +1,108 @@
+// Java class go.interfaces.Error is a proxy for talking to a Go program.
+//   gobind -lang=java interfaces
+//
+// File is generated by gobind. Do not edit.
+package go.interfaces;
+
+import go.Seq;
+
+public interface Error {
+    public void Err() throws Exception;
+    
+}
+
+// Java class go.interfaces.I is a proxy for talking to a Go program.
+//   gobind -lang=java interfaces
+//
+// File is generated by gobind. Do not edit.
+package go.interfaces;
+
+import go.Seq;
+
+public interface I {
+    public int Rand();
+    
+}
+
+// Java class go.interfaces.I1 is a proxy for talking to a Go program.
+//   gobind -lang=java interfaces
+//
+// File is generated by gobind. Do not edit.
+package go.interfaces;
+
+import go.Seq;
+
+public interface I1 {
+    public void J();
+    
+}
+
+// Java class go.interfaces.I2 is a proxy for talking to a Go program.
+//   gobind -lang=java interfaces
+//
+// File is generated by gobind. Do not edit.
+package go.interfaces;
+
+import go.Seq;
+
+public interface I2 {
+    public void G();
+    
+}
+
+// Java class go.interfaces.I3 is a proxy for talking to a Go program.
+//   gobind -lang=java interfaces
+//
+// File is generated by gobind. Do not edit.
+package go.interfaces;
+
+import go.Seq;
+
+public interface I3 {
+    public I1 F();
+    
+}
+
+// Java class go.interfaces.LargerI is a proxy for talking to a Go program.
+//   gobind -lang=java interfaces
+//
+// File is generated by gobind. Do not edit.
+package go.interfaces;
+
+import go.Seq;
+
+public interface LargerI extends I, SameI {
+    public void AnotherFunc();
+    public int Rand();
+    
+}
+
+// Java class go.interfaces.SameI is a proxy for talking to a Go program.
+//   gobind -lang=java interfaces
+//
+// File is generated by gobind. Do not edit.
+package go.interfaces;
+
+import go.Seq;
+
+public interface SameI {
+    public int Rand();
+    
+}
+
+// Java class go.interfaces.WithParam is a proxy for talking to a Go program.
+//   gobind -lang=java interfaces
+//
+// File is generated by gobind. Do not edit.
+package go.interfaces;
+
+import go.Seq;
+
+public interface WithParam {
+    public void HasParam(boolean p0);
+    
+}
+
 // Java class go.interfaces.Interfaces is a proxy for talking to a Go program.
 //   gobind -lang=java interfaces
 //
@@ -19,82 +124,42 @@
     
     private static native void init();
     
-    public interface Error {
-        public void Err() throws Exception;
-    }
-    
     private static final class proxyError extends Seq.Proxy implements Error {
         proxyError(Seq.Ref ref) { super(ref); }
     
         public native void Err() throws Exception;
     }
-    
-    public interface I {
-        public int Rand();
-    }
-    
     private static final class proxyI extends Seq.Proxy implements I {
         proxyI(Seq.Ref ref) { super(ref); }
     
         public native int Rand();
     }
-    
-    public interface I1 {
-        public void J();
-    }
-    
     private static final class proxyI1 extends Seq.Proxy implements I1 {
         proxyI1(Seq.Ref ref) { super(ref); }
     
         public native void J();
     }
-    
-    public interface I2 {
-        public void G();
-    }
-    
     private static final class proxyI2 extends Seq.Proxy implements I2 {
         proxyI2(Seq.Ref ref) { super(ref); }
     
         public native void G();
     }
-    
-    public interface I3 {
-        public I1 F();
-    }
-    
     private static final class proxyI3 extends Seq.Proxy implements I3 {
         proxyI3(Seq.Ref ref) { super(ref); }
     
         public native I1 F();
     }
-    
-    public interface LargerI extends I, SameI {
-        public void AnotherFunc();
-        public int Rand();
-    }
-    
     private static final class proxyLargerI extends Seq.Proxy implements LargerI {
         proxyLargerI(Seq.Ref ref) { super(ref); }
     
         public native void AnotherFunc();
         public native int Rand();
     }
-    
-    public interface SameI {
-        public int Rand();
-    }
-    
     private static final class proxySameI extends Seq.Proxy implements SameI {
         proxySameI(Seq.Ref ref) { super(ref); }
     
         public native int Rand();
     }
-    
-    public interface WithParam {
-        public void HasParam(boolean p0);
-    }
-    
     private static final class proxyWithParam extends Seq.Proxy implements WithParam {
         proxyWithParam(Seq.Ref ref) { super(ref); }
     
diff --git a/bind/testdata/issue10788.java.c.golden b/bind/testdata/issue10788.java.c.golden
index 96ed481..3a32f2e 100644
--- a/bind/testdata/issue10788.java.c.golden
+++ b/bind/testdata/issue10788.java.c.golden
@@ -19,27 +19,27 @@
 JNIEXPORT void JNICALL
 Java_go_issue10788_Issue10788_init(JNIEnv *env, jclass _unused) {
     jclass clazz;
-    clazz = (*env)->FindClass(env, "go/issue10788/Issue10788$TestStruct");
+    clazz = (*env)->FindClass(env, "go/issue10788/TestStruct");
     proxy_class_issue10788_TestStruct = (*env)->NewGlobalRef(env, clazz);
     proxy_class_issue10788_TestStruct_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
     clazz = (*env)->FindClass(env, "go/issue10788/Issue10788$proxyTestInterface");
     proxy_class_issue10788_TestInterface = (*env)->NewGlobalRef(env, clazz);
     proxy_class_issue10788_TestInterface_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/issue10788/Issue10788$TestInterface");
-    mid_TestInterface_DoSomeWork = (*env)->GetMethodID(env, clazz, "DoSomeWork", "(Lgo/issue10788/Issue10788$TestStruct;)V");
+    clazz = (*env)->FindClass(env, "go/issue10788/TestInterface");
+    mid_TestInterface_DoSomeWork = (*env)->GetMethodID(env, clazz, "DoSomeWork", "(Lgo/issue10788/TestStruct;)V");
     mid_TestInterface_MultipleUnnamedParams = (*env)->GetMethodID(env, clazz, "MultipleUnnamedParams", "(JLjava/lang/String;J)V");
     
 }
 
 JNIEXPORT void JNICALL
-Java_go_issue10788_Issue10788_00024TestStruct_setValue(JNIEnv *env, jobject this, jstring v) {
+Java_go_issue10788_TestStruct_setValue(JNIEnv *env, jobject this, jstring v) {
     int32_t o = go_seq_to_refnum(env, this);
     nstring _v = go_seq_from_java_string(env, v);
     proxyissue10788_TestStruct_Value_Set(o, _v);
 }
 
 JNIEXPORT jstring JNICALL
-Java_go_issue10788_Issue10788_00024TestStruct_getValue(JNIEnv *env, jobject this) {
+Java_go_issue10788_TestStruct_getValue(JNIEnv *env, jobject this) {
     int32_t o = go_seq_to_refnum(env, this);
     nstring r0 = proxyissue10788_TestStruct_Value_Get(o);
     jstring _r0 = go_seq_to_java_string(env, r0);
diff --git a/bind/testdata/issue10788.java.golden b/bind/testdata/issue10788.java.golden
index a41efd6..30aa170 100644
--- a/bind/testdata/issue10788.java.golden
+++ b/bind/testdata/issue10788.java.golden
@@ -1,3 +1,60 @@
+// Java class go.issue10788.TestStruct is a proxy for talking to a Go program.
+//   gobind -lang=java issue10788
+//
+// File is generated by gobind. Do not edit.
+package go.issue10788;
+
+import go.Seq;
+
+public final class TestStruct extends Seq.Proxy {
+    private TestStruct(go.Seq.Ref ref) { super(ref); }
+    
+    public final native String getValue();
+    public final native void setValue(String v);
+    
+    @Override public boolean equals(Object o) {
+        if (o == null || !(o instanceof TestStruct)) {
+            return false;
+        }
+        TestStruct that = (TestStruct)o;
+        String thisValue = getValue();
+        String thatValue = that.getValue();
+        if (thisValue == null) {
+            if (thatValue != null) {
+                return false;
+            }
+        } else if (!thisValue.equals(thatValue)) {
+            return false;
+        }
+        return true;
+    }
+    
+    @Override public int hashCode() {
+        return java.util.Arrays.hashCode(new Object[] {getValue()});
+    }
+    
+    @Override public String toString() {
+        StringBuilder b = new StringBuilder();
+        b.append("TestStruct").append("{");
+        b.append("Value:").append(getValue()).append(",");
+        return b.append("}").toString();
+    }
+}
+
+// Java class go.issue10788.TestInterface is a proxy for talking to a Go program.
+//   gobind -lang=java issue10788
+//
+// File is generated by gobind. Do not edit.
+package go.issue10788;
+
+import go.Seq;
+
+public interface TestInterface {
+    public void DoSomeWork(TestStruct s);
+    public void MultipleUnnamedParams(long p0, String p1, long p2);
+    
+}
+
 // Java class go.issue10788.Issue10788 is a proxy for talking to a Go program.
 //   gobind -lang=java issue10788
 //
@@ -19,46 +76,6 @@
     
     private static native void init();
     
-    public static final class TestStruct extends Seq.Proxy {
-        private TestStruct(go.Seq.Ref ref) { super(ref); }
-        
-        public final native String getValue();
-        public final native void setValue(String v);
-        
-        @Override public boolean equals(Object o) {
-            if (o == null || !(o instanceof TestStruct)) {
-                return false;
-            }
-            TestStruct that = (TestStruct)o;
-            String thisValue = getValue();
-            String thatValue = that.getValue();
-            if (thisValue == null) {
-                if (thatValue != null) {
-                    return false;
-                }
-            } else if (!thisValue.equals(thatValue)) {
-                return false;
-            }
-            return true;
-        }
-        
-        @Override public int hashCode() {
-            return java.util.Arrays.hashCode(new Object[] {getValue()});
-        }
-        
-        @Override public String toString() {
-            StringBuilder b = new StringBuilder();
-            b.append("TestStruct").append("{");
-            b.append("Value:").append(getValue()).append(",");
-            return b.append("}").toString();
-        }
-    }
-    
-    public interface TestInterface {
-        public void DoSomeWork(TestStruct s);
-        public void MultipleUnnamedParams(long p0, String p1, long p2);
-    }
-    
     private static final class proxyTestInterface extends Seq.Proxy implements TestInterface {
         proxyTestInterface(Seq.Ref ref) { super(ref); }
     
diff --git a/bind/testdata/issue12328.java.c.golden b/bind/testdata/issue12328.java.c.golden
index 9830f6b..ef74476 100644
--- a/bind/testdata/issue12328.java.c.golden
+++ b/bind/testdata/issue12328.java.c.golden
@@ -15,20 +15,20 @@
 JNIEXPORT void JNICALL
 Java_go_issue12328_Issue12328_init(JNIEnv *env, jclass _unused) {
     jclass clazz;
-    clazz = (*env)->FindClass(env, "go/issue12328/Issue12328$T");
+    clazz = (*env)->FindClass(env, "go/issue12328/T");
     proxy_class_issue12328_T = (*env)->NewGlobalRef(env, clazz);
     proxy_class_issue12328_T_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
 }
 
 JNIEXPORT void JNICALL
-Java_go_issue12328_Issue12328_00024T_setErr(JNIEnv *env, jobject this, jobject v) {
+Java_go_issue12328_T_setErr(JNIEnv *env, jobject this, jobject v) {
     int32_t o = go_seq_to_refnum(env, this);
     int32_t _v = go_seq_to_refnum(env, v);
     proxyissue12328_T_Err_Set(o, _v);
 }
 
 JNIEXPORT jobject JNICALL
-Java_go_issue12328_Issue12328_00024T_getErr(JNIEnv *env, jobject this) {
+Java_go_issue12328_T_getErr(JNIEnv *env, jobject this) {
     int32_t o = go_seq_to_refnum(env, this);
     int32_t r0 = proxyissue12328_T_Err_Get(o);
     jobject _r0 = go_seq_from_refnum(env, r0, proxy_class__error, proxy_class__error_cons);
diff --git a/bind/testdata/issue12328.java.golden b/bind/testdata/issue12328.java.golden
index 1a01f48..ce378e1 100644
--- a/bind/testdata/issue12328.java.golden
+++ b/bind/testdata/issue12328.java.golden
@@ -1,3 +1,46 @@
+// Java class go.issue12328.T is a proxy for talking to a Go program.
+//   gobind -lang=java issue12328
+//
+// File is generated by gobind. Do not edit.
+package go.issue12328;
+
+import go.Seq;
+
+public final class T extends Seq.Proxy {
+    private T(go.Seq.Ref ref) { super(ref); }
+    
+    public final native String getErr();
+    public final native void setErr(String v);
+    
+    @Override public boolean equals(Object o) {
+        if (o == null || !(o instanceof T)) {
+            return false;
+        }
+        T that = (T)o;
+        String thisErr = getErr();
+        String thatErr = that.getErr();
+        if (thisErr == null) {
+            if (thatErr != null) {
+                return false;
+            }
+        } else if (!thisErr.equals(thatErr)) {
+            return false;
+        }
+        return true;
+    }
+    
+    @Override public int hashCode() {
+        return java.util.Arrays.hashCode(new Object[] {getErr()});
+    }
+    
+    @Override public String toString() {
+        StringBuilder b = new StringBuilder();
+        b.append("T").append("{");
+        b.append("Err:").append(getErr()).append(",");
+        return b.append("}").toString();
+    }
+}
+
 // Java class go.issue12328.Issue12328 is a proxy for talking to a Go program.
 //   gobind -lang=java issue12328
 //
@@ -19,40 +62,6 @@
     
     private static native void init();
     
-    public static final class T extends Seq.Proxy {
-        private T(go.Seq.Ref ref) { super(ref); }
-        
-        public final native String getErr();
-        public final native void setErr(String v);
-        
-        @Override public boolean equals(Object o) {
-            if (o == null || !(o instanceof T)) {
-                return false;
-            }
-            T that = (T)o;
-            String thisErr = getErr();
-            String thatErr = that.getErr();
-            if (thisErr == null) {
-                if (thatErr != null) {
-                    return false;
-                }
-            } else if (!thisErr.equals(thatErr)) {
-                return false;
-            }
-            return true;
-        }
-        
-        @Override public int hashCode() {
-            return java.util.Arrays.hashCode(new Object[] {getErr()});
-        }
-        
-        @Override public String toString() {
-            StringBuilder b = new StringBuilder();
-            b.append("T").append("{");
-            b.append("Err:").append(getErr()).append(",");
-            return b.append("}").toString();
-        }
-    }
     
     
 }
diff --git a/bind/testdata/issue12403.java.c.golden b/bind/testdata/issue12403.java.c.golden
index f7323f1..64eec04 100644
--- a/bind/testdata/issue12403.java.c.golden
+++ b/bind/testdata/issue12403.java.c.golden
@@ -20,7 +20,7 @@
     clazz = (*env)->FindClass(env, "go/issue12403/Issue12403$proxyParsable");
     proxy_class_issue12403_Parsable = (*env)->NewGlobalRef(env, clazz);
     proxy_class_issue12403_Parsable_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/issue12403/Issue12403$Parsable");
+    clazz = (*env)->FindClass(env, "go/issue12403/Parsable");
     mid_Parsable_FromJSON = (*env)->GetMethodID(env, clazz, "FromJSON", "(Ljava/lang/String;)Ljava/lang/String;");
     mid_Parsable_ToJSON = (*env)->GetMethodID(env, clazz, "ToJSON", "()Ljava/lang/String;");
     
diff --git a/bind/testdata/issue12403.java.golden b/bind/testdata/issue12403.java.golden
index 1838705..e268ecc 100644
--- a/bind/testdata/issue12403.java.golden
+++ b/bind/testdata/issue12403.java.golden
@@ -1,3 +1,17 @@
+// Java class go.issue12403.Parsable is a proxy for talking to a Go program.
+//   gobind -lang=java issue12403
+//
+// File is generated by gobind. Do not edit.
+package go.issue12403;
+
+import go.Seq;
+
+public interface Parsable {
+    public String FromJSON(String jstr);
+    public String ToJSON() throws Exception;
+    
+}
+
 // Java class go.issue12403.Issue12403 is a proxy for talking to a Go program.
 //   gobind -lang=java issue12403
 //
@@ -19,11 +33,6 @@
     
     private static native void init();
     
-    public interface Parsable {
-        public String FromJSON(String jstr);
-        public String ToJSON() throws Exception;
-    }
-    
     private static final class proxyParsable extends Seq.Proxy implements Parsable {
         proxyParsable(Seq.Ref ref) { super(ref); }
     
diff --git a/bind/testdata/structs.java.c.golden b/bind/testdata/structs.java.c.golden
index f2417cd..91897d9 100644
--- a/bind/testdata/structs.java.c.golden
+++ b/bind/testdata/structs.java.c.golden
@@ -20,16 +20,16 @@
 JNIEXPORT void JNICALL
 Java_go_structs_Structs_init(JNIEnv *env, jclass _unused) {
     jclass clazz;
-    clazz = (*env)->FindClass(env, "go/structs/Structs$S");
+    clazz = (*env)->FindClass(env, "go/structs/S");
     proxy_class_structs_S = (*env)->NewGlobalRef(env, clazz);
     proxy_class_structs_S_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/structs/Structs$S2");
+    clazz = (*env)->FindClass(env, "go/structs/S2");
     proxy_class_structs_S2 = (*env)->NewGlobalRef(env, clazz);
     proxy_class_structs_S2_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
     clazz = (*env)->FindClass(env, "go/structs/Structs$proxyI");
     proxy_class_structs_I = (*env)->NewGlobalRef(env, clazz);
     proxy_class_structs_I_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/structs/Structs$I");
+    clazz = (*env)->FindClass(env, "go/structs/I");
     mid_I_M = (*env)->GetMethodID(env, clazz, "M", "()V");
     
 }
@@ -53,7 +53,7 @@
 }
 
 JNIEXPORT jobject JNICALL
-Java_go_structs_Structs_00024S_Identity(JNIEnv* env, jobject this) {
+Java_go_structs_S_Identity(JNIEnv* env, jobject this) {
     int32_t o = go_seq_to_refnum(env, this);
     struct proxystructs_S_Identity_return res = proxystructs_S_Identity(o);
     jobject _r0 = go_seq_from_refnum(env, res.r0, proxy_class_structs_S, proxy_class_structs_S_cons);
@@ -63,7 +63,7 @@
 }
 
 JNIEXPORT jdouble JNICALL
-Java_go_structs_Structs_00024S_Sum(JNIEnv* env, jobject this) {
+Java_go_structs_S_Sum(JNIEnv* env, jobject this) {
     int32_t o = go_seq_to_refnum(env, this);
     double r0 = proxystructs_S_Sum(o);
     jdouble _r0 = (jdouble)r0;
@@ -71,14 +71,14 @@
 }
 
 JNIEXPORT void JNICALL
-Java_go_structs_Structs_00024S_setX(JNIEnv *env, jobject this, jdouble v) {
+Java_go_structs_S_setX(JNIEnv *env, jobject this, jdouble v) {
     int32_t o = go_seq_to_refnum(env, this);
     double _v = (double)v;
     proxystructs_S_X_Set(o, _v);
 }
 
 JNIEXPORT jdouble JNICALL
-Java_go_structs_Structs_00024S_getX(JNIEnv *env, jobject this) {
+Java_go_structs_S_getX(JNIEnv *env, jobject this) {
     int32_t o = go_seq_to_refnum(env, this);
     double r0 = proxystructs_S_X_Get(o);
     jdouble _r0 = (jdouble)r0;
@@ -86,14 +86,14 @@
 }
 
 JNIEXPORT void JNICALL
-Java_go_structs_Structs_00024S_setY(JNIEnv *env, jobject this, jdouble v) {
+Java_go_structs_S_setY(JNIEnv *env, jobject this, jdouble v) {
     int32_t o = go_seq_to_refnum(env, this);
     double _v = (double)v;
     proxystructs_S_Y_Set(o, _v);
 }
 
 JNIEXPORT jdouble JNICALL
-Java_go_structs_Structs_00024S_getY(JNIEnv *env, jobject this) {
+Java_go_structs_S_getY(JNIEnv *env, jobject this) {
     int32_t o = go_seq_to_refnum(env, this);
     double r0 = proxystructs_S_Y_Get(o);
     jdouble _r0 = (jdouble)r0;
@@ -101,13 +101,13 @@
 }
 
 JNIEXPORT void JNICALL
-Java_go_structs_Structs_00024S2_M(JNIEnv* env, jobject this) {
+Java_go_structs_S2_M(JNIEnv* env, jobject this) {
     int32_t o = go_seq_to_refnum(env, this);
     proxystructs_S2_M(o);
 }
 
 JNIEXPORT jstring JNICALL
-Java_go_structs_Structs_00024S2_String(JNIEnv* env, jobject this) {
+Java_go_structs_S2_String(JNIEnv* env, jobject this) {
     int32_t o = go_seq_to_refnum(env, this);
     nstring r0 = proxystructs_S2_String(o);
     jstring _r0 = go_seq_to_java_string(env, r0);
diff --git a/bind/testdata/structs.java.golden b/bind/testdata/structs.java.golden
index 1f980ed..2b5a877 100644
--- a/bind/testdata/structs.java.golden
+++ b/bind/testdata/structs.java.golden
@@ -1,3 +1,96 @@
+// Java class go.structs.S is a proxy for talking to a Go program.
+//   gobind -lang=java structs
+//
+// File is generated by gobind. Do not edit.
+package go.structs;
+
+import go.Seq;
+
+public final class S extends Seq.Proxy {
+    private S(go.Seq.Ref ref) { super(ref); }
+    
+    public final native double getX();
+    public final native void setX(double v);
+    
+    public final native double getY();
+    public final native void setY(double v);
+    
+    public native S Identity() throws Exception;
+    public native double Sum();
+    @Override public boolean equals(Object o) {
+        if (o == null || !(o instanceof S)) {
+            return false;
+        }
+        S that = (S)o;
+        double thisX = getX();
+        double thatX = that.getX();
+        if (thisX != thatX) {
+            return false;
+        }
+        double thisY = getY();
+        double thatY = that.getY();
+        if (thisY != thatY) {
+            return false;
+        }
+        return true;
+    }
+    
+    @Override public int hashCode() {
+        return java.util.Arrays.hashCode(new Object[] {getX(), getY()});
+    }
+    
+    @Override public String toString() {
+        StringBuilder b = new StringBuilder();
+        b.append("S").append("{");
+        b.append("X:").append(getX()).append(",");
+        b.append("Y:").append(getY()).append(",");
+        return b.append("}").toString();
+    }
+}
+
+// Java class go.structs.S2 is a proxy for talking to a Go program.
+//   gobind -lang=java structs
+//
+// File is generated by gobind. Do not edit.
+package go.structs;
+
+import go.Seq;
+
+public final class S2 extends Seq.Proxy implements I {
+    private S2(go.Seq.Ref ref) { super(ref); }
+    
+    public native void M();
+    public native String String();
+    @Override public boolean equals(Object o) {
+        if (o == null || !(o instanceof S2)) {
+            return false;
+        }
+        S2 that = (S2)o;
+        return true;
+    }
+    
+    @Override public int hashCode() {
+        return java.util.Arrays.hashCode(new Object[] {});
+    }
+    
+    @Override public String toString() {
+        return String();
+    }
+}
+
+// Java class go.structs.I is a proxy for talking to a Go program.
+//   gobind -lang=java structs
+//
+// File is generated by gobind. Do not edit.
+package go.structs;
+
+import go.Seq;
+
+public interface I {
+    public void M();
+    
+}
+
 // Java class go.structs.Structs is a proxy for talking to a Go program.
 //   gobind -lang=java structs
 //
@@ -19,74 +112,6 @@
     
     private static native void init();
     
-    public static final class S extends Seq.Proxy {
-        private S(go.Seq.Ref ref) { super(ref); }
-        
-        public final native double getX();
-        public final native void setX(double v);
-        
-        public final native double getY();
-        public final native void setY(double v);
-        
-        public native S Identity() throws Exception;
-        public native double Sum();
-        @Override public boolean equals(Object o) {
-            if (o == null || !(o instanceof S)) {
-                return false;
-            }
-            S that = (S)o;
-            double thisX = getX();
-            double thatX = that.getX();
-            if (thisX != thatX) {
-                return false;
-            }
-            double thisY = getY();
-            double thatY = that.getY();
-            if (thisY != thatY) {
-                return false;
-            }
-            return true;
-        }
-        
-        @Override public int hashCode() {
-            return java.util.Arrays.hashCode(new Object[] {getX(), getY()});
-        }
-        
-        @Override public String toString() {
-            StringBuilder b = new StringBuilder();
-            b.append("S").append("{");
-            b.append("X:").append(getX()).append(",");
-            b.append("Y:").append(getY()).append(",");
-            return b.append("}").toString();
-        }
-    }
-    
-    public static final class S2 extends Seq.Proxy implements I {
-        private S2(go.Seq.Ref ref) { super(ref); }
-        
-        public native void M();
-        public native String String();
-        @Override public boolean equals(Object o) {
-            if (o == null || !(o instanceof S2)) {
-                return false;
-            }
-            S2 that = (S2)o;
-            return true;
-        }
-        
-        @Override public int hashCode() {
-            return java.util.Arrays.hashCode(new Object[] {});
-        }
-        
-        @Override public String toString() {
-            return String();
-        }
-    }
-    
-    public interface I {
-        public void M();
-    }
-    
     private static final class proxyI extends Seq.Proxy implements I {
         proxyI(Seq.Ref ref) { super(ref); }
     
diff --git a/bind/testdata/try.java.golden b/bind/testdata/try.java.golden
index 2b0514f..940e67c 100644
--- a/bind/testdata/try.java.golden
+++ b/bind/testdata/try.java.golden
@@ -20,5 +20,6 @@
     private static native void init();
     
     
+    
     public static native String This();
 }
diff --git a/bind/testdata/vars.java.c.golden b/bind/testdata/vars.java.c.golden
index 539f4e0..8bbdce1 100644
--- a/bind/testdata/vars.java.c.golden
+++ b/bind/testdata/vars.java.c.golden
@@ -17,13 +17,13 @@
 JNIEXPORT void JNICALL
 Java_go_vars_Vars_init(JNIEnv *env, jclass _unused) {
     jclass clazz;
-    clazz = (*env)->FindClass(env, "go/vars/Vars$S");
+    clazz = (*env)->FindClass(env, "go/vars/S");
     proxy_class_vars_S = (*env)->NewGlobalRef(env, clazz);
     proxy_class_vars_S_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
     clazz = (*env)->FindClass(env, "go/vars/Vars$proxyI");
     proxy_class_vars_I = (*env)->NewGlobalRef(env, clazz);
     proxy_class_vars_I_cons = (*env)->GetMethodID(env, clazz, "<init>", "(Lgo/Seq$Ref;)V");
-    clazz = (*env)->FindClass(env, "go/vars/Vars$I");
+    clazz = (*env)->FindClass(env, "go/vars/I");
     
 }
 
diff --git a/bind/testdata/vars.java.golden b/bind/testdata/vars.java.golden
index 2f0bee7..a7af2db 100644
--- a/bind/testdata/vars.java.golden
+++ b/bind/testdata/vars.java.golden
@@ -1,3 +1,45 @@
+// Java class go.vars.S is a proxy for talking to a Go program.
+//   gobind -lang=java vars
+//
+// File is generated by gobind. Do not edit.
+package go.vars;
+
+import go.Seq;
+
+public final class S extends Seq.Proxy implements I {
+    private S(go.Seq.Ref ref) { super(ref); }
+    
+    @Override public boolean equals(Object o) {
+        if (o == null || !(o instanceof S)) {
+            return false;
+        }
+        S that = (S)o;
+        return true;
+    }
+    
+    @Override public int hashCode() {
+        return java.util.Arrays.hashCode(new Object[] {});
+    }
+    
+    @Override public String toString() {
+        StringBuilder b = new StringBuilder();
+        b.append("S").append("{");
+        return b.append("}").toString();
+    }
+}
+
+// Java class go.vars.I is a proxy for talking to a Go program.
+//   gobind -lang=java vars
+//
+// File is generated by gobind. Do not edit.
+package go.vars;
+
+import go.Seq;
+
+public interface I {
+    
+}
+
 // Java class go.vars.Vars is a proxy for talking to a Go program.
 //   gobind -lang=java vars
 //
@@ -19,31 +61,6 @@
     
     private static native void init();
     
-    public static final class S extends Seq.Proxy implements I {
-        private S(go.Seq.Ref ref) { super(ref); }
-        
-        @Override public boolean equals(Object o) {
-            if (o == null || !(o instanceof S)) {
-                return false;
-            }
-            S that = (S)o;
-            return true;
-        }
-        
-        @Override public int hashCode() {
-            return java.util.Arrays.hashCode(new Object[] {});
-        }
-        
-        @Override public String toString() {
-            StringBuilder b = new StringBuilder();
-            b.append("S").append("{");
-            return b.append("}").toString();
-        }
-    }
-    
-    public interface I {
-    }
-    
     private static final class proxyI extends Seq.Proxy implements I {
         proxyI(Seq.Ref ref) { super(ref); }
     
diff --git a/cmd/gobind/gen.go b/cmd/gobind/gen.go
index 5dd1d05..f60b7de 100644
--- a/cmd/gobind/gen.go
+++ b/cmd/gobind/gen.go
@@ -5,6 +5,7 @@
 package main
 
 import (
+	"bytes"
 	"go/token"
 	"go/types"
 	"io"
@@ -25,19 +26,41 @@
 	}
 	switch *lang {
 	case "java":
+		var buf bytes.Buffer
+		g := &bind.JavaGen{
+			JavaPkg: *javaPkg,
+			Generator: &bind.Generator{
+				Printer: &bind.Printer{Buf: &buf, IndentEach: []byte("    ")},
+				Fset:    conf.Fset,
+				AllPkg:  conf.AllPkg,
+				Pkg:     conf.Pkg,
+			},
+		}
+		g.Init()
+
+		buf.Reset()
 		w, closer := writer(fname)
-		conf.Writer = w
-		processErr(bind.GenJava(conf, *javaPkg, bind.Java))
+		processErr(g.GenJava())
+		io.Copy(w, &buf)
 		closer()
+		for i, name := range g.ClassNames() {
+			buf.Reset()
+			w, closer := writer(name + ".java")
+			processErr(g.GenClass(i))
+			io.Copy(w, &buf)
+			closer()
+		}
+		buf.Reset()
 		cname := "java_" + p.Name() + ".c"
 		w, closer = writer(cname)
-		conf.Writer = w
-		processErr(bind.GenJava(conf, *javaPkg, bind.JavaC))
+		processErr(g.GenC())
+		io.Copy(w, &buf)
 		closer()
+		buf.Reset()
 		hname := p.Name() + ".h"
 		w, closer = writer(hname)
-		conf.Writer = w
-		processErr(bind.GenJava(conf, *javaPkg, bind.JavaH))
+		processErr(g.GenH())
+		io.Copy(w, &buf)
 		closer()
 	case "go":
 		w, closer := writer(fname)
diff --git a/cmd/gobind/main.go b/cmd/gobind/main.go
index 5e4b953..c0d2f9b 100644
--- a/cmd/gobind/main.go
+++ b/cmd/gobind/main.go
@@ -18,7 +18,7 @@
 var (
 	lang    = flag.String("lang", "java", "target language for bindings, either java, go, or objc (experimental).")
 	outdir  = flag.String("outdir", "", "result will be written to the directory instead of stdout.")
-	javaPkg = flag.String("javapkg", "", "custom Java package path used instead of the default 'go.<go package name>'. Valid only with -lang=java.")
+	javaPkg = flag.String("javapkg", "", "custom Java package path prefix used instead of the default 'go'. Valid only with -lang=java.")
 	prefix  = flag.String("prefix", "", "custom Objective-C name prefix used instead of the default 'Go'. Valid only with -lang=objc.")
 )
 
diff --git a/cmd/gomobile/bind.go b/cmd/gomobile/bind.go
index 542da96..18de5d0 100644
--- a/cmd/gomobile/bind.go
+++ b/cmd/gomobile/bind.go
@@ -5,6 +5,7 @@
 package main
 
 import (
+	"bytes"
 	"fmt"
 	"go/ast"
 	"go/build"
@@ -139,7 +140,7 @@
 func init() {
 	// bind command specific commands.
 	cmdBind.flag.StringVar(&bindJavaPkg, "javapkg", "",
-		"specifies custom Java package path used instead of the default 'go.<go package name>'. Valid only with -target=android.")
+		"specifies custom Java package path prefix used instead of the default 'go'. Valid only with -target=android.")
 	cmdBind.flag.StringVar(&bindPrefix, "prefix", "",
 		"custom Objective-C name prefix used instead of the default 'Go'. Valid only with -lang=ios.")
 }
@@ -273,11 +274,18 @@
 		bindOption += " -javapkg=" + javaPkg
 	}
 
-	conf := &bind.GeneratorConfig{
-		Fset:   b.fset,
-		Pkg:    pkg,
-		AllPkg: allPkg,
+	var buf bytes.Buffer
+	g := &bind.JavaGen{
+		JavaPkg: javaPkg,
+		Generator: &bind.Generator{
+			Printer: &bind.Printer{Buf: &buf, IndentEach: []byte("    ")},
+			Fset:    b.fset,
+			AllPkg:  allPkg,
+			Pkg:     pkg,
+		},
 	}
+	g.Init()
+
 	generate := func(w io.Writer) error {
 		if buildX {
 			printcmd("gobind %s -outdir=%s %s", bindOption, javadir, pkgPath)
@@ -285,18 +293,43 @@
 		if buildN {
 			return nil
 		}
-		conf.Writer = w
-		return bind.GenJava(conf, javaPkg, bind.Java)
+		buf.Reset()
+		if err := g.GenJava(); err != nil {
+			return err
+		}
+		_, err := io.Copy(w, &buf)
+		return err
 	}
 	if err := writeFile(javaFile, generate); err != nil {
 		return err
 	}
+	for i, name := range g.ClassNames() {
+		generate := func(w io.Writer) error {
+			if buildN {
+				return nil
+			}
+			buf.Reset()
+			if err := g.GenClass(i); err != nil {
+				return err
+			}
+			_, err := io.Copy(w, &buf)
+			return err
+		}
+		classFile := filepath.Join(javadir, name+".java")
+		if err := writeFile(classFile, generate); err != nil {
+			return err
+		}
+	}
 	generate = func(w io.Writer) error {
 		if buildN {
 			return nil
 		}
-		conf.Writer = w
-		return bind.GenJava(conf, bindJavaPkg, bind.JavaC)
+		buf.Reset()
+		if err := g.GenC(); err != nil {
+			return err
+		}
+		_, err := io.Copy(w, &buf)
+		return err
 	}
 	if err := writeFile(cFile, generate); err != nil {
 		return err
@@ -305,8 +338,12 @@
 		if buildN {
 			return nil
 		}
-		conf.Writer = w
-		return bind.GenJava(conf, bindJavaPkg, bind.JavaH)
+		buf.Reset()
+		if err := g.GenH(); err != nil {
+			return err
+		}
+		_, err := io.Copy(w, &buf)
+		return err
 	}
 	return writeFile(hFile, generate)
 }
diff --git a/cmd/gomobile/bind_test.go b/cmd/gomobile/bind_test.go
index 6f2da43..9430537 100644
--- a/cmd/gomobile/bind_test.go
+++ b/cmd/gomobile/bind_test.go
@@ -117,6 +117,7 @@
 mkdir -p $WORK/gomobile_bind
 mkdir -p $WORK/android/src/main/java/go
 gobind -lang=java -outdir=$WORK/android/src/main/java/go 
+mkdir -p $WORK/android/src/main/java/go
 mkdir -p $WORK/gomobile_bind
 mkdir -p $WORK/gomobile_bind
 cp $GOPATH/src/golang.org/x/mobile/bind/java/seq_android.go.support $WORK/gomobile_bind/seq_android.go
diff --git a/example/ivy/android/app/src/main/java/org/golang/ivy/Help.java b/example/ivy/android/app/src/main/java/org/golang/ivy/Help.java
index 256930b..ccdec2e 100644
--- a/example/ivy/android/app/src/main/java/org/golang/ivy/Help.java
+++ b/example/ivy/android/app/src/main/java/org/golang/ivy/Help.java
@@ -15,6 +15,8 @@
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
 
+import org.golang.mobile.Mobile;
+
 /*
  * Displays the help message for Ivy.
  */
diff --git a/example/ivy/android/app/src/main/java/org/golang/ivy/MainActivity.java b/example/ivy/android/app/src/main/java/org/golang/ivy/MainActivity.java
index 7142f52..af26876 100644
--- a/example/ivy/android/app/src/main/java/org/golang/ivy/MainActivity.java
+++ b/example/ivy/android/app/src/main/java/org/golang/ivy/MainActivity.java
@@ -32,6 +32,8 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 
+import org.golang.mobile.Mobile;
+
 /*
  * Main activity that consists of an edit view to accept the expression
  * and a web view to display output of the expression.
diff --git a/example/ivy/android/ivy/build.gradle b/example/ivy/android/ivy/build.gradle
index bf0a5ac..ededcd2 100644
--- a/example/ivy/android/ivy/build.gradle
+++ b/example/ivy/android/ivy/build.gradle
@@ -10,7 +10,7 @@
     pkg = "robpike.io/ivy/mobile"
 
     // -javapkg flag puts the generated java class into the specified package.
-    GOMOBILEFLAGS="-javapkg=org.golang.ivy"
+    GOMOBILEFLAGS="-javapkg=org.golang"
 
     /* Set the following variable(s):