尽管您提到您已经知道了:您应该避免这么长的方法链,原因有几个:
- 这表明了糟糕的 OO 设计(更准确地说,它看起来违反了德墨忒耳定律)
- 这是很容易出错的。如果一行像
a.getB().getC().getD().doSomething()
抛出一个NullPointerException
,你不会有调试这个的乐趣......
- 让我在这里说一点主观的话:它看起来很糟糕。
当然,你还得考虑what这些方法实际上正在做。虽然一个get
-方法通常应该只返回一个值,你不知道它是否真的这样做。甚至像
List<T> getList() {
// Return an unmodifiable view to the caller
return Collections.unmodifiableList(internalList);
}
(这当然is一个好的做法)可能会改变结果。
话虽这么说,并考虑到这些get
-方法真的很简单,很愚蠢Getters:
它不会对实践中的性能产生影响。方法调用将由 JIT 内联。
例如,考虑以下程序:
class ChainA {
private ChainB b = new ChainB();
ChainB getB() {
return b;
}
}
class ChainB {
private ChainC c = new ChainC();
ChainC getC() {
return c;
}
}
class ChainC {
private ChainD d = new ChainD();
ChainD getD() {
return d;
}
}
class ChainD {
private int result = 0;
int getResult() { return result; }
void doSomething1(){ result += 1; }
void doSomething2(){ result += 2; }
void doSomething3(){ result += 3; }
}
class Chaining
{
public static void main(String args[])
{
for (int n=100; n<10000; n+=100)
{
ChainA a0 = new ChainA();
runChained(a0, n);
System.out.println(a0.getB().getC().getD().getResult());
ChainA a1 = new ChainA();
runUnChained(a1, n);
System.out.println(a1.getB().getC().getD().getResult());
}
}
private static void runChained(ChainA a, int n)
{
for (int i=0; i<n; i++)
{
a.getB().getC().getD().doSomething1();
a.getB().getC().getD().doSomething2();
a.getB().getC().getD().doSomething3();
}
}
private static void runUnChained(ChainA a, int n)
{
ChainD d = a.getB().getC().getD();
for (int i=0; i<n; i++)
{
d.doSomething1();
d.doSomething2();
d.doSomething3();
}
}
}
它像您所描述的那样执行调用,一次作为链式调用,一次作为非链式版本。
运行它
java -server -XX:+UnlockDiagnosticVMOptions -XX:+TraceClassLoading -XX:+LogCompilation -XX:+PrintAssembly Chaining
在启用 HotSpot-Disassembler 的 JVM 上会产生以下输出(无需read仅供参考)
链式运行:
Decoding compiled method 0x0000000002885d50:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000055360550} 'runChained' '(LChainA;I)V' in 'Chaining'
# parm0: rdx:rdx = 'ChainA'
# parm1: r8 = int
# [sp+0x30] (sp of caller)
0x0000000002885e80: mov %eax,-0x6000(%rsp)
0x0000000002885e87: push %rbp
0x0000000002885e88: sub $0x20,%rsp ;*synchronization entry
; - Chaining::runChained@-1 (line 47)
0x0000000002885e8c: mov %rdx,%r9
0x0000000002885e8f: mov %r8d,%ebx
0x0000000002885e92: test %r8d,%r8d
0x0000000002885e95: jle 0x0000000002885f8a ;*if_icmpge
; - Chaining::runChained@4 (line 47)
0x0000000002885e9b: mov 0xc(%rdx),%r11d ;*getfield b
; - ChainA::getB@1 (line 4)
; - Chaining::runChained@8 (line 49)
; implicit exception: dispatches to 0x0000000002885f96
0x0000000002885e9f: mov 0xc(%r11),%r10d ;*getfield c
; - ChainB::getC@1 (line 10)
; - Chaining::runChained@11 (line 49)
; implicit exception: dispatches to 0x0000000002885f96
0x0000000002885ea3: mov 0xc(%r10),%edx ;*getfield d
; - ChainC::getD@1 (line 16)
; - Chaining::runChained@14 (line 49)
; implicit exception: dispatches to 0x0000000002885f96
0x0000000002885ea7: mov 0xc(%rdx),%r11d ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runChained@17 (line 49)
; implicit exception: dispatches to 0x0000000002885f96
0x0000000002885eab: xor %r10d,%r10d
0x0000000002885eae: xor %esi,%esi
0x0000000002885eb0: xor %r8d,%r8d
0x0000000002885eb3: xor %ecx,%ecx ;*aload_0
; - Chaining::runChained@7 (line 49)
0x0000000002885eb5: add %r11d,%esi ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runChained@17 (line 49)
0x0000000002885eb8: add %ecx,%r8d
0x0000000002885ebb: mov %esi,%eax
0x0000000002885ebd: add $0x6,%eax ;*iadd
; - ChainD::doSomething3@6 (line 25)
; - Chaining::runChained@43 (line 51)
0x0000000002885ec0: mov %eax,0xc(%rdx) ;*putfield result
; - ChainD::doSomething3@7 (line 25)
; - Chaining::runChained@43 (line 51)
0x0000000002885ec3: mov %r10d,%edi
0x0000000002885ec6: inc %edi ;*iinc
; - Chaining::runChained@46 (line 47)
0x0000000002885ec8: cmp $0x1,%edi
0x0000000002885ecb: jge 0x0000000002885eee ;*if_icmpge
; - Chaining::runChained@4 (line 47)
0x0000000002885ecd: mov %r10d,%ecx
0x0000000002885ed0: shl %ecx
0x0000000002885ed2: mov %r8d,%esi
0x0000000002885ed5: add $0x6,%esi
0x0000000002885ed8: mov %ecx,%r8d
0x0000000002885edb: add $0x2,%r8d
0x0000000002885edf: shl $0x2,%r10d
0x0000000002885ee3: mov %r10d,%ecx
0x0000000002885ee6: add $0x4,%ecx
0x0000000002885ee9: mov %edi,%r10d
0x0000000002885eec: jmp 0x0000000002885eb5
0x0000000002885eee: mov %ebx,%r11d
0x0000000002885ef1: add $0xfffffff1,%r11d
0x0000000002885ef5: mov $0x80000000,%r9d
0x0000000002885efb: cmp %r11d,%ebx
0x0000000002885efe: cmovl %r9d,%r11d
0x0000000002885f02: cmp %r11d,%edi
0x0000000002885f05: jge 0x0000000002885f3c
0x0000000002885f07: sub %r8d,%esi
0x0000000002885f0a: nopw 0x0(%rax,%rax,1) ;*aload_0
; - Chaining::runChained@7 (line 49)
0x0000000002885f10: mov %edi,%r8d
0x0000000002885f13: shl %r8d
0x0000000002885f16: mov %edi,%r9d
0x0000000002885f19: shl $0x2,%r9d
0x0000000002885f1d: add %r9d,%r8d
0x0000000002885f20: add %esi,%r8d ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runChained@17 (line 49)
0x0000000002885f23: mov %r8d,%eax
0x0000000002885f26: add $0x60,%eax ;*iadd
; - ChainD::doSomething3@6 (line 25)
; - Chaining::runChained@43 (line 51)
0x0000000002885f29: add $0x5a,%r8d
0x0000000002885f2d: mov %r8d,0xc(%rdx)
0x0000000002885f31: mov %eax,0xc(%rdx) ;*putfield result
; - ChainD::doSomething3@7 (line 25)
; - Chaining::runChained@43 (line 51)
0x0000000002885f34: add $0x10,%edi ;*iinc
; - Chaining::runChained@46 (line 47)
0x0000000002885f37: cmp %r11d,%edi
0x0000000002885f3a: jl 0x0000000002885f10 ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runChained@17 (line 49)
0x0000000002885f3c: cmp %ebx,%edi
0x0000000002885f3e: jge 0x0000000002885f8a
0x0000000002885f40: mov %edi,%r10d
0x0000000002885f43: shl $0x2,%r10d
0x0000000002885f47: mov %edi,%r11d
0x0000000002885f4a: shl %r11d
0x0000000002885f4d: mov %r11d,%r8d
0x0000000002885f50: add %r10d,%r8d
0x0000000002885f53: sub %r8d,%eax
0x0000000002885f56: xchg %ax,%ax ;*aload_0
; - Chaining::runChained@7 (line 49)
0x0000000002885f58: add %r11d,%r10d
0x0000000002885f5b: add %eax,%r10d
0x0000000002885f5e: add $0x6,%r10d
0x0000000002885f62: mov %r10d,0xc(%rdx) ;*putfield result
; - ChainD::doSomething3@7 (line 25)
; - Chaining::runChained@43 (line 51)
0x0000000002885f66: mov %edi,%r8d
0x0000000002885f69: inc %r8d ;*iinc
; - Chaining::runChained@46 (line 47)
0x0000000002885f6c: cmp %ebx,%r8d
0x0000000002885f6f: jge 0x0000000002885f8a
0x0000000002885f71: mov %edi,%r10d
0x0000000002885f74: shl $0x2,%r10d
0x0000000002885f78: shl %edi
0x0000000002885f7a: add $0x4,%r10d
0x0000000002885f7e: mov %edi,%r11d
0x0000000002885f81: add $0x2,%r11d
0x0000000002885f85: mov %r8d,%edi
0x0000000002885f88: jmp 0x0000000002885f58 ;*if_icmpge
; - Chaining::runChained@4 (line 47)
0x0000000002885f8a: add $0x20,%rsp
0x0000000002885f8e: pop %rbp
0x0000000002885f8f: test %eax,-0x26c5f95(%rip) # 0x00000000001c0000
; {poll_return}
0x0000000002885f95: retq
0x0000000002885f96: mov $0xffffff86,%edx
0x0000000002885f9b: mov %r9,%rbp
0x0000000002885f9e: mov %r8d,(%rsp)
0x0000000002885fa2: nop
0x0000000002885fa3: callq 0x00000000027b7320 ; OopMap{rbp=Oop off=296}
;*aload_0
; - Chaining::runChained@7 (line 49)
; {runtime_call}
0x0000000002885fa8: int3 ;*aload_0
; - Chaining::runChained@7 (line 49)
0x0000000002885fa9: hlt
0x0000000002885faa: hlt
0x0000000002885fab: hlt
0x0000000002885fac: hlt
0x0000000002885fad: hlt
0x0000000002885fae: hlt
0x0000000002885faf: hlt
0x0000000002885fb0: hlt
0x0000000002885fb1: hlt
0x0000000002885fb2: hlt
0x0000000002885fb3: hlt
0x0000000002885fb4: hlt
0x0000000002885fb5: hlt
0x0000000002885fb6: hlt
0x0000000002885fb7: hlt
0x0000000002885fb8: hlt
0x0000000002885fb9: hlt
0x0000000002885fba: hlt
0x0000000002885fbb: hlt
0x0000000002885fbc: hlt
0x0000000002885fbd: hlt
0x0000000002885fbe: hlt
0x0000000002885fbf: hlt
[Exception Handler]
[Stub Code]
0x0000000002885fc0: jmpq 0x00000000028694a0 ; {no_reloc}
[Deopt Handler Code]
0x0000000002885fc5: callq 0x0000000002885fca
0x0000000002885fca: subq $0x5,(%rsp)
0x0000000002885fcf: jmpq 0x00000000027b6f40 ; {runtime_call}
0x0000000002885fd4: hlt
0x0000000002885fd5: hlt
0x0000000002885fd6: hlt
0x0000000002885fd7: hlt
不受束缚地运行:
Decoding compiled method 0x00000000028893d0:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000055360628} 'runUnChained' '(LChainA;I)V' in 'Chaining'
# parm0: rdx:rdx = 'ChainA'
# parm1: r8 = int
# [sp+0x30] (sp of caller)
0x0000000002889500: mov %eax,-0x6000(%rsp)
0x0000000002889507: push %rbp
0x0000000002889508: sub $0x20,%rsp ;*synchronization entry
; - Chaining::runUnChained@-1 (line 57)
0x000000000288950c: mov 0xc(%rdx),%r11d ;*getfield b
; - ChainA::getB@1 (line 4)
; - Chaining::runUnChained@1 (line 57)
; implicit exception: dispatches to 0x0000000002889612
0x0000000002889510: mov 0xc(%r11),%r10d ;*getfield c
; - ChainB::getC@1 (line 10)
; - Chaining::runUnChained@4 (line 57)
; implicit exception: dispatches to 0x000000000288961d
0x0000000002889514: mov 0xc(%r10),%ebx ;*getfield d
; - ChainC::getD@1 (line 16)
; - Chaining::runUnChained@7 (line 57)
; implicit exception: dispatches to 0x0000000002889629
0x0000000002889518: mov %r8d,%esi
0x000000000288951b: test %r8d,%r8d
0x000000000288951e: jle 0x0000000002889606 ;*if_icmpge
; - Chaining::runUnChained@15 (line 58)
0x0000000002889524: mov 0xc(%rbx),%r10d ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runUnChained@19 (line 60)
; implicit exception: dispatches to 0x0000000002889635
0x0000000002889528: xor %r8d,%r8d
0x000000000288952b: xor %edx,%edx
0x000000000288952d: xor %r9d,%r9d
0x0000000002889530: xor %r11d,%r11d ;*aload_2
; - Chaining::runUnChained@18 (line 60)
0x0000000002889533: add %r10d,%edx ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runUnChained@19 (line 60)
0x0000000002889536: add %r11d,%r9d
0x0000000002889539: mov %edx,%edi
0x000000000288953b: add $0x6,%edi ;*iadd
; - ChainD::doSomething3@6 (line 25)
; - Chaining::runUnChained@27 (line 62)
0x000000000288953e: mov %edi,0xc(%rbx) ;*putfield result
; - ChainD::doSomething3@7 (line 25)
; - Chaining::runUnChained@27 (line 62)
0x0000000002889541: mov %r8d,%ecx
0x0000000002889544: inc %ecx ;*iinc
; - Chaining::runUnChained@30 (line 58)
0x0000000002889546: cmp $0x1,%ecx
0x0000000002889549: jge 0x000000000288956e ;*if_icmpge
; - Chaining::runUnChained@15 (line 58)
0x000000000288954b: mov %r8d,%r11d
0x000000000288954e: shl %r11d
0x0000000002889551: mov %r9d,%edx
0x0000000002889554: add $0x6,%edx
0x0000000002889557: mov %r11d,%r9d
0x000000000288955a: add $0x2,%r9d
0x000000000288955e: shl $0x2,%r8d
0x0000000002889562: mov %r8d,%r11d
0x0000000002889565: add $0x4,%r11d
0x0000000002889569: mov %ecx,%r8d
0x000000000288956c: jmp 0x0000000002889533
0x000000000288956e: mov %esi,%r10d
0x0000000002889571: add $0xfffffff1,%r10d
0x0000000002889575: mov $0x80000000,%r11d
0x000000000288957b: cmp %r10d,%esi
0x000000000288957e: cmovl %r11d,%r10d
0x0000000002889582: cmp %r10d,%ecx
0x0000000002889585: jge 0x00000000028895bc
0x0000000002889587: sub %r9d,%edx
0x000000000288958a: nopw 0x0(%rax,%rax,1) ;*aload_2
; - Chaining::runUnChained@18 (line 60)
0x0000000002889590: mov %ecx,%r11d
0x0000000002889593: shl %r11d
0x0000000002889596: mov %ecx,%r8d
0x0000000002889599: shl $0x2,%r8d
0x000000000288959d: add %r8d,%r11d
0x00000000028895a0: add %edx,%r11d ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runUnChained@19 (line 60)
0x00000000028895a3: mov %r11d,%edi
0x00000000028895a6: add $0x60,%edi ;*iadd
; - ChainD::doSomething3@6 (line 25)
; - Chaining::runUnChained@27 (line 62)
0x00000000028895a9: add $0x5a,%r11d
0x00000000028895ad: mov %r11d,0xc(%rbx)
0x00000000028895b1: mov %edi,0xc(%rbx) ;*putfield result
; - ChainD::doSomething3@7 (line 25)
; - Chaining::runUnChained@27 (line 62)
0x00000000028895b4: add $0x10,%ecx ;*iinc
; - Chaining::runUnChained@30 (line 58)
0x00000000028895b7: cmp %r10d,%ecx
0x00000000028895ba: jl 0x0000000002889590 ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runUnChained@19 (line 60)
0x00000000028895bc: cmp %esi,%ecx
0x00000000028895be: jge 0x0000000002889606
0x00000000028895c0: mov %ecx,%r11d
0x00000000028895c3: shl $0x2,%r11d
0x00000000028895c7: mov %ecx,%r8d
0x00000000028895ca: shl %r8d
0x00000000028895cd: mov %r8d,%r9d
0x00000000028895d0: add %r11d,%r9d
0x00000000028895d3: sub %r9d,%edi
0x00000000028895d6: xchg %ax,%ax ;*aload_2
; - Chaining::runUnChained@18 (line 60)
0x00000000028895d8: add %r8d,%r11d
0x00000000028895db: add %edi,%r11d
0x00000000028895de: add $0x6,%r11d
0x00000000028895e2: mov %r11d,0xc(%rbx) ;*putfield result
; - ChainD::doSomething3@7 (line 25)
; - Chaining::runUnChained@27 (line 62)
0x00000000028895e6: mov %ecx,%edx
0x00000000028895e8: inc %edx ;*iinc
; - Chaining::runUnChained@30 (line 58)
0x00000000028895ea: cmp %esi,%edx
0x00000000028895ec: jge 0x0000000002889606
0x00000000028895ee: mov %ecx,%r11d
0x00000000028895f1: shl $0x2,%r11d
0x00000000028895f5: shl %ecx
0x00000000028895f7: add $0x4,%r11d
0x00000000028895fb: mov %ecx,%r8d
0x00000000028895fe: add $0x2,%r8d
0x0000000002889602: mov %edx,%ecx
0x0000000002889604: jmp 0x00000000028895d8 ;*if_icmpge
; - Chaining::runUnChained@15 (line 58)
0x0000000002889606: add $0x20,%rsp
0x000000000288960a: pop %rbp
0x000000000288960b: test %eax,-0x26c9611(%rip) # 0x00000000001c0000
; {poll_return}
0x0000000002889611: retq
0x0000000002889612: mov $0xfffffff6,%edx
0x0000000002889617: callq 0x00000000027b7320 ; OopMap{off=284}
;*invokevirtual getB
; - Chaining::runUnChained@1 (line 57)
; {runtime_call}
0x000000000288961c: int3 ;*invokevirtual getB
; - Chaining::runUnChained@1 (line 57)
0x000000000288961d: mov $0xfffffff6,%edx
0x0000000002889622: nop
0x0000000002889623: callq 0x00000000027b7320 ; OopMap{off=296}
;*invokevirtual getC
; - Chaining::runUnChained@4 (line 57)
; {runtime_call}
0x0000000002889628: int3 ;*invokevirtual getC
; - Chaining::runUnChained@4 (line 57)
0x0000000002889629: mov $0xfffffff6,%edx
0x000000000288962e: nop
0x000000000288962f: callq 0x00000000027b7320 ; OopMap{off=308}
;*invokevirtual getD
; - Chaining::runUnChained@7 (line 57)
; {runtime_call}
0x0000000002889634: int3 ;*invokevirtual getD
; - Chaining::runUnChained@7 (line 57)
0x0000000002889635: mov $0xffffff86,%edx
0x000000000288963a: mov %ebx,%ebp
0x000000000288963c: mov %r8d,(%rsp)
0x0000000002889640: data32 xchg %ax,%ax
0x0000000002889643: callq 0x00000000027b7320 ; OopMap{rbp=NarrowOop off=328}
;*aload_2
; - Chaining::runUnChained@18 (line 60)
; {runtime_call}
0x0000000002889648: int3 ;*aload_2
; - Chaining::runUnChained@18 (line 60)
0x0000000002889649: hlt
0x000000000288964a: hlt
0x000000000288964b: hlt
0x000000000288964c: hlt
0x000000000288964d: hlt
0x000000000288964e: hlt
0x000000000288964f: hlt
0x0000000002889650: hlt
0x0000000002889651: hlt
0x0000000002889652: hlt
0x0000000002889653: hlt
0x0000000002889654: hlt
0x0000000002889655: hlt
0x0000000002889656: hlt
0x0000000002889657: hlt
0x0000000002889658: hlt
0x0000000002889659: hlt
0x000000000288965a: hlt
0x000000000288965b: hlt
0x000000000288965c: hlt
0x000000000288965d: hlt
0x000000000288965e: hlt
0x000000000288965f: hlt
[Exception Handler]
[Stub Code]
0x0000000002889660: jmpq 0x00000000028694a0 ; {no_reloc}
[Deopt Handler Code]
0x0000000002889665: callq 0x000000000288966a
0x000000000288966a: subq $0x5,(%rsp)
0x000000000288966f: jmpq 0x00000000027b6f40 ; {runtime_call}
0x0000000002889674: hlt
0x0000000002889675: hlt
0x0000000002889676: hlt
0x0000000002889677: hlt
通过比较输出,可以看出它们基本上是相等的。在这两种情况下,调用都是内联的。
现在人们可以争辩说doSomething
方法是如此琐碎以至于they也已内联,对于更复杂的结果可能会有所不同doSomething
方法。这可能是真的。但是用类似的方法进行快速测试
int doSomething(int i)
{
List<Integer> list = new ArrayList<Integer>(
Arrays.asList(1,2,3,4,5,6,7,8,9,10));
Collections.sort(list);
return list.get(i);
}
表明链式调用的实际内联仍然发生,并且当“内部”方法变得更加复杂时,任何潜在的开销might与中所做的相比,由于链式调用而发生的情况将变得可以忽略不计doSomething
method.