Go语言的学习路线图
图片来自Github


我将会从翻写一个简单的java项目开始
https://github.com/jane-cloud/Open-API-SDK-V5/tree/main/okex-java-sdk-api-v5
这是Okex交易所的SDK,我之前使用过java版的,刚好没有golang版,我可以翻写一下。
这篇文章先聊一下两个语言的特性差异


特性差异


1、Go语言函数多返回值的特点
目前主流语言除Python外基本都不支持函数的多返回值功能。需求还是有的,
比如:得到一个由蜡烛图组成的行情走势 List<Candle>, size等于300条,需要求50日均线(MA50)、100日均线(MA100)、150日均线(MA150)、300日均线(MA300)。
程序的主体是一个300次的for循环,一轮循环结束,产生有4个BigDecimal值,java只能通过Map的方式返回。也不是不能解决,就是不太优雅。

public static Map<Integer, BigDecimal> MA(JSONArray array, int scale){

        BigDecimal total = new BigDecimal(0);
        BigDecimal total150 = new BigDecimal(0);
        BigDecimal total100 = new BigDecimal(0);
        BigDecimal total50 = new BigDecimal(0);

        for(int i=array.size()-1; i>=0; i--) {
            JSONArray candle = array.getJSONArray(i);
            BigDecimal close = candle.getBigDecimal(CandleEnum.close.v());
            total = total.add(close);

            if(i<150){
                total150 = total150.add(close);
            }

            if(i<100){
                total100 = total100.add(close);
            }

            if(i<50){
                total50 = total50.add(close);
            }
        }

        Map<Integer, BigDecimal> map = new HashMap<>();
        map.put(300, total.divide(new BigDecimal(300), scale, BigDecimal.ROUND_HALF_UP));
        map.put(150, total150.divide(new BigDecimal(150), scale, BigDecimal.ROUND_HALF_UP));
        map.put(100, total150.divide(new BigDecimal(100), scale, BigDecimal.ROUND_HALF_UP));
        map.put(50, total150.divide(new BigDecimal(50), scale, BigDecimal.ROUND_HALF_UP));
        return map;
    }


2、也说一个Go语言不如java方便的地方,众所周知java的spring全家桶的强大,我们几乎已经是无spring不java。所以看完这个你可以不用去搜索:go有没有类似spring的框架啊?go有没有annotation的注解机制啊?不用问,问就是没有。有也不成熟,达不到你心中spring在java的高度。这时候你只能反问自己,没有spring我就不会写java了吗?如果答案不太肯定,那go就先别转了。

3、错误处理
Go语言引入了defer关键字,并提供内置函数panic、recover完成异常的抛出和捕获。让开发者无需仅仅为了程序安全性而添加大量一层套一层的try-catch语句。

4、匿名函数和闭包

f := func(x, y int) int {
    return x + y
}


5、Go语言不支持继承和重载
Java和C++在接口重载的特性上给我留下了阴影,在很多情况下代码会被反复修改,当修改了implements对象时interface就要一起修改,每次我都在想到底这样设计是好处多,还是增加了维护的麻烦。


public interface GuardianApi {

    @GET("/api/band")
    Call<JSONObject> getBand();

    @GET("/api/instPoint")
    Call<JSONObject> getInstPoint(@Query("instId") String instId);

    @GET("/api/worker")
    Call<JSONObject> getWorker();
}


到底要不要让每个service都去implements一个interface,一直都是我对java最大的怀疑

public class GuardianServiceImpl implements GuardianService {

    ....

    @Override
    public Set<String> band() {
        //...
    }

    @Override
    public InstPoint getInstPoint(String instId) {
        //...
    }

    @Override
    public Account getWorker() {
        //...
    }
}


Go语言的接口体系避免了这类问题

type Bird struct {
    
} 

fuc (b *Bird) Fly(){
    //...
}

type IFly interface {
    Fly()
}

func main(){
    var fly IFly = new{Bird}
    fly.Fly()
}

6、Go的并发特性
Go语言实现了CSP(通信顺序进程,Communication Sequential Process)模型来作为goroutine间的默认通信方式。在CSP模型中,一个并发系统由若干并行运行的顺序进程组成,每个进程不能对其它进程的变量赋值。进程间只能通过一对通信原语实现协作。Go语言用channel这个概念来轻松的实现了CSP模型。channel的使用方式比较接近Unix系统中的管道(pipe)概念,可以方便地进行通信。而在Java中则需要通过Netty框架来实现类似功能。

package test

import (
	"fmt"
	"testing"
)

func sum(values[] int, resultChan chan int)  {
	sum := 0
	for _, value := range values {
		sum += value
	}

	resultChan <- sum
}

func Test(t *testing.T) {

	values := [] int{1,2,3,4,5,6,7,8,9,10}
	resultChan := make(chan int, 2)
	go sum(values[:len(values)/2], resultChan)
	go sum(values[len(values)/2:], resultChan)
	sum1, sum2 := <-resultChan, <-resultChan
	fmt.Println("Result:", sum1, sum2, sum1 + sum2)
}

7、反射


package reflection

import (
	"fmt"
	"reflect"
	"testing"
)

type Bird struct {
	Name string
	LifeExpectance int
}

func (b *Bird) Fly()  {
	fmt.Println("I am flying...")
}

func Test(t *testing.T) {
	sparrow := &Bird{"Sparrow", 3}
	s := reflect.ValueOf(sparrow).Elem()
	typeOfT := s.Type()
	for i :=0; i <s.NumField(); i++ {
		f := s.Field(i)
		fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface())
	}
}
  1. Cgo

在Go语言中,可以按照Cgo的特定语法混合编写C语言代码





若有收获,就点个赞吧