V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
echoless
V2EX  ›  程序员

感觉 golang 比 Java 还难读, 难学

  •  
  •   echoless · 2023-02-23 13:20:57 +08:00 · 7750 次点击
    这是一个创建于 631 天前的主题,其中的信息可能已经有所发展或是发生改变。

    要解析的 json, 格式不完美. 比如

    [{
    "name": "Abyssin",
    "price": 500,
    "location": "Lviv",
    "image": "https://olxua-ring02.akamaized.net/images_slandocomua/476948786_2_1000x700_abissenysh-chempion-fotografii.jpg"
    },
    {
    "name": "Abyssin",
    "price": "550",
    "location": "Lviv",
    "image": "https://olxua-ring10.akamaized.net/images_slandocomua/342850976_3_1000x700_abidetki-koti_rev006.jpg"
    }]
    

    price 实际上是个 int 但是有些值是 string. 加 tag 就没啥用了, 只能自己写 UnmarshalJSON

    好奇看了一下

    json/decode

    // indirect walks down v allocating pointers as needed,
    // until it gets to a non-pointer.
    // If it encounters an Unmarshaler, indirect stops and returns that.
    // If decodingNull is true, indirect stops at the first settable pointer so it
    // can be set to nil.
    func indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) {
    	// Issue #24153 indicates that it is generally not a guaranteed property
    	// that you may round-trip a reflect.Value by calling Value.Addr().Elem()
    	// and expect the value to still be settable for values derived from
    	// unexported embedded struct fields.
    	//
    	// The logic below effectively does this when it first addresses the value
    	// (to satisfy possible pointer methods) and continues to dereference
    	// subsequent pointers as necessary.
    	//
    	// After the first round-trip, we set v back to the original value to
    	// preserve the original RW flags contained in reflect.Value.
    	v0 := v
    	haveAddr := false
    
    	// If v is a named type and is addressable,
    	// start with its address, so that if the type has pointer methods,
    	// we find them.
    	if v.Kind() != reflect.Pointer && v.Type().Name() != "" && v.CanAddr() {
    		haveAddr = true
    		v = v.Addr()
    	}
    	for {
    		// Load value from interface, but only if the result will be
    		// usefully addressable.
    		if v.Kind() == reflect.Interface && !v.IsNil() {
    			e := v.Elem()
    			if e.Kind() == reflect.Pointer && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Pointer) {
    				haveAddr = false
    				v = e
    				continue
    			}
    		}
    
    		if v.Kind() != reflect.Pointer {
    			break
    		}
    
    		if decodingNull && v.CanSet() {
    			break
    		}
    
    		// Prevent infinite loop if v is an interface pointing to its own address:
    		//     var v interface{}
    		//     v = &v
    		if v.Elem().Kind() == reflect.Interface && v.Elem().Elem() == v {
    			v = v.Elem()
    			break
    		}
    		if v.IsNil() {
    			v.Set(reflect.New(v.Type().Elem()))
    		}
    		if v.Type().NumMethod() > 0 && v.CanInterface() {
    			if u, ok := v.Interface().(Unmarshaler); ok {
    				return u, nil, reflect.Value{}
    			}
    			if !decodingNull {
    				if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
    					return nil, u, reflect.Value{}
    				}
    			}
    		}
    
    		if haveAddr {
    			v = v0 // restore original value after round-trip Value.Addr().Elem()
    			haveAddr = false
    		} else {
    			v = v.Elem()
    		}
    	}
    	return nil, nil, v
    }
    

    感觉 java 感觉清爽很多. java 感觉不需要学, 看看 ArrayList HashMap 这些接口就可以写了,语法也比较简单. golang 真是各种奇技淫巧满天飞.

    65 条回复    2023-02-24 14:27:43 +08:00
    Nazz
        1
    Nazz  
       2023-02-23 13:24:31 +08:00 via Android   ❤️ 2
    interface 了解下
    NeoZephyr
        2
    NeoZephyr  
       2023-02-23 13:25:20 +08:00   ❤️ 2
    说真的,看到这种满屏的 v, i, e 变量,就想吐🤮
    echoless
        3
    echoless  
    OP
       2023-02-23 13:27:56 +08:00   ❤️ 1
    @NeoZephyr 官方源代码, 可不是我写的. 我也是吐, 有些同事把 golang 这种风格弄到 python 里面, 看到我就...
    echoless
        4
    echoless  
    OP
       2023-02-23 13:33:45 +08:00
    另外求 golang 的资料, 中级水平的, 准备去卷 golang 了. 感觉 python 工作工资越来越不行了.
    SuperManNoPain
        5
    SuperManNoPain  
       2023-02-23 13:35:21 +08:00   ❤️ 1
    大道至简,滑稽.jpg
    liuxu
        6
    liuxu  
       2023-02-23 13:38:59 +08:00   ❤️ 22
    一般不是从 C 学起,没看过大型 C 项目的是对这种写法不适应

    特别是有极少部分 javaer 喜欢拿 java 的格式挑其他语言,不然《 linux 内核设计与实现》作者这种级别的大佬也不会专门在书的最后喷 javaer ,太惹人厌了

    WispZhan
        7
    WispZhan  
       2023-02-23 13:41:51 +08:00 via Android   ❤️ 2
    Java 是语法最简单、最啰嗦的编程语言
    echoless
        8
    echoless  
    OP
       2023-02-23 13:42:00 +08:00
    @liuxu 上段代码里面的 v, u 我倒是没有意见. 因为也不好找一个合适的命名. 但是有些地方, 比如输入参数是 car Car, 那么命名弄个 c 就是恶心人了.
    fo0o7hU2tr6v6TCe
        9
    fo0o7hU2tr6v6TCe  
       2023-02-23 13:43:23 +08:00   ❤️ 1
    type Model struct {
    Name string `json:"name"`
    Price interface{} `json:"price"`
    Location string `json:"location"`
    Image string `json:"image"`
    }

    func ParseModel() []Model {
    str := `[{
    "name": "Abyssin",
    "price": 500,
    "location": "Lviv",
    "image": "https://olxua-ring02.akamaized.net/images_slandocomua/476948786_2_1000x700_abissenysh-chempion-fotografii.jpg"
    },
    {
    "name": "Abyssin",
    "price": "550",
    "location": "Lviv",
    "image": "https://olxua-ring10.akamaized.net/images_slandocomua/342850976_3_1000x700_abidetki-koti_rev006.jpg"
    }]`
    var models []Model
    err := json.Unmarshal([]byte(str), &models)
    if err != nil {
    panic(err)
    }
    return models

    }
    func main() {
    items := ParseModel()
    fmt.Println(items)
    }
    echoless
        10
    echoless  
    OP
       2023-02-23 13:44:12 +08:00
    上面的问题主要是各种 reflect, 看晕了. 还有 reflect.Zero MakeSlice 这种, 可能是用的少吧, 看起来抽象
    echoless
        11
    echoless  
    OP
       2023-02-23 13:50:28 +08:00
    @hzjseasea 多谢, 这个解决了解析的问题, 但是我这个 price 还是要转换成 int64
    RICKEYGONG
        12
    RICKEYGONG  
       2023-02-23 13:56:01 +08:00
    @liuxu 赞同
    coderxy
        13
    coderxy  
       2023-02-23 13:59:27 +08:00   ❤️ 1
    go 如果你读不明白,java 你大概率也读不明白的。
    8355
        14
    8355  
       2023-02-23 14:09:01 +08:00
    喜欢 java 和 go 的本质上就是两种人
    写 java 循规蹈矩建一堆文件起长长的名字 xml 真的很刺激
    假如没有注解🙅 🙅 🙅
    fo0o7hU2tr6v6TCe
        15
    fo0o7hU2tr6v6TCe  
       2023-02-23 14:10:45 +08:00
    @wuhaoecho mapstructure 这个库里有个 example 可以看一下
    https://pkg.go.dev/github.com/mitchellh/mapstructure?utm_source=godoc
    Example (WeaklyTypedInput)

    我个人觉得, 如果你要改的话,reflect 是逃不过的,java 里面不是通过反射去修改的么 0.0 ,python 里面确实没有这个考虑
    echoless
        16
    echoless  
    OP
       2023-02-23 14:15:04 +08:00
    @hzjseasea
    ```
    items := ParseModel()
    for _, v := range items {
    switch v.Price.(type) {
    case float64:
    fmt.Printf("%+v\n", int(reflect.ValueOf(v.Price).Float()))。// 草草草
    case string:
    fmt.Println(v.Price)
    default:
    panic("unknown type")
    }

    // if reflect.TypeOf(v.Price) == reflect.Kind.String() {
    // fmt.Printf("%d\n", v.Price)
    //}
    }
    ```

    差不多这样, 吐槽归吐槽, 你的 interface 倒是提供了一个思路. 但是还不够优雅. 最好是我每个 attribute 能提供一个函数去解析. python 弱类型, 比较方便.
    lyz1990
        17
    lyz1990  
       2023-02-23 14:17:27 +08:00   ❤️ 3
    解析 json 还得看我大 PHP 和 JS 啊,哈哈
    tuxz
        18
    tuxz  
       2023-02-23 14:17:54 +08:00   ❤️ 1
    可以自定义实现 struct 的反序列化,参考这个 https://attilaolah.eu/2013/11/29/json-decoding-in-go
    zjsxwc
        19
    zjsxwc  
       2023-02-23 14:19:16 +08:00   ❤️ 1
    不会吧,golang 解析 json 时居然没有注解能够处理某一个 json 字段可能有多个类型,

    但 rust 可以通过注解这么弄:
    https://stackoverflow.com/questions/37870428/convert-two-types-into-a-single-type-with-serde#answer-37871403

    ```rust
    #[derive(Clone, Debug, Deserialize)]
    struct Points {
    #[serde(deserialize_with = "deserialize_u64_or_empty_string")]
    x: u64,
    #[serde(deserialize_with = "deserialize_u64_or_empty_string")]
    y: u64,
    name: String,
    }
    struct DeserializeU64OrEmptyStringVisitor;

    impl<'de> de::Visitor<'de> for DeserializeU64OrEmptyStringVisitor {
    //处理 包含字符串或 u64 的字段
    }
    ```
    MoYi123
        20
    MoYi123  
       2023-02-23 14:19:48 +08:00
    @NeoZephyr 麻烦解释一下 https://github.com/alibaba/fastjson/blob/436cae79bfb327f3641ac4c901e9411fc827b415/src/main/java/com/alibaba/fastjson/parser/JSONLexerBase.java#L41 这里的 ch,sp,bp,np 都是什么意思?

    解 json 的代码和业务代码是一回事吗?
    Macolor21
        21
    Macolor21  
       2023-02-23 14:21:30 +08:00   ❤️ 10
    @liuxu #6 后面的怎么不截了?

    1. 驼峰不允许,写、看 Linux 内核的人,大部分都是 C ,之前的字体可能会大小写不敏感,或者没有等宽字体吧,这是风格问题
    2. 局部变量尽量简便,如果 for loop 里面的 index 这种可以简化为 i. 因为它在这里不难理解.
    3. 匈牙利命令法:太啰嗦了,没几个人会用
    4. 全局变量、函数应该有小写的描述名,适当用下划线区分。一个全局函数叫 `atty()` 也被骂啊,人家要求写成 `get_active_tty()` 还骂这是 Linux 不是 BSD

    说白了,就是风格统一的问题,Linux 源码有自己的一套风格( c 语言用户群用的风格),拿其他语言的风格上去提交代码,不是找骂么。你在 Java 里写 get_active_tty() 我也觉得你是傻逼。

    第二的问题,Java 里 Foreach 一个集合,也是 collection.foreach(e-> e.printf()); 用 e 或者 ele 代替 elements.

    命名风格无法决定语言啰嗦不啰嗦,而是语法糖、特性( Pattern match 、类型推断、类型定义)决定,如果你认为命令风格是 Java 被诟病的原因,只能说你的水平太低,只能当个 reader 和传话太监。

    连命令风格都不知道,要么网管转运维的半吊子,要么销售培训转开发的半吊子。
    echoless
        22
    echoless  
    OP
       2023-02-23 14:25:14 +08:00 via Android
    @zjsxwc 我需要的就是这种
    echoless
        23
    echoless  
    OP
       2023-02-23 14:30:32 +08:00 via Android
    @MoYi123 他这个命名也不好 应该用单词
    a132811
        24
    a132811  
       2023-02-23 14:43:21 +08:00
    1. 这不是 golang 本身的问题。golang 自带的这个 json 库封装时就没有支持多类型自动转换。
    2. 语言风格、习惯其实没有什么好喷的,有人就是喜欢这种简化,有人就是喜欢冗长风格,争论不会任何结果。
    3. 不过 golang 有一点好就是源码修改和调试非常的方便。有精力的话,可以给官方提一个 PR
    erichen86
        25
    erichen86  
       2023-02-23 14:44:25 +08:00
    python 为什么这么流行,代码看着也挺难受的,感觉还不如 go 看的舒服
    yaott2020
        26
    yaott2020  
       2023-02-23 14:44:27 +08:00 via Android
    @zjsxwc 你可以自己在 github 提个 issue ,或者自定义解析函数,字段多类型可以用 any(interface{})
    rrfeng
        27
    rrfeng  
       2023-02-23 14:45:30 +08:00   ❤️ 1
    type IntStr int64

    type Model struct {
    Price IntStr
    }

    func (IntStr) Unmarshal(){
    ...
    }
    wangsongyan
        28
    wangsongyan  
       2023-02-23 14:50:13 +08:00
    json.Number
    dobelee
        29
    dobelee  
       2023-02-23 15:03:53 +08:00
    value -> v
    element -> e

    没看出有任何问题。
    xiaocaiji111
        30
    xiaocaiji111  
       2023-02-23 15:04:03 +08:00   ❤️ 2
    底层源码是不好看的,不看底层学起来是蛮简单。短变量命名,真的是,有时候源码过长,前面读了后面就忘了啥意思(局部变量用一下的能短则短),还是 java 那样啰嗦点儿好。毕竟是给人看的,看懂最重要。
    liuxu
        31
    liuxu  
       2023-02-23 15:04:43 +08:00   ❤️ 1
    @wuhaoecho 参数就算是 c 也肯定是也要写清楚的,但也不会写成 myFirstCar, mySecondCar 这种,不过 rust 和 golang 这种可以用闭包的,参数用 k,v 简写也正常,闭包本身建议代码不要太复杂

    楼上还有一个急的跳脚开始疯狂瞎咬人的我就不 at 了,免得把你这帖子搞的水深火热了,我就是在说 go,c,java 各有各的风格,他连基本的阅读理解能力都没有,就有一些人非要拿 java 的风格往 linux 内核里面搞,截图后面的是 linux 向 bsd 开炮,话题就撤太远了

    醉舞经楼半卷书,坐井谈天阔
    fgwmlhdkkkw
        32
    fgwmlhdkkkw  
       2023-02-23 15:13:28 +08:00
    你自定义一个 Price 类型,不久好了吗?
    现实世界就是这么复杂,你换什么语言都要面对的啊?
    lxiian
        33
    lxiian  
       2023-02-23 15:36:25 +08:00 via iPhone
    @erichen86 一样 我反正是讨厌 python
    echoless
        34
    echoless  
    OP
       2023-02-23 15:57:42 +08:00
    @rrfeng
    @fgwmlhdkkkw

    ```
    type IntStr struct {
    value int
    }

    type Model struct {
    Name string `json:"name"`
    Price IntStr `json:"price"`
    Location string `json:"location"`
    Image string `json:"image"`
    }

    func (v *IntStr) UnmarshalJSON(b []byte) (err error) {
    s, n := "", float64(0)
    if err = json.Unmarshal(b, &s); err == nil {
    intVar, _ := strconv.Atoi(s)
    v.value = intVar
    return
    }
    if err = json.Unmarshal(b, &n); err == nil {
    v.value = int(n)
    }
    return
    }
    ````

    搞定, 还行了. v 友牛皮
    realpg
        35
    realpg  
       2023-02-23 16:08:26 +08:00
    我就搞不懂为啥还要规定变量命名风格。。。
    无论啥语言
    a132811
        36
    a132811  
       2023-02-23 16:24:33 +08:00   ❤️ 3
    @wuhaoecho
    没必要用结构体
    ```
    type StrInt int
    func (v *StrInt) UnmarshalJSON(b []byte) (err error) {
    s, n := "", float64(0)
    if err = json.Unmarshal(b, &s); err == nil {
    intVar, _ := strconv.Atoi(s)
    *v = StrInt(intVar)
    return
    }
    if err = json.Unmarshal(b, &n); err == nil {
    *v = StrInt(n)
    }
    return
    }
    ```
    echoless
        37
    echoless  
    OP
       2023-02-23 16:27:35 +08:00
    @a132811

    我本来想这个的, golang 水平不够,没搞通.

    实际应用来讲 我觉得多数时候没区别 可能有一些性能差异.
    falcon05
        38
    falcon05  
       2023-02-23 16:29:11 +08:00
    从动态语言过来的看到这 json 处理感觉有点呆
    virusdefender
        39
    virusdefender  
       2023-02-23 16:32:33 +08:00
    json.Number 就是来干这个的
    vcbal
        40
    vcbal  
       2023-02-23 16:39:44 +08:00
    有没有想过这是你自己的原因?
    echoless
        41
    echoless  
    OP
       2023-02-23 16:42:30 +08:00
    我把完整的代码列出来

    https://go.dev/play/p/8sFRipQ1hAn

    就处理一个 json

    要用到懂

    1. interface (golang 默认的, 否则不了解 UnmarshalJSON 咋回事, 这个看文档是最简单的途径, 目前我还没有入门怎么看 golang 文档最快, 容易晕)
    2. pointer 这玩意有时候容易晕, 比 c 容易, 但是跟 java, python 比是复杂了.
    3. type 声明 或者 struct

    ```
    type StrInt int


    type IntStr struct {
    value int
    }
    ```
    总体看上来, 没觉得哪里比其他语言简单. 上面有人讲, 你用别的语言(强类型)也一样的复杂度. 但是别的语言不像 golang 这种, 宣扬 less is more.
    echoless
        42
    echoless  
    OP
       2023-02-23 16:43:45 +08:00   ❤️ 2
    @vcbal 我就是个初学着, 一个整天宣扬自己简单的语言, 学习有疑问, 就说是人的原因. 那也对.
    guyanyouyou
        43
    guyanyouyou  
       2023-02-23 16:44:09 +08:00   ❤️ 2
    @wuhaoecho 学好以后发现,在 golang 的擂台上,不仅看到了原来在 python 擂台一起卷的朋友,还有前端、PHP 和 Java 的小伙伴
    dongtingyue
        44
    dongtingyue  
       2023-02-23 17:22:03 +08:00
    json 这方面确实不如 js 方便
    fregie
        45
    fregie  
       2023-02-23 17:50:54 +08:00
    没有语言是万能的,如果你觉得在某个方面 go 不好用,那么你就不该用 go 来做.
    明明 go 都不是一个面向对象的语言,非要和面向对象的语言比写业务,这才是出问题的地方
    zong400
        46
    zong400  
       2023-02-23 18:05:08 +08:00   ❤️ 2
    只有 #39 是老实人
    bxb100
        47
    bxb100  
       2023-02-23 18:21:35 +08:00
    rust 杀手锏 json -> struct
    gogorush
        48
    gogorush  
       2023-02-23 18:59:28 +08:00
    golang 说 别跟我谈 JSON 和 MySQL 的 ORM 不然我发火了
    echoless
        49
    echoless  
    OP
       2023-02-23 19:23:40 +08:00 via Android
    @gogorush 不要吓我 orm 还没用过
    sealinfree
        50
    sealinfree  
       2023-02-23 21:08:15 +08:00
    @a132811 赞,学习了
    lixintcwdsg
        51
    lixintcwdsg  
       2023-02-23 21:20:19 +08:00   ❤️ 1
    写代码 10 多年了哈
    js as ts java php python bash 都写过一些,java 写的尤其多
    架构什么的也都搞
    go 的问题很早就和人讲过了,和过去 node.js 其实类似,因为标准轮子还是太少,各种私人写的又很重要的框架、工具很多,这个实际上会有大问题。维护起来更麻烦。
    java 最大的好处就是 web 开发东西都很明确 spring 解决大部分问题,其余一些通用的解决其他问题,所以写出来东西都类似,谁接手都没啥问题。
    panlatent
        52
    panlatent  
       2023-02-23 22:46:56 +08:00   ❤️ 1
    毕业后经过 PHP -> Go -> (C# ...) -> C++(重学) 这么学一圈。对 Go 的理解就是 Better C ,至于语法,各有权衡,各有取舍,但不一定都是精心设计出来的(专指) :D
    jeesk
        53
    jeesk  
       2023-02-23 23:46:00 +08:00
    觉得 java 好阅读的可以看看 google juice 的代码。觉得 golang 好读的可以看看 docker 的代码。
    echoless
        54
    echoless  
    OP
       2023-02-23 23:47:12 +08:00 via Android
    @jeesk 诛心了这是
    hzxxx
        55
    hzxxx  
       2023-02-24 01:07:48 +08:00
    估计还没到呢,Java boy ,等你再学 protoc 、orm 、wire 、还有微服务,你就会怀念以前 spring 全家桶梭哈的日子了
    WilliamYang
        56
    WilliamYang  
       2023-02-24 01:53:17 +08:00
    你不够了解 Golang 而已,json.Number ,json.RawMessage 了解一下
    Nazz
        57
    Nazz  
       2023-02-24 07:51:07 +08:00 via Android
    刚学 go 的时候我也很疑惑,json_encode, json_decode 和万能的 array 用习惯了而已
    jorneyr
        58
    jorneyr  
       2023-02-24 09:37:33 +08:00
    写了一年 Go ,终于习惯了 Go 的写法,Java 和 Go 已经可以平滑切换。
    naturekingfree
        59
    naturekingfree  
       2023-02-24 09:39:16 +08:00
    @NeoZephyr 我也是
    macscsbf
        60
    macscsbf  
       2023-02-24 09:51:50 +08:00   ❤️ 1
    你说说看哪里难阅读了,不然我也不知道怎么去解答你的疑问
    DamonLin
        61
    DamonLin  
       2023-02-24 10:14:37 +08:00
    java 和 php 直接梭哈哈哈哈,没有这个烦恼
    gzxy
        62
    gzxy  
       2023-02-24 10:43:06 +08:00
    @wuhaoecho
    https://github.com/duke-git/lancet

    做个格式转换 convertor.ToInt()
    ZeroDu
        63
    ZeroDu  
       2023-02-24 11:02:40 +08:00
    String str = "A"
    int i = 1

    在其他语言里面可能直接 i+str 就行了。然而在 go 里面居然还要引入一个 strconv 包来转换在拼接
    vipppppp
        64
    vipppppp  
       2023-02-24 14:25:13 +08:00
    Go 开发者,Go json, error 都让我很头疼,基本实现不了好的 AOP 方式也很难受。。

    但是并发编程的价值远远大于这些让我头疼的点。。。
    vipppppp
        65
    vipppppp  
       2023-02-24 14:27:43 +08:00
    @ZeroDu 逆天,大部分强类型语言都不能这么拼,php/js 是可以这么拼接,java 是个类似语法糖
    最简单的 python 也是不允许这么拼接的,更别提 c/c++这种了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2855 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 14:37 · PVG 22:37 · LAX 06:37 · JFK 09:37
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.