如何从破解Java代码生成Go代码?

时间:2013-12-06 01:38:31

标签: java go polymorphism

我想写一个Java2Go生成器,但我发现很难表达多态性(例如:形式arg是Base类,但是真正的arg是Sub类),我如何在Go中表达打击代码?

class Base{
    public int i;     
  }

class Sub extends Base{ 
  }

class Test{
   public static int test(Base base){
          base.i = 99;
          return base.i;
   }
   public static void main(String [] args){
       Sub sub = new Sub();
       System.out.println(test(sub));
   }
}

3 个答案:

答案 0 :(得分:3)

你需要复制你的代码或制作调用常见效用函数的包装器,它基本上都是疯狂的。

没有优雅的方法来做“功能翻译功能”。优雅的方法是以不同的方式编写程序。

Go计划从根本上必须采用不同的结构。从多年编写面向对象的代码开始,这是一个很难实现的习惯,但是当我找到解决问题的“Go-like”方法时,它通常会随着时间的推移变得更加简单。

有适当的继承和所有看起来可以保存代码并保持整洁的东西,但是 - 至少在我的经验中 - 经过多年的开发它很容易就像乱七八糟的混乱,或者至少很难深入研究暂时没有使用代码。

Go的界面更加有限,但它会迫使你让事情变得更简单,更“明显”。不同类之间的分离是明确的。

还有一些优点;在获得一些经验之后,“混合”类型比继承更容易。另一个“技巧”是类型可以满足多个接口,例如。

如果您一直在编写面向对象的代码,那么需要一些习惯来适应新工具。我建议不要写一个翻译,只是尝试编写一些基于Go的工具。那一定会很好玩。 : - )

答案 1 :(得分:0)

这就是我想出来的。

  • 扩展类时,将父类添加为嵌入式结构,并将隐藏方法附加到子类/结构以转换回父类/结构。
  • 对于接受对象的函数,让它们接受指向结构的指针。

从start.go运行

文件:Test.go

package Test
import (
  "fmt"
)
type Base struct{
    I int
}
type Sub struct {
   Base
}
func (s *Sub) _toBase() *Base{
  return &s.Base
}
func Test( base *Base) int{
      base.I = 99;
      return base.I;
}
func Main( args []string) error{
   sub := Sub{}
   fmt.Println(Test(sub._toBase()));
   return nil
}

文件:start.go

package main
import (
    "so-java2go/javaStructs/Test"
    "os"
)
func main(){
    Test.Main(os.Args)
}

文件:Test.java

class Base{
    public int i;     
  }

class Sub extends Base{ 
  }

class Test{
   public static int test(Base base){
          base.i = 99;
          return base.i;
   }
   public static void main(String [] args){
       Sub sub = new Sub();
       System.out.println(test(sub));
   }
}

答案 2 :(得分:0)

如果您的班级中只有成员变量,那么您可以使用嵌入。但是,此解决方案不会扩展到您还需要类的方法的情况,这些方法可以在子类中重写,因为名称冲突将阻止您的Go代码编译。

您可以使用vtable将类编译为原始内存结构(就像您编译为汇编程序或C一样),但是您必须实现自己的垃圾收集器。假设这不是你想要的,你可以扩展vtable的想法,包括返回成员变量地址的方法,这将允许你使用Go接口作为实现vtable的廉价方式。这是一些代码,略微压缩以减少样板的外观。

package main

import "fmt"

type Base struct {
    i int
}
func (b *Base) get_i() *int { return &b.i }
func NewBase() *Base { return &Base{} }

type Sub struct {
    parent Base
}
func NewSub() *Sub { return &Sub{*NewBase()} }
func (s *Sub) get_i() *int { return s.parent.get_i() }

type BaseI interface {
    get_i() *int
}

func test(b BaseI) int {
    *b.get_i() = 99
    return *b.get_i()
}

func main() {
    s := NewSub()
    fmt.Println(test(s))
}

由于Java允许重载,因此需要对方法进行名称修改。您会发现确切地确定在给定的调用站点需要调用哪个方法很有趣,具体取决于对象的类型和所有方法参数的类型:)

事实上,很多事情最终都需要被命名。例如,上面的代码直接翻译了类名“Base”和“Sub”,但如果我将其中一个类称为“main”,或者我称之为“Sub”,“NewBase”(其中没有',那该怎么办? t出现在原始Java源代码中,但在翻译过程中出现了)?通常,翻译后的代码看起来更像是为了避免这些问题:

type Java_class_Base struct {
    Java_member_i Java_basetype_int
}

还有很多其他障碍。例如,上面的代码将Java int转换为Go int,但两者并不相同。 Go int32更接近,但行为仍然不同(例如,Java指定溢出时会发生什么,但Go不会)。这意味着即使像x = x + 1这样的简单表达式也难以翻译,因为您将不得不编写自己的add函数以确保转换后的代码与Java代码的行为相同。另一个例子是每个对象都可能充当线程可重入锁(因为它可以是synchronised)。这意味着你将不得不决定如何翻译它,这将涉及有一个Java线程的概念,并能够找出你的翻译调用正在执行的Java线程。

祝你好运!未来有很多困难,但制作编译器是一个有趣的挑战。