你一定不知道的链式调用新姿势

如何链式调用Java静态方法?

链式调用

首先需要提一下最近几年在开发界大?的链式调用,链式调用非常利于编写,更利于阅读,那么,什么是链式调用呢,为了扫盲,举个例子。

示例代码如下,如果我们想创建一个Dialog,不依赖链式调用的话,将会产生如下代码:

    NRStandardDialog.Builder nrStandardDialogBuilder = NRDialog.standard();

    nrStandardDialogBuilder.setTitle("Test");
    nrStandardDialogBuilder.setMessage("Test");
    nrStandardDialogBuilder.setNegativeTitle("Test");
    nrStandardDialogBuilder.setPositiveTitle("Test");
    nrStandardDialogBuilder.setNeutralTitle("Test");

    nrStandardDialogBuilder.show(this);
复制代码

上述代码共计372个字符,当然,通过依赖强大的IDEA,我们站在巨人的肩膀上操作,通过智能提示和自动补全,极大降低了开发成本。

那么,现在来估算一下操作成本,通过FreeKey记录键盘输入,我们的操作路径大概是

“NRdia .sta ; nr .set "Test; nr .setM "Test; nr .setnet ""Test; nr .setPo ""Test; nr .setneu "Test; nr .sh thi ;”
复制代码

共计需要输入126个字符,What?这样看好像其实也没有比全部输入少很多。

那么,我们将这个Dialog改为链式调用模式创建,会产生如下代码。

    NRDialog.standard()
            .setTitle("Test")
            .setMessage("Test")
            .setNegativeTitle("Test")
            .setPositiveTitle("Test")
            .setNeutralTitle("Test")
            .show(this);
复制代码

上述代码共计223个字符,减少了149个字符。怎么样,代码数量是不是一下子掉下来了很多。

那么,我们来统计一下链式调用的操作路径成本,再次通过FreeKey记录键盘输入,我们的操作路径大概是:

NRD .st .sett "Test.setme "Test.setneg "Test.setpo "Test.setNe "Test.sho thi ;
复制代码

共计需要输入94个字符,通过链式调用,我们节省了1.5倍的开发时间成本,并且隐形的节省了代码的阅读成本。

方法链的优秀案例

一些流行的开源库的方法链优秀使用案例如下:

RxJava
    Flowable.range(1, 10)
      .observeOn(Schedulers.computation())
      .map(v -> v * v)
      .blockingSubscribe(System.out::println);
复制代码
EventBus
    EventBus.builder()
            .eventInheritance(false)
            .logSubscriberExceptions(false)
            .build()
            .register(this);
复制代码

静态方法的链式调用

有些时候,我们会遇到一些需求,考虑想把静态方法进行封装一下,来实现优美的链式调用,Like this;

public class Test {

    public static Test doSth(){
        // dosth.
        return this;
    }
}
复制代码

如果是非静态方法的话,我们可以从容的返回this,如果是静态方法,这么操作,真的就是想太多。

或者我们这么操作?

这么操作?

R U OK?

一顿操作后,我们还是没法实现静态方法的链式调用,因为他必须返回一个自身的实例。

好吧,最终屈服于IDEA,你反悔了这个静态方法的实例。

创建了一个Test类的实例,然后愉快地实现了链式调用

public class MainTest {

    public void test(){
        Test.doSth().doSth2();
    }
}
复制代码

可是问题来了,为了实现链式调用,我们必须为每个对象开辟内存空间吗?

当然不!

姿势来了,其实我们可以这样处理:

public class Test {

    public static Test doSth(){
        return null;
    }

    public static Test doSth2(){
        return null;
    }
}
复制代码

定义两个静态方法,返回为空

public class MainTest {

    public void test(){
        Test.doSth().doSth2();
    }

}
复制代码

完美实现链式调用,而且不会报空指针,这是为什么?

原理分析

我们对上述调用代码的示例进行编译与反编译:

    public void test(){
        Test.doSth().doSth2();
    }
复制代码

对应的反编译后的字节码如下:

  public void test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: invokestatic  #2                  // Method com/Test.doSth:()Lcom/Test;
         3: pop
         4: invokestatic  #3                  // Method com//Test.doSth2:()Lcom/Test;
         7: pop
         8: return
      LineNumberTable:
        line 9: 0
        line 10: 8
复制代码

invokestatic调用静态方法doSth和doSth2,可以直接找到索引2和索引4的方法,所以并没有依赖Test实例。

而对于正常的对象调用,如果使用Null调用为什么会报空指针呢?我们再来看一段示例代码和其反编译的字节码。

声明mainTest为空并调用其test方法。

public void test2(){
    MainTest mainTest = null;
    mainTest.test();
}
复制代码

反编译后的字节码如下,invokevirtual为调用实例方法,动态绑定,invokevirtual #4代表会调用索引4的方法,即test()。

  public void test2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=1
         0: aconst_null  // 将null入栈
         1: astore_1     // 将引用存入局部变量表1中
         2: aload_1      // 将局部变量表1的对象引用压入栈顶
         3: invokevirtual #4     // Method com/MainTest.test:()V
         6: return
      LineNumberTable:
        line 25: 0
        line 26: 2
        line 27: 6
复制代码

而invokevirtual则负责找到操作栈栈顶元素所指向的对象并查找和调用其相关方法,但此时栈顶为空,自然就无法找到相关方法,抛出空指针。

而这里就涉及到了静态绑定和动态绑定。绑定过程,即指方法的调用与其类的关联过程。

其中的静态绑定,是在方法执行前,已经被关联,而动态绑定则是在运行时绑定。

最后

最后,在项目代码中还是并不推荐这种方式,团队开发,难免会有小伙伴们产生误解,会认为该对象已经生成了实例了,而创建非静态方法调用导致空指针。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
近年来,APM进入了一个高速发展的快车道,分布式环境下的应用自动发现及动态调用链路分析作为APM的核心功能在APM的推广中也获得了广泛的使用,但在实践中,也暴露出了一些不足:完全基于运行态的分析模式决定了其只能获取到有实际调用的逻辑链路,大量没有埋点或未被触发的调用逻辑则成了“失落的世界”,无法触碰。 寻找这部分“失落的世界”对我们了解分布式环境下的应用全貌至关重要,天弘基金移动平台团队在这方面做了一些创性的探索,我们跳出了传统APM的惯性思维,通过对海量代码中的调用关系的扫描分析来获取分布式环境下“前中后”台的完整调用链路,并在此基础上叠加动态调用链来构建精细化的APM监控。 通过本专题,我将基于我们的实践来详细介绍“静态调用链路发现”的技术与手段,并探索运维及开发场景中如何将它和现有的APM能力进行结合。 -->听众受益: 1、了解分布式环境下APM的“优”与“缺”。 2、了解构建“静态调用链路发现”能力的技术与手段。 3、了解如何用创性的思维拓展APM的应用场景。 -->个人简介: 李鑫,天弘基金(余额宝)移动平台首席架构师,负责移动平台总体技术架构设计。曾任当当网架构师,负责电商后端运作产品平台整体技术架构和研发团队管理;华为云计算专家,主导了华为软件多款云计算产品和服务的设计规划和构建工作。 个人技术涉及大规模分布式应用及治理、中间件云化及服务化(PaaS)、APM 监控、基础开发平台等领域,有多年大规模复杂系统架构实践经验。
Prettier 链式调用不换行是指在代码中使用 Prettier 工具对链式调用的代码进行格式化时,不会将链式调用的每个方法调用放在单独的一行上,而是将它们放在同一行上。 链式调用是指在代码中连续调用多个方法,并将方法调用连接起来,形成一个链式结构。例如,在 JavaScript 中,我们可以使用`.`运算符来连续调用多个对象的方法: ``` obj.method1() .method2() .method3(); ``` 这样的链式调用可以使代码更加清晰、简洁,使我们能够更方便地使用和理解代码。 Prettier 是一款代码格式化工具,可以帮助开发者自动格式化代码,使代码风格更加一致。当我们在使用 Prettier 对代码进行格式化时,可以选择是否对链式调用进行换行。 如果选择不对链式调用进行换行,那么 Prettier 会将所有的方法调用都放在同一行上,可以使代码更加紧凑,节省空间。这种格式化方式适用于链式调用方法数量较少、较短的情况。 然而,如果链式调用的方法数量较多、较长,那么不进行换行的格式化方式可能会导致代码在阅读时显得拥挤,不易于理解。因此,在使用 Prettier 进行代码格式化时,我们也可以选择对链式调用进行换行,使每个方法调用在单独的一行上,提高代码的可读性。 总之,使用 Prettier 进行链式调用不换行的格式化可以使代码更加紧凑,适用于简短的链式调用。但在链式调用较长时,为了代码的可读性,我们还可以选择进行换行。这样可以使每个方法调用都独占一行,提高代码的可读性和可维护性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值