Golang似乎已成为一种流行的流行语言,因为它是Docker , Kubernetes和OpenShift等一些很酷的新技术的首选编程语言。 幸运的是,它们也都是开源的,这意味着我们都可以为这些社区做出贡献并参与其中。 开源的一项特别了不起的好处被证明是极有价值的,并且在许多情况下都非常有用: 开源 。 我无法告诉您我必须多久跳入一个项目的源代码,无论使用哪种编程语言来实现它,才能真正了解它在做什么甚至更重要:为什么它没有做文档说明的事情(或没有文档时)。 读取代码的频率远远高于编写代码的频率,因此有助于理解如何读取代码。
我这一天已经学到了很多编程语言(C,C ++,C#,Java,Python,Scala,Groovy,Assembly,JavaScript等),现在就开始使用。 我花了几周的时间学习阅读Golang并从Kubernetes,Fabric8和OpenShift Origin代码库中读取源文件,以便能够更深入地了解它们,并且我认为这对熟悉另一种编程语言的开发人员会有所帮助快速将某些概念(例如Java)映射到Golang,其简单目的是摆脱对加入社区的恐惧,或者更自私地阅读和理解Golang编写的一些新技术实际有效。
以下是一些最初让我脱颖而出的关键作品。 随时向Tweet(@christianposta)发消息给我,您可以在此添加其他人,或发表评论。 本文当然不是Go的介绍,也不是完整的Java-> Go映射。 本文的目的是快速帮助Java开发人员了解如何映射到Java方面的一些常见Go习惯用法。
项目结构
看来,Golang项目的通用约定是将所有cli二进制源文件或“主”包中的文件放置在源树根目录下的cmd
文件夹中。 通常,可以在源目录根目录的pkg
文件夹中找到实现许多功能并表示组织类型,常量,变量和函数的内pkg
软件包。
配套
与Java类似,Golang将其代码组织到软件包中。 您可以通过在源文件顶部键入package <package_name>
来声明源文件所在的包(及其所有常量,类型,函数等)。 但是,与Java不同,您不输入完整路径(目录位置),仅输入名称。 例:
package api
因此,如果您具有“ api / endpoints”软件包,则文件系统上将具有该目录结构(例如./pkg/api/endpoints),但是endpoints
软件包将在endpoints
目录下的源文件中声明,但它们看起来像这样:
package endpoints
导入包
我们可以导入包,就像我们在Java中使用以下操作一样:
import (
stderrs "errors"
"time"
"golang.org/x/net/context"
"k8s.io/kubernetes/pkg/auth/user"
)
我们可以根据包路径中的最后一个包名称在源代码中使用包。 例如,在上面的示例中,我们导入k8s.io/kubernetes/pkg/auth/user
然后从代码中使用user.Foo()
引用该包中的元素。 如果我们想在源文件中重命名包,以免与其他名称冲突,则可以执行上面的操作:
import (
stderrs "errors"
)
并在我们的源代码中将errors
包称为stderrs.Foo()
主包装
main
软件包是golang查找应用程序入口点的地方。 main
包必须具有一个main()
函数,该函数不带任何参数并且不提供任何返回值:
func main() { … }
如前所述,通常在根目录的cmd
文件夹中找到此软件包。
类型,常量,函数的范围/可见性
在golang中,为了提供对struct / type / function / variable的可见性范围(要暴露在包的外部),符号中的第一个字符是有效的。 例如,在包foo
,如果您有一个名为func Bar()
的函数,则由于“ Bar”的首字母大写,因此可以在包外部使用。 如果导入foo
包,则可以访问foo.Bar()
。 如果“ bar”为小写字母,则将其隐藏-也就是说,首字母的大小写决定可见性
方法可以返回多个值
golang中的函数或方法(有区别)可以返回“元组”或多值返回。 例如,调用返回多个值的函数如下所示:
internalCtx, ok := foo.bar(context.Context)
类,结构,方法
在Java中,我们有类,但是在Go中,我们有结构。 结构上可以有方法,所以它们有点像类。
例如:
type Rectangle struct {
width int
height int
}
这是一个名为“ Rectangle”的数据结构,具有两个字段: width
和height
。 我们可以像这样创建一个新的Rectangle
:
r := new(Rectangle)
我们可以像这样引用其字段:
r.width = 10
r.height = 5
我们可以在对结构的字段进行操作的Struct上编写方法,如下所示:
func (r *Rectangle) area() int {
return r.width * r.height
}
因此,如果您在golang代码中看到这样的成语,请考虑“ Java类”
类型继承
Golang有目的地没有Java那样的“扩展”类型。 继承是通过组合完成的(有点像我们无论如何都应作为Java的“最佳实践” :))。但是,就程序员对Java的可见性而言,它确实很像Java中的“ extends”关键字。 例如:
type Rectangle struct {
Shape
width int
height int
}
在此示例中,Rectangle具有类型为Shape
的匿名字段。 无论Shape
有什么字段和方法,在Rectangle
对象上都是可见的。
但是要注意的一件事是,因为Rectangle
是“ is-a” Shape
,所以这与Java中的不一样,我们可以将Rectangle
传递Rectangle
Rectangle
作为参数的函数。 这将在Go中失败。 要获得这种“类型”多态性,您应该使用Go接口(下一部分)
多态,接口和鸭子类型
在Java中,我们有特定的接口类型,它们根据必须由声称是该类型的类所实现的方法来指定行为。 在Go中,我们确实具有完成相同功能的接口,但是类实际上并未声明要具体实现它们:它们仅实现方法,然后适合该接口。
例如,此接口声明具有Print()方法的Shape类型:
type Shape interface {
Print()
}
但是,在创建Structs时,我们不需要像Java中那样使用“实现”来声明所有这些:我们只需实现方法,就可以将它们传递给函数并视为该类型:
type Rectangle struct {
width int
height int
}
func (r *Rectangle) Print() {
fmt.println("Rectangle!");
}
在这种情况下,Rectangle对象可以传递给需要Shape
类型的函数,因为它实现了该类型的所有方法。
对于循环
使用go的for循环示例:
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
但是,当迭代数组(或类似数组的东西,例如字符串,映射,切片等)时,可以使用如下范围运算符(假设foo
是一个列表):
for v := range foo {
fmt.println("value="+v);
}
如果您在遍历列表时需要知道列表的索引,则如下所示:
for i, v := range foo {
fmt.println("index " + i + "has value="+v);
}
While循环
Go没有while循环,do-while或foreach等。您只需再次使用for循环,如下所示:
sum := 1
for sum < 1000 {
sum += sum
}
fmt.Println(sum)
或者可以做一个无限的while循环:
for {
something...
}
指针和参考
Golang显式使用指针和引用,而Java大多将其隐藏。 例如,在Java中,我们可以
Shape shape = new Shape()
然后是shape.foo()
但是在Go中,我们必须直接处理指针:
type Rectangle struct {
width int
height int
}
func updateRectangle(r *Rectangle){
r.width = 5;
r.height = 10;
}
func main() {
r := Rectangle{20,30}
updateRectangle(&r)
}
当主函数结束时,矩形r
将具有您期望的值width = 5和height = 10。 注意,我们必须对指针进行显式引用。
垃圾收集
Golang是一种垃圾收集语言; 无需自己显式释放内存(我知道您在看上面的指针和引用部分时正在精简什么:)。
如果您是Java开发人员,并且想添加更多内容,请随时告诉我(@christianposta)! 或者,如果您是Go-lang开发人员,而我在这里遗漏了一些信息,请纠正我! 希望这会有所帮助...
翻译自: https://www.javacodegeeks.com/2015/11/quick-go-lang-for-java-developers.html