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

感觉 golang 比 Java 还难读, 难学

  •  
  •   echoless · 215 天前 · 6781 次点击
    这是一个创建于 215 天前的主题,其中的信息可能已经有所发展或是发生改变。

    要解析的 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  
       215 天前 via Android   ❤️ 2
    interface 了解下
    NeoZephyr
        2
    NeoZephyr  
       215 天前   ❤️ 2
    说真的,看到这种满屏的 v, i, e 变量,就想吐🤮
    echoless
        3
    echoless  
    OP
       215 天前   ❤️ 1
    @NeoZephyr 官方源代码, 可不是我写的. 我也是吐, 有些同事把 golang 这种风格弄到 python 里面, 看到我就...
    echoless
        4
    echoless  
    OP
       215 天前
    另外求 golang 的资料, 中级水平的, 准备去卷 golang 了. 感觉 python 工作工资越来越不行了.
    SuperManNoPain
        5
    SuperManNoPain  
       215 天前   ❤️ 1
    大道至简,滑稽.jpg
    liuxu
        6
    liuxu  
       215 天前   ❤️ 22
    一般不是从 C 学起,没看过大型 C 项目的是对这种写法不适应

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

    WispZhan
        7
    WispZhan  
       215 天前 via Android   ❤️ 2
    Java 是语法最简单、最啰嗦的编程语言
    echoless
        8
    echoless  
    OP
       215 天前
    @liuxu 上段代码里面的 v, u 我倒是没有意见. 因为也不好找一个合适的命名. 但是有些地方, 比如输入参数是 car Car, 那么命名弄个 c 就是恶心人了.
    hzjseasea
        9
    hzjseasea  
       215 天前   ❤️ 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
       215 天前
    上面的问题主要是各种 reflect, 看晕了. 还有 reflect.Zero MakeSlice 这种, 可能是用的少吧, 看起来抽象
    echoless
        11
    echoless  
    OP
       215 天前
    @hzjseasea 多谢, 这个解决了解析的问题, 但是我这个 price 还是要转换成 int64
    RICKEYGONG
        12
    RICKEYGONG  
       215 天前
    @liuxu 赞同
    coderxy
        13
    coderxy  
       215 天前   ❤️ 1
    go 如果你读不明白,java 你大概率也读不明白的。
    8355
        14
    8355  
       215 天前
    喜欢 java 和 go 的本质上就是两种人
    写 java 循规蹈矩建一堆文件起长长的名字 xml 真的很刺激
    假如没有注解🙅 🙅 🙅
    hzjseasea
        15
    hzjseasea  
       215 天前
    @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
       215 天前
    @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  
       215 天前   ❤️ 3
    解析 json 还得看我大 PHP 和 JS 啊,哈哈
    tuxz
        18
    tuxz  
       215 天前   ❤️ 1
    可以自定义实现 struct 的反序列化,参考这个 https://attilaolah.eu/2013/11/29/json-decoding-in-go
    zjsxwc
        19
    zjsxwc  
       215 天前   ❤️ 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  
       215 天前
    @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  
       215 天前   ❤️ 9
    @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
       215 天前 via Android
    @zjsxwc 我需要的就是这种
    echoless
        23
    echoless  
    OP
       215 天前 via Android
    @MoYi123 他这个命名也不好 应该用单词
    a132811
        24
    a132811  
       215 天前
    1. 这不是 golang 本身的问题。golang 自带的这个 json 库封装时就没有支持多类型自动转换。
    2. 语言风格、习惯其实没有什么好喷的,有人就是喜欢这种简化,有人就是喜欢冗长风格,争论不会任何结果。
    3. 不过 golang 有一点好就是源码修改和调试非常的方便。有精力的话,可以给官方提一个 PR
    erichen86
        25
    erichen86  
       215 天前
    python 为什么这么流行,代码看着也挺难受的,感觉还不如 go 看的舒服
    yaott2020
        26
    yaott2020  
       215 天前 via Android
    @zjsxwc 你可以自己在 github 提个 issue ,或者自定义解析函数,字段多类型可以用 any(interface{})
    rrfeng
        27
    rrfeng  
       215 天前   ❤️ 1
    type IntStr int64

    type Model struct {
    Price IntStr
    }

    func (IntStr) Unmarshal(){
    ...
    }
    wangsongyan
        28
    wangsongyan  
       215 天前
    json.Number
    dobelee
        29
    dobelee  
       215 天前
    value -> v
    element -> e

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

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

    醉舞经楼半卷书,坐井谈天阔
    fgwmlhdkkkw
        32
    fgwmlhdkkkw  
       215 天前
    你自定义一个 Price 类型,不久好了吗?
    现实世界就是这么复杂,你换什么语言都要面对的啊?
    lxiian
        33
    lxiian  
       215 天前 via iPhone
    @erichen86 一样 我反正是讨厌 python
    echoless
        34
    echoless  
    OP
       215 天前
    @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  
       215 天前
    我就搞不懂为啥还要规定变量命名风格。。。
    无论啥语言
    a132811
        36
    a132811  
       215 天前   ❤️ 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
       215 天前
    @a132811

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

    实际应用来讲 我觉得多数时候没区别 可能有一些性能差异.
    falcon05
        38
    falcon05  
       215 天前
    从动态语言过来的看到这 json 处理感觉有点呆
    virusdefender
        39
    virusdefender  
       215 天前
    json.Number 就是来干这个的
    vcbal
        40
    vcbal  
       215 天前
    有没有想过这是你自己的原因?
    echoless
        41
    echoless  
    OP
       215 天前
    我把完整的代码列出来

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

    做个格式转换 convertor.ToInt()
    ZeroDu
        63
    ZeroDu  
       214 天前
    String str = "A"
    int i = 1

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

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