我想写一个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));
}
}
答案 0 :(得分:3)
你需要复制你的代码或制作调用常见效用函数的包装器,它基本上都是疯狂的。
没有优雅的方法来做“功能翻译功能”。优雅的方法是以不同的方式编写程序。
Go计划从根本上必须采用不同的结构。从多年编写面向对象的代码开始,这是一个很难实现的习惯,但是当我找到解决问题的“Go-like”方法时,它通常会随着时间的推移变得更加简单。
有适当的继承和所有看起来可以保存代码并保持整洁的东西,但是 - 至少在我的经验中 - 经过多年的开发它很容易就像乱七八糟的混乱,或者至少很难深入研究暂时没有使用代码。
Go的界面更加有限,但它会迫使你让事情变得更简单,更“明显”。不同类之间的分离是明确的。
还有一些优点;在获得一些经验之后,“混合”类型比继承更容易。另一个“技巧”是类型可以满足多个接口,例如。
如果您一直在编写面向对象的代码,那么需要一些习惯来适应新工具。我建议不要写一个翻译,只是尝试编写一些基于Go的工具。那一定会很好玩。 : - )
答案 1 :(得分:0)
这就是我想出来的。
从start.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
}
package main
import (
"so-java2go/javaStructs/Test"
"os"
)
func main(){
Test.Main(os.Args)
}
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线程。