V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
monster1priest
V2EX  ›  Java

Java 库为什么要这么写?

  •  
  •   monster1priest · Oct 25, 2021 via iPhone · 8122 views
    This topic created in 1645 days ago, the information mentioned may be changed or developed.
    我一般都是
    int a = getNumber(); if(a >0)…….
    但是我看到源码写法很多是
    int a; if((a=getNumber()) >0)…….
    为什么会这么写?是有什么好处吗
    50 replies    2021-10-27 20:03:38 +08:00
    bololobo
        1
    bololobo  
       Oct 25, 2021
    能显得比较厉害。别人问我为什么要这样写,我就会说 你看源码也这样写
    monster1priest
        2
    monster1priest  
    OP
       Oct 25, 2021 via iPhone
    @bololobo hashmap 就是这么写的。人家应该不是为了炫耀吧
    BBCCBB
        3
    BBCCBB  
       Oct 25, 2021
    一般是
    int a;
    if (xxx && (a=getNumber() > 0)) 这样吧,
    不能只有一个条件也 f((a=getNumber()) >0)/
    cmdOptionKana
        4
    cmdOptionKana  
       Oct 25, 2021   ❤️ 6
    有 C 语言背景可能会习惯这样写
    rannnn
        5
    rannnn  
       Oct 25, 2021
    这么写一般是在 while 里吧
    zardmyLove
        6
    zardmyLove  
       Oct 25, 2021
    我的想法是显得紧凑
    zjsxwc
        7
    zjsxwc  
       Oct 25, 2021 via Android   ❤️ 3
    因为这个变量 a 只是单纯想在 if 条件中使用?
    而 if 的 condition 里不能 declare 变量 a 类型,不得不挪到外面。

    https://stackoverflow.com/questions/16148580/assign-variable-value-inside-if-statement
    zjsxwc
        8
    zjsxwc  
       Oct 25, 2021 via Android
    感觉,莫名好笑😄
    546L5LiK6ZOt
        9
    546L5LiK6ZOt  
       Oct 25, 2021   ❤️ 4
    我记得很多类库还有一个常见的写法,就是在一个方法里,把类的成员变量赋值给局部变量,方法里都使用这个局部变量,而不直接用成员变量。例如:
    class A {
    int a;

    void func() {
    int a = this.a;
    // ……
    }
    }

    后来看到一种解释,说访问方法里成员变量不利于运行时优化(至少局部变量肯定是线程安全的)。


    我记得 jdk 的类库里还经常看到这么种情况,明明这两个循环可以写在一起,合并成一个循环,但是却特地分开写。一种解释说,小的循环容易让 cpu 命中 cache ,执行更快。。。

    总之,类库总有些特殊的写法,跟写业务代码不一样
    mritd
        10
    mritd  
       Oct 26, 2021 via iPhone
    @546L5LiK6ZOt #9 这种好像是为了满足那些 get set 规范
    Jooooooooo
        11
    Jooooooooo  
       Oct 26, 2021
    这是习惯问题

    两种写法还能用性能差异那是 jvm 有毛病
    chendy
        12
    chendy  
       Oct 26, 2021
    没啥好处,甚至不太好读
    是源码还是反编译?反编译的话经常看到一些奇怪的写法,但是都是编译器优化导致的
    rpish
        13
    rpish  
       Oct 26, 2021
    @546L5LiK6ZOt
    @zjsxwc
    感谢两位老哥,涨姿势了👍
    yidinghe
        14
    yidinghe  
       Oct 26, 2021 via Android
    不看上下文的话,每次都这么写是脱裤子放屁,但确实有合理使用的场景,一般都是在 IO 流读取时出现。
    Cbdy
        15
    Cbdy  
       Oct 26, 2021
    IO 时会这样写,C 语言延续下来的传统,我记得 Unix 高程称赞过这种写法
    Leviathann
        16
    Leviathann  
       Oct 26, 2021 via iPhone
    这就是 c 味
    反正我挺不喜欢的
    micean
        17
    micean  
       Oct 26, 2021 via Android
    @zjsxwc 那不需要声明 a 变量
    aguesuka
        18
    aguesuka  
       Oct 26, 2021
    就是 4 楼的说法, 请相信这种简单的优化, 编译器比人更聪明.
    bk201
        19
    bk201  
       Oct 26, 2021
    语意应该浅显易懂,我觉得源码这处不行
    cubecube
        20
    cubecube  
       Oct 26, 2021
    @aguesuka 然而有时候编译器并不会更聪明。
    javac 近些年几乎保持不变。很多 jdk 的代码的确有部分优化,至于这种方式是否有神秘性能加成,回头可以分析下。
    wolfie
        21
    wolfie  
       Oct 26, 2021
    简洁啊。

    举例不对,这个例子没有任何必要。
    cpstar
        22
    cpstar  
       Oct 26, 2021
    从编译出的 bytecode 上,这两个没差别,也无所谓性能调优。
    都是先 invoke 再 ifgt 。

    如果说性能调优,那就是 9#说的问题,局部变量,不需要每次都 invoke——当然编译器也可能自动识别并且加上一个局部变量压栈
    yinzhili
        23
    yinzhili  
       Oct 26, 2021
    一些人原先习惯了其它的编程语言,写 Java 代码的时候就会带上之前的习惯,比如:喜欢用下划线开头的变量名
    ipwx
        24
    ipwx  
       Oct 26, 2021
    if 这么写就是魔怔了。。。

    但是如果换成 while ,这么写就爽了。比如:

    int bytesRead;
    while ((bytesRead = read(fd)) > 0) {
    ...
    }

    对比如果不这么写:

    while (true) {
    int bytesRead = read(fd);
    if (bytesRead <= 0) {
    break;
    }
    ...
    }

    显然前者更简洁。
    skinny
        25
    skinny  
       Oct 26, 2021
    可能没什么特殊用意,可能就是写这部分代码的人有一些在 C/C++的不良习惯而已,这写法不是特殊需要在 C/C++也不是推荐写法啊。后面更新维护的人也不会在没有明显问题时去动它,于是就保留了下来。JVM 优化不会这么挫的。说起库源代码违反最佳写法官方推荐写法的多了去了,比如 Python ,就很多问题,但是没人愿意去更新源代码。
    clf
        26
    clf  
       Oct 26, 2021
    有没有可能第二种的 a 作用域只在 if 的括号里,所以这样写。
    b0644170fc
        27
    b0644170fc  
       Oct 26, 2021
    我觉得 4 楼说的有理.
    cubecube
        28
    cubecube  
       Oct 26, 2021   ❤️ 1
    @aguesuka 的确没有神秘加成, 可能就是程序员的个人习惯了

    JMHPerfCondition.conditionInvokeFirst avgt 3 13.930 ± 3.733 ns/op
    JMHPerfCondition.conditionInvokeLater avgt 3 13.742 ± 1.632 ns/op

    ```java
    @Benchmark
    public int conditionInvokeFirst() {
    int a = SpecialCaseTest.getNumber();
    if (a > 0) {
    return 1;
    } else {
    return 0;
    }

    }

    @Benchmark
    public int conditionInvokeLater() {
    int a;
    if ((a = SpecialCaseTest.getNumber()) > 0) {
    return 1;
    } else {
    return 0;
    }
    }
    ```
    MineDog
        29
    MineDog  
       Oct 26, 2021
    @546L5LiK6ZOt #9 还有是访问成员变量需要的指令比局部变量多,在保证语义一致的情况下,指令更少,性能更好一点
    tobepro
        30
    tobepro  
       Oct 26, 2021
    记得以前学嵌入式的时候,听韦东山老师说过,有部分大型开源项目的看着感觉很高大上的代码,其实没什么卵用,单纯就是写代码的人想炫技
    streamrx
        31
    streamrx  
       Oct 26, 2021 via iPhone
    1
    yuchting
        32
    yuchting  
       Oct 26, 2021
    其实更吊的写法应该是 if((var a = getNumber()) > 0) ...
    可惜没语言支持。
    oldshensheep
        33
    oldshensheep  
       Oct 26, 2021
    @yuchting
    python 可以
    xiang0818
        34
    xiang0818  
       Oct 26, 2021
    因为是上了年代的程序员写的~
    ColinZeb
        35
    ColinZeb  
       Oct 26, 2021
    @yuchting c# 可以 ,还可以顺便判断类型或者是否为空。

    if( obj is int a )//判断是否 int 类型 如果是 int 赋值给 int a

    if(obj is {})//判断是否非空
    ipwx
        36
    ipwx  
       Oct 26, 2021
    @yuchting python 3.9


    if (a := getNumber()) > 0:
    ....
    newmlp
        37
    newmlp  
       Oct 26, 2021   ❤️ 1
    代码行数少,有限的空间内可以看到更多的逻辑,没其他原因
    zhgg0
        38
    zhgg0  
       Oct 26, 2021
    @newmlp 就是 37 楼说的原因,纯粹就是因为代码行数少。仔细翻下 HashMap 的源码就能验证,我刚验证过。
    在 HashMap 源码里面,就拿楼主写的代码来作比方 int a = getNumber(); if(a >0);
    1 、如果 a 变量在别的地方早就定义过,那就会被写成 if((a=getNumber()) >0);
    2 、如果 a 变量在别的地方没定义过,需要定义,那就会被写成 int a = getNumber(); if(a >0);

    1 的情况放 if 里能节省一行,所以放 if 里了; 2 的情况不管是否放 if 里都不能节省一行,所以没放 if 里;仔细翻下源码就发现了。
    penguinWWY
        39
    penguinWWY  
       Oct 26, 2021
    @cubecube 优化是后面 Hotspot 干的事情,javac 几乎不做优化
    geligaoli
        40
    geligaoli  
       Oct 26, 2021
    @546L5LiK6ZOt 多线程中,有时这么写是为了避免锁,局部变量之后的操作,不用担心其他线程的影响。
    littlewing
        41
    littlewing  
       Oct 26, 2021
    羡慕 Java ,能看懂 标准库的代码,最新学 C++,STL 不是人看的
    rrfeng
        42
    rrfeng  
       Oct 26, 2021 via Android
    @yuchting
    你们最讨厌的 go 也支持
    iceheart
        43
    iceheart  
       Oct 26, 2021 via Android
    C 代码转过来的吧,直接用了,省事
    eason1874
        44
    eason1874  
       Oct 26, 2021
    @yuchting #32 PHP 很多就这么写的,不用提前声明变量 $a ,直接在条件判断里赋值运算,某些场景能省不少 if 嵌套

    if (isOk() && ($a = getNumber()) > 0 && $a < 10) echo $a;
    someonedeng
        45
    someonedeng  
       Oct 27, 2021
    c 语言留下的习惯,个人感觉不好看
    fuchaofather
        46
    fuchaofather  
       Oct 27, 2021
    c 味儿
    GiftedJarvis
        47
    GiftedJarvis  
       Oct 27, 2021
    我也有这疑问, 不光 HashMap, 还有一堆 BlockQueue, AQS 里都这么写
    yolee599
        48
    yolee599  
       Oct 27, 2021
    @skinny #25 对于 C 语言,这样写不是不良习惯,反而是好习惯,因为有的编译器不支持以下语法:

    for (int i = 0; i < 10; i++)
    {
    ...
    }

    要改成下面的写法才能编译通过:

    int i;
    for (i = 0; i < 10; i++)
    {
    ...
    }

    为了兼容所有的编译器所以使用第二种写法,还有的编译器定义变量的时候不能在函数中间定义,必须在函数最前面定义。
    yolee599
        49
    yolee599  
       Oct 27, 2021
    @yolee599 #48 第一种写法是 C99 的语法,很多编译器是不支持 C99 语法的。有时候编译器支持 C99 也不会特意去开启 C99 ,都是为了兼容😂
    skinny
        50
    skinny  
       Oct 27, 2021
    @yolee599 你没明白我的意思
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   6117 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 179ms · UTC 02:45 · PVG 10:45 · LAX 19:45 · JFK 22:45
    ♥ Do have faith in what you're doing.