-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
1350 lines (1205 loc) · 318 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Averson</title>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2017-11-04T15:13:45.435Z</updated>
<id>http://yoursite.com/</id>
<author>
<name>Averson</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>(译) Google Kotlin CodeStyle</title>
<link href="http://yoursite.com/2017/11/04/kcodestyle/"/>
<id>http://yoursite.com/2017/11/04/kcodestyle/</id>
<published>2017-11-04T01:23:58.000Z</published>
<updated>2017-11-04T15:13:45.435Z</updated>
<content type="html"><![CDATA[<p>Google 官方 Kotlin 编码风格翻译</p>
<a id="more"></a>
<h2 id="源文件"><a href="#源文件" class="headerlink" title="源文件"></a>源文件</h2><p>所有源文件编码必须是 <code>UTF-8</code>。</p>
<h4 id="命名"><a href="#命名" class="headerlink" title="命名"></a>命名</h4><p>如果源文件只包含一个顶级类(Top-level),文件名应该命名为大写小敏感和 <code>.kt</code> 拓展名。其他情况如果源文件包含多个顶级声明,则选择一个描述文件内容的名称,使用驼峰命名法,并附上名称和 <code>.kt</code> 拓展名。</p>
<blockquote>
<p>Top-level 解释请参考该<a href="https://blog.jetbrains.com/kotlin/2015/06/improving-java-interop-top-level-functions-and-properties/" target="_blank" rel="external">链接</a></p>
</blockquote>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 单一顶级类</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span> </span>{ }</div><div class="line"></div><div class="line"><span class="comment">// Bar.kt </span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Bar</span> </span>{ }</div><div class="line"><span class="function"><span class="keyword">fun</span> Runnabel.<span class="title">toBar</span><span class="params">()</span></span> : Bar = <span class="comment">// ...</span></div><div class="line"></div><div class="line"><span class="comment">// Map.kt -> 描述文件内容的命名</span></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="type"><T, O></span> Set<span class="type"><T></span>.<span class="title">map</span><span class="params">(func: (<span class="type">T</span>)</span></span> -> O): List<O> = <span class="comment">// …</span></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="type"><T, O></span> List<span class="type"><T></span>.<span class="title">map</span><span class="params">(func: (<span class="type">T</span>)</span></span> -> O): List<O> = <span class="comment">// …</span></div></pre></td></tr></table></figure>
<h4 id="特殊编码"><a href="#特殊编码" class="headerlink" title="特殊编码"></a>特殊编码</h4><h4 id="空格"><a href="#空格" class="headerlink" title="空格"></a>空格</h4><p>除了换行,它是 <strong>ASCII 水平方向字符 (0x20) </strong> 唯一能出现在源文件中的空格。</p>
<blockquote>
<p>0x20 表示空格</p>
</blockquote>
<p>这意味着:</p>
<ul>
<li>所有其他空格字符在字符串和字符源文本中均被转义</li>
<li>制表符<strong>不</strong>用于缩进</li>
</ul>
<blockquote>
<p>源文本(literal),是指程序源代码中用来表示固定的值的符号序列。例如在大多数语言中,引号包围的字符序列即为字符串源文本(string literal),表示一个特定的字符串值。</p>
</blockquote>
<h4 id="特殊的转义序列"><a href="#特殊的转义序列" class="headerlink" title="特殊的转义序列"></a>特殊的转义序列</h4><p>对于任何具有特殊转义的字符(\b、\n、\r、\t、\’、\ 和 \$)使用的是对应的 <code>Unicode</code> 编码(例如:Line Feed : \n = u000a)转义。</p>
<h4 id="Non-ASCII-字符"><a href="#Non-ASCII-字符" class="headerlink" title="Non-ASCII 字符"></a>Non-ASCII 字符</h4><p>对于剩下的 <code>Non-ASCII</code> 字符,要么使用实际的 <code>Unicode</code> 字符(例如:∞),要么使用等效的 <code>Unicode</code> 转义(如:\u221e)。这个选择只取决于代码易读和理解。对于任何位置的可打印的字符,都不建议使用 <code>Unicode</code> 转义,尤其在字符串源文本和注释。</p>
<table>
<thead>
<tr>
<th>例子</th>
<th>建议</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>val unitAbbrev = "μs"</code></td>
<td>最好的:即使没有评论,也非常清楚</td>
</tr>
<tr>
<td><code>val unitAbbrev = "\u03bcs" // μs"</code></td>
<td>较差的:没有理由在一个可打印字符字符中使用转义字符表示</td>
</tr>
<tr>
<td><code>val unitAbbrev = "\u03bcs"</code></td>
<td>较差的:代码阅读者不知道这是一个什么概念</td>
</tr>
<tr>
<td><code>return "\ufeff" + content</code></td>
<td>良好的:对不可打印字符使用转义,必要时可以进行注释。</td>
</tr>
</tbody>
</table>
<h2 id="结构"><a href="#结构" class="headerlink" title="结构"></a>结构</h2><p>一个 <code>.kt</code> 文件由以下组成:</p>
<ol>
<li>版权和许可证(可选)</li>
<li>File-Level 注解</li>
<li>包名声明</li>
<li>导包声明</li>
<li>Top-level 声明</li>
</ol>
<p>每个部分均用换行来分隔。</p>
<h4 id="版权-许可证"><a href="#版权-许可证" class="headerlink" title="版权 / 许可证"></a>版权 / 许可证</h4><p>如果文件中包含版权或许可证,则应将其放在多行注释的最上面。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/*</span></div><div class="line"> * Copyright 2017 Google, Inc.</div><div class="line"> *</div><div class="line"> * ...</div><div class="line"> */</div></pre></td></tr></table></figure>
<p>不要使用 <a href="https://kotlinlang.org/docs/reference/kotlin-doc.html" target="_blank" rel="external">KDoc-style</a> 或者单行注释。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * Copyright 2017 Google, Inc.</div><div class="line"> *</div><div class="line"> * ...</div><div class="line"> */</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Copyright 2017 Google, Inc.</span></div><div class="line"><span class="comment">//</span></div><div class="line"><span class="comment">// ...</span></div></pre></td></tr></table></figure>
<h4 id="File-Level-注解"><a href="#File-Level-注解" class="headerlink" title="File-Level 注解"></a>File-Level 注解</h4><p>如果使用 <code>kotlin</code> 的 <code>File-Level</code> 注解请放在头部和包名声明之间。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/*</span></div><div class="line"> * Copyright 2017 Google Inc.</div><div class="line"> */</div><div class="line"></div><div class="line"><span class="meta">@file:JvmName</span>(<span class="string">"ViewUtils"</span>)</div><div class="line"></div><div class="line"><span class="keyword">package</span> domain.util</div></pre></td></tr></table></figure>
<h4 id="包名声明"><a href="#包名声明" class="headerlink" title="包名声明"></a>包名声明</h4><p>包名声明不受行列限制并且 使用 <code>line-wrap</code> 模式。</p>
<blockquote>
<p>line-wrap 参考<a href="https://zh.wikipedia.org/wiki/%E8%87%AA%E5%8A%A8%E6%8D%A2%E8%A1%8C" target="_blank" rel="external">维基百科定义</a></p>
</blockquote>
<h4 id="导包声明"><a href="#导包声明" class="headerlink" title="导包声明"></a>导包声明</h4><p>类、函数和属性的导入语句组合在一个单独列表中,并按 <code>ASCII</code> 排序。</p>
<p><strong>不允许</strong>使用通配符导入。</p>
<p>和”包名声明”一样,导包声明不受行列限制并且 使用 <code>line-wrap</code> 模式。</p>
<h4 id="Top-level-声明"><a href="#Top-level-声明" class="headerlink" title="Top-level 声明"></a>Top-level 声明</h4><p>一个 <code>.kt</code> 文件可以在顶层声明一个或多个类型、函数、属性或类型别名。</p>
<p>文件中内容应该围绕一个主题。</p>
<p>比如只有一个公开类型(public)或一组拓展函数,对于多个接收者都应该执行同样的操作。</p>
<p>比如(<code>AreaMaths.kt</code>):</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> Circle.<span class="title">area</span><span class="params">()</span></span> : <span class="built_in">Double</span> {</div><div class="line"> <span class="comment">// ...</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">fun</span> Rectangle.<span class="title">area</span><span class="params">()</span></span> : <span class="built_in">Double</span> {</div><div class="line"> <span class="comment">// ...</span></div><div class="line">}</div></pre></td></tr></table></figure>
<p>不相关的声明应该分离开来放到自己的文件中,并且在一个文件内公开声明应该最小化尽可能的保持内聚。</p>
<p>没有文件内容的数量和排序限制。</p>
<p>源文件一般是从上至下阅读,所以尽可能按这个顺序反应出相关重要的内容。因此不同的文件可能会选择不同的排序。比如说,一个文件可能包含 100 个属性,10 个函数,1 个类。最重要的一点,每个类应该以某种逻辑去排序它的成员,维护者应该要能解释这种排序逻辑。再如:新函数不应该添加在类的末尾,因为这样是日期排序,这不是一个逻辑排序。</p>
<h4 id="成员变量排序"><a href="#成员变量排序" class="headerlink" title="成员变量排序"></a>成员变量排序</h4><p>类成员变量的排序和 Top-level 声明一致</p>
<h2 id="格式"><a href="#格式" class="headerlink" title="格式"></a>格式</h2><h4 id="大括号"><a href="#大括号" class="headerlink" title="大括号"></a>大括号</h4><p><code>when</code> 分支和不具有 <code>if/else</code> 的分支且适用于单行的语句不需要大括号。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (string.isEmpty()) <span class="keyword">return</span></div><div class="line"></div><div class="line"><span class="keyword">when</span>(value) {</div><div class="line"> () -> <span class="keyword">return</span></div><div class="line">}</div></pre></td></tr></table></figure>
<p>其他情况都需要大括号,比如说 <code>if</code>、<code>for</code>、<code>when</code>、<code>do</code> 和 <code>while</code>。即使语句体是空语句或者只有一句也需要大括号。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (string.isEmpty()) </div><div class="line"> <span class="keyword">return</span> <span class="comment">// WRONG!</span></div><div class="line"></div><div class="line"><span class="keyword">if</span> (string.isEmpty()) {</div><div class="line"> <span class="keyword">return</span> <span class="comment">// OKay</span></div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="非空块"><a href="#非空块" class="headerlink" title="非空块"></a>非空块</h4><p>对于非空块和块状结构,大括号遵循 Kernighan(K) 和 Ritchie(R) 风格 (<a href="http://www.codinghorror.com/blog/2012/07/new-programming-jargon.html" target="_blank" rel="external">Egyptian brackets</a>):</p>
<ul>
<li>左大括号前不换行</li>
<li>左大括号后换行</li>
<li>右大括号前换行</li>
<li>如果右大括号是一个语句、函数体或类的终止,则右大括号后换行; 否则不换行。例如,如果右大括号后面是 else 或逗号,则不换行。</li>
</ul>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">return</span> Runnable {</div><div class="line"> <span class="keyword">while</span> (condition()) {</div><div class="line"> foo()</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">return</span> <span class="keyword">object</span> : MyClass() {</div><div class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">foo</span><span class="params">()</span></span> {</div><div class="line"> <span class="keyword">if</span> (condition()) {</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> something()</div><div class="line"> } <span class="keyword">catch</span> (e: ProblemException) {</div><div class="line"> recover()</div><div class="line"> }</div><div class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (otherCondition()) {</div><div class="line"> somethingElse()</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> lastThing()</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>后续给出枚举类的一些例外。</p>
<h4 id="空块"><a href="#空块" class="headerlink" title="空块"></a>空块</h4><p>空语块或类似空语块的必须是 K&R 风格</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">try</span> {</div><div class="line"> doSomething()</div><div class="line">} <span class="keyword">catch</span> (e: Exception) {} <span class="comment">// WRONG!!</span></div><div class="line"></div><div class="line"><span class="keyword">try</span> {</div><div class="line"> doSomething()</div><div class="line">} <span class="keyword">catch</span> (e: Exception) {</div><div class="line">} <span class="comment">// OKay</span></div></pre></td></tr></table></figure>
<h4 id="表达式"><a href="#表达式" class="headerlink" title="表达式"></a>表达式</h4><p>只有在单行能够完成整个表达式的 <code>if/else</code> 语句才适用省略大括号。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> value = <span class="keyword">if</span> (string.isEmpty()) <span class="number">0</span> <span class="keyword">else</span> <span class="number">1</span> <span class="comment">// OKay</span></div><div class="line"></div><div class="line"><span class="keyword">val</span> value = <span class="keyword">if</span> (string.isEmpty()) <span class="comment">// WRONG!!</span></div><div class="line"> <span class="number">0</span></div><div class="line"> <span class="keyword">else</span></div><div class="line"> <span class="number">1</span></div><div class="line"></div><div class="line"><span class="keyword">val</span> value = <span class="keyword">if</span> (string.isEmpty()) { <span class="comment">// OKay</span></div><div class="line"> <span class="number">0</span></div><div class="line">} <span class="keyword">else</span> {</div><div class="line"> <span class="number">1</span></div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="缩进"><a href="#缩进" class="headerlink" title="缩进"></a>缩进</h4><p>每当开始一个新的块,缩进增加 4 个空格。</p>
<h4 id="每行一个语句"><a href="#每行一个语句" class="headerlink" title="每行一个语句"></a>每行一个语句</h4><p>每个语句后面都有一个换行符。不使用分号。</p>
<h4 id="自动换行"><a href="#自动换行" class="headerlink" title="自动换行"></a>自动换行</h4><p>一般情况下,一行长代码为了避免超出列限制(100个字符)而被分为多行,我们称之为自动换行(line-wrapping)。除了以下指出的情况,任何超出这些限制的必须按照说明换行。</p>
<ul>
<li>不可能遵循列限制的行(例如,KDoc中的一个长URL)</li>
<li>包和导入语句</li>
<li>在注释中可以剪切并粘贴到 shell 中的命令行。</li>
</ul>
<h4 id="从哪里断开"><a href="#从哪里断开" class="headerlink" title="从哪里断开"></a>从哪里断开</h4><p>自动换行的基本准则是:更倾向于在<strong>更高的语法级别</strong>处断开。</p>
<ol>
<li>如果在非赋值运算符处断开,那么在该符号前断开( +,它将位于下一行)。这条规则也适用以下类操作符语法:<ul>
<li>点分隔符(.)</li>
<li>双冒号(::)</li>
</ul>
</li>
<li>当赋值运算符在一行断开,符号后面会出现断开。</li>
<li>一个方法或者构造函数的参数名依然在左括号后面</li>
<li>逗号(,)与其前面的内容留在同一行。</li>
<li>lambda 的 -> 符号和参数列表在同一行</li>
</ol>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> longString = <span class="string">"some long text"</span></div><div class="line"> + <span class="string">"some more long text"</span></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">longNameFunc</span><span class="params">(param1: <span class="type">string</span>, param2: <span class="type">string</span></span></span></div><div class="line"> <span class="type">param3</span>: <span class="type">string</span>, param4: <span class="type">string</span>) {</div><div class="line">}</div><div class="line"></div><div class="line">Flowable</div><div class="line"> .fromCallable {}</div><div class="line"> .subscribeOn()</div><div class="line"> .map(DataMapper::toData)</div><div class="line"> .subscribe()</div><div class="line"></div><div class="line"><span class="keyword">val</span> list: List<String> thisIsLongNamingVal</div><div class="line"> = listOf(<span class="string">""</span>)</div></pre></td></tr></table></figure>
<blockquote>
<p>注意:换行的主要目的是代码更清晰,而不一定是最小行数的代码。</p>
</blockquote>
<h4 id="继续缩进"><a href="#继续缩进" class="headerlink" title="继续缩进"></a>继续缩进</h4><p>当自动换行时,第一行后的每一行至少比第一行多缩进 4 个空格(注意:制表符不用于缩进)。当存在连续自动换行时,缩进可能会多缩进不止 4 个空格(语法元素存在多级时)。一般而言,两个连续行使用相同的缩进当且仅当它们开始于同级语法元素。</p>
<h4 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h4><p>当一个函数签名不适合一行时,将每个参数声明分解到它自己的行上。在这种格式中定义的参数应该使用继续缩进(+8)。闭括号(<code>)</code>)和返回类型被放在它们自己的行上,没有附加的缩进。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> <span class="type"><T></span> Iterable<span class="type"><T></span>.<span class="title">joinToString</span><span class="params">(</span></span></div><div class="line"> separator: <span class="type">CharSequence</span> = <span class="string">", "</span>,</div><div class="line"> prefix: <span class="type">CharSequence</span> = <span class="string">""</span>,</div><div class="line"> postfix: <span class="type">CharSequence</span> = <span class="string">""</span></div><div class="line">): String {</div><div class="line"> <span class="comment">// …</span></div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="表达式函数"><a href="#表达式函数" class="headerlink" title="表达式函数"></a>表达式函数</h4><p>当一个函数只包含一个表达式时,它可以被表示为一个表达式函数。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">toString</span><span class="params">()</span></span>: String {</div><div class="line"> <span class="keyword">return</span> <span class="string">"Hey"</span></div><div class="line">}</div></pre></td></tr></table></figure>
<p>替换为</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">toString</span><span class="params">()</span></span>: String = <span class="string">"Hey"</span></div></pre></td></tr></table></figure>
<p>表达式函数不应该使用换行导致用两行表示。</p>
<p>如果表达式函数需要拓展则需要换行,使用正常的函数体、返回声明和正常的表达式换行规则。</p>
<h4 id="属性"><a href="#属性" class="headerlink" title="属性"></a>属性</h4><p>当一个属性初始化器不适用一行时,在等号(=)之后之换行并使用继续缩进规则。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">private</span> <span class="keyword">val</span> defaultCharset: Charset? =</div><div class="line"> EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)</div></pre></td></tr></table></figure>
<p>属性声明一个 getter 或者 setter 函数应该在它们所在行前加一个普通缩进(+4)。<br>使用与函数相同规则进行格式化。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> directory: File? = <span class="literal">null</span></div><div class="line"> <span class="keyword">set</span>(value) {</div><div class="line"> <span class="comment">// …</span></div><div class="line"> }</div><div class="line"> <span class="keyword">get</span>() {</div><div class="line"> <span class="keyword">return</span> value</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>对于只读属性可以使用更简洁的语法,适用于一行。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> defaultExtension: String <span class="keyword">get</span>() = <span class="string">"kt"</span></div></pre></td></tr></table></figure>
<h2 id="空白"><a href="#空白" class="headerlink" title="空白"></a>空白</h2><h4 id="垂直方向"><a href="#垂直方向" class="headerlink" title="垂直方向"></a>垂直方向</h4><p>垂直方向空白行出现原则:</p>
<ul>
<li>类的连续成员: 属性、构造函数、函数、嵌套类等等。<ul>
<li>例外:</li>
<li>两个连续属性之间的空行(在它们之间没有其他的代码)是可选的。</li>
<li>如果存在,则使用这些空白行来创建属性的逻辑分组,并将属性与它们的相关属性关联起来。</li>
</ul>
</li>
<li>在函数体内,语句的逻辑分组间使用空行。</li>
<li>类内的第一个成员变量前或最后一个成员变量后的空行是可选的(既不鼓励也不反对这样做,视个人喜好而定)。</li>
<li>根据本文档的其他部分(如“结构”一节)的要求。</li>
</ul>
<p>允许使用多个连续的空行,但不鼓励或不需要。</p>
<h4 id="水平方向"><a href="#水平方向" class="headerlink" title="水平方向"></a>水平方向</h4><p>除了语言需求和其它规则,并且除了文字,注释和 Kdoc 用到单个空格,单个 ASCII 空格也可以出现在以下几个地方:</p>
<ol>
<li>分隔任何保留字与紧随其后的左括号(<code>(</code>)(如:if、for 和 catch)。</li>
</ol>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// WRONG!</span></div><div class="line"><span class="keyword">for</span>(i <span class="keyword">in</span> <span class="number">0.</span><span class="number">.1</span>) {</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// OKay</span></div><div class="line"><span class="keyword">for</span> (i <span class="keyword">in</span> <span class="number">0.</span><span class="number">.1</span>) {</div><div class="line">}</div></pre></td></tr></table></figure>
<ol>
<li>分隔任何保留字与其前面的右大括号(<code>}</code>) (如:else, catch)。</li>
</ol>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// WRONG!</span></div><div class="line">}<span class="keyword">else</span> {</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// OKay</span></div><div class="line">} <span class="keyword">else</span> {</div><div class="line">}</div></pre></td></tr></table></figure>
<ol>
<li>任何左大括号(<code>{</code>)前</li>
</ol>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// WRONG!</span></div><div class="line"><span class="keyword">if</span> (list.isEmpty()){</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// OKay</span></div><div class="line"><span class="keyword">if</span> (list.isEmpty()) {</div><div class="line">}</div></pre></td></tr></table></figure>
<ol>
<li>在任何二元或三元运算符的两侧</li>
</ol>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// WRONG!</span></div><div class="line"><span class="keyword">val</span> two = <span class="number">1</span>+<span class="number">1</span></div><div class="line"></div><div class="line"><span class="comment">// OKay</span></div><div class="line"><span class="keyword">val</span> two = <span class="number">1</span> + <span class="number">1</span></div></pre></td></tr></table></figure>
<p>同样使用于 “类似操作符” 语法:</p>
<ul>
<li>lambda 表达式(<code>-></code>)</li>
</ul>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// WRONG!</span></div><div class="line">ints.map { value->value.toString() }</div><div class="line"></div><div class="line"><span class="comment">// OKay</span></div><div class="line">ints.map { value -> value.toString() }</div></pre></td></tr></table></figure>
<p>但是不适用于:</p>
<ul>
<li>双冒号(<code>::</code>)成员引用语法</li>
</ul>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// WRONG! </span></div><div class="line"><span class="keyword">val</span> toString = Any :: toString</div><div class="line"></div><div class="line"><span class="comment">// OKay</span></div><div class="line"><span class="keyword">val</span> toString = Any::toString</div></pre></td></tr></table></figure>
<ul>
<li>逗号(<code>.</code>)</li>
</ul>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// WRONG! </span></div><div class="line">it . toString()</div><div class="line"></div><div class="line"><span class="comment">// OKay</span></div><div class="line">it.toString()</div></pre></td></tr></table></figure>
<ol>
<li>在一个冒号前使用(<code>:</code>)空格,仅在类使用基类或接口,或者用于 <code>where</code> <a href="https://kotlinlang.org/docs/reference/generics.html#generic-constraints" target="_blank" rel="external">泛型约束</a>时候。</li>
</ol>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// WRONG!</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span>: <span class="type">Runnable</span></span></div><div class="line"></div><div class="line"><span class="comment">// Okay</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span> : <span class="type">Runnable</span></span></div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// WRONG!</span></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="type"><T></span> <span class="title">max</span><span class="params">(a: <span class="type">T</span>, b: <span class="type">T</span>)</span></span> where T: Comparable<T></div><div class="line"></div><div class="line"><span class="comment">// Okay</span></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="type"><T></span> <span class="title">max</span><span class="params">(a: <span class="type">T</span>, b: <span class="type">T</span>)</span></span> where T : Comparable<T></div></pre></td></tr></table></figure>
<ol>
<li>逗号(<code>,</code>)或冒号(<code>:</code>)后。</li>
</ol>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// WRONG!</span></div><div class="line"><span class="keyword">val</span> oneAndTwo = listOf(<span class="number">1</span>,<span class="number">2</span>)</div><div class="line"></div><div class="line"><span class="comment">// Okay</span></div><div class="line"><span class="keyword">val</span> oneAndTwo = listOf(<span class="number">1</span>, <span class="number">2</span>)</div><div class="line"></div><div class="line"><span class="comment">// WRONG!</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span> :<span class="type">Runnable</span></span></div><div class="line"></div><div class="line"><span class="comment">// Okay</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span> : <span class="type">Runnable</span></span></div></pre></td></tr></table></figure>
<ol>
<li>如果在一条语句后做注释,则双斜杠(//)两边都要空格。这里可以允许多个空格,但没有必要。</li>
</ol>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// WRONG!</span></div><div class="line"><span class="keyword">var</span> debugging = <span class="literal">false</span><span class="comment">//disabled by default</span></div><div class="line"></div><div class="line"><span class="comment">// Okay</span></div><div class="line"><span class="keyword">var</span> debugging = <span class="literal">false</span> <span class="comment">// disabled by default</span></div></pre></td></tr></table></figure>
<blockquote>
<p>注意:这个规则并不要求或禁止一行的开关或结尾需要额外的空格,只对内部空格做要求。</p>
</blockquote>
<h2 id="特殊结构"><a href="#特殊结构" class="headerlink" title="特殊结构"></a>特殊结构</h2><h4 id="枚举类"><a href="#枚举类" class="headerlink" title="枚举类"></a>枚举类</h4><p>一个没有函数的枚举,且它的常量没有文档,可以随意地格式化为单行。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">enum</span> <span class="class"><span class="keyword">class</span> <span class="title">Answer</span> </span>{ YES, NO, MAYBE }</div></pre></td></tr></table></figure>
<p>当枚举中的常量被放在单独的行上时,它们之间不需要空白行,除非它们定义了一个正体。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">enum</span> <span class="class"><span class="keyword">class</span> <span class="title">Answer</span> </span>{</div><div class="line"> YES,</div><div class="line"> NO,</div><div class="line"></div><div class="line"> MAYBE {</div><div class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">toString</span><span class="params">()</span></span> = <span class="string">"""¯\_(ツ)_/¯"""</span></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>因为 enum 类是类,所以所有其他用于格式化类的规则都适用。</p>
<h4 id="注解"><a href="#注解" class="headerlink" title="注解"></a>注解</h4><p>在构造注释之前,成员或类型注释被放在单独的行上。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Retention(SOURCE)</span></div><div class="line"><span class="meta">@Target(FUNCTION, PROPERTY_SETTER, FIELD)</span></div><div class="line"><span class="keyword">annotation</span> <span class="class"><span class="keyword">class</span> <span class="title">Global</span></span></div></pre></td></tr></table></figure>
<p>没有参数的注释可以放在一行上。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@JvmField</span> <span class="meta">@Volatile</span></div><div class="line"><span class="keyword">var</span> disposable: Disposable? = <span class="literal">null</span></div></pre></td></tr></table></figure>
<p>当只有一个没有参数的注释时,可以放在一行上。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Volatile</span> <span class="keyword">var</span> disposable: Disposable? = <span class="literal">null</span></div><div class="line"></div><div class="line"><span class="meta">@Test</span> <span class="function"><span class="keyword">fun</span> <span class="title">selectAll</span><span class="params">()</span></span> {</div><div class="line"> <span class="comment">// …</span></div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="隐式返回-属性类型"><a href="#隐式返回-属性类型" class="headerlink" title="隐式返回/属性类型"></a>隐式返回/属性类型</h4><p>如果表达式函数体或属性初始化器是标量值,或者返回类型可以从正文中清楚地推断出来,那么就可以省略它。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">toString</span><span class="params">()</span></span>: String = <span class="string">"Hey"</span></div><div class="line"><span class="comment">// 转换为</span></div><div class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">toString</span><span class="params">()</span></span> = <span class="string">"Hey"</span></div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">private</span> <span class="keyword">val</span> ICON: Icon = IconLoader.getIcon(<span class="string">"/icons/kotlin.png"</span>)</div><div class="line"><span class="comment">// becomes</span></div><div class="line"><span class="keyword">private</span> <span class="keyword">val</span> ICON = IconLoader.getIcon(<span class="string">"/icons/kotlin.png"</span>)</div></pre></td></tr></table></figure>
<p>在编写库时,当它是公共 <code>API</code> 的一部分时,保留显式类型声明,方便框架使用者调用。</p>
<h2 id="命名-1"><a href="#命名-1" class="headerlink" title="命名"></a>命名</h2><p>识符只能使用 ASCII 字母和数字,因此每个有效的标识符名称都能匹配正则表达式 <code>\w+</code>。</p>
<p>在 <code>Google</code> 其它编程语言风格中使用的特殊前缀或后缀,如<code>name_</code>, <code>mName</code>, <code>s_name</code> 和 <code>kName</code>,在 <code>Java</code> 编程风格中都不再使用。</p>
<p>特殊的前缀或后缀,如示例中所见的:<code>name_</code>,<code>mName</code>、<code>s_name</code> 和 <code>kName</code>,除了在备份属性(参见“<a href="https://android.github.io/kotlin-guides/style.html#backing-properties" target="_blank" rel="external">支持属性</a>”),这些都一律不使用。</p>
<h4 id="包名"><a href="#包名" class="headerlink" title="包名"></a>包名</h4><p>包名都是小写的,连续的单词简单地连接在一起(没有下划线)。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Okay</span></div><div class="line"><span class="keyword">package</span> com.example.deepspace</div><div class="line"><span class="comment">// WRONG!</span></div><div class="line"><span class="keyword">package</span> com.example.deepSpace</div><div class="line"><span class="comment">// WRONG!</span></div><div class="line"><span class="keyword">package</span> com.example.deep_space</div></pre></td></tr></table></figure>
<h4 id="类名"><a href="#类名" class="headerlink" title="类名"></a>类名</h4><p>类名是用使用驼峰命名,通常是名词或名词短语。例如:<code>Character</code> 或 <code>ImmutableList</code>。接口名也可能是名词或名词短语(例如:<code>List</code>),但我的有时是形容词或形容词短语(例如:<code>Readable</code>)。</p>
<h4 id="函数名"><a href="#函数名" class="headerlink" title="函数名"></a>函数名</h4><p>函数名用使用驼峰命名,通常是动词或动词短语。例如:<code>sendMessage</code> 或 <code>stop</code>。下划线允许出现在测试函数名中,以分离名称的逻辑组件。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Test</span> <span class="function"><span class="keyword">fun</span> <span class="title">pop_emptyStack</span><span class="params">()</span></span> {</div><div class="line"> <span class="comment">// …</span></div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="常量命名"><a href="#常量命名" class="headerlink" title="常量命名"></a>常量命名</h4><p>常量名命名模式为 <code>CONSTANT_CASE</code>,全部字母大写,用下划线分隔单词。那,到底什么算是一个常量?</p>
<p>常量是使用 <code>val</code> 定义并且没有 getter,其内容是不可更改的,其函数没有副作用。这包括不可变类型和不可变集合的不可变类型,如果它们标记为 const 则和标量和字符串一样。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">const <span class="keyword">val</span> NUMBER = <span class="number">5</span></div><div class="line"><span class="keyword">val</span> NAMES = listOf(<span class="string">"Alice"</span>, <span class="string">"Bob"</span>)</div><div class="line"><span class="keyword">val</span> AGES = mapOf(<span class="string">"Alice"</span> to <span class="number">35</span>, <span class="string">"Bob"</span> to <span class="number">32</span>)</div><div class="line"><span class="keyword">val</span> COMMA_JOINER = Joiner.on(<span class="string">','</span>) <span class="comment">// Joiner is immutable</span></div><div class="line"><span class="keyword">val</span> EMPTY_ARRAY = arrayOf<SomeMutableType>()</div></pre></td></tr></table></figure>
<p>这些名字通常是名词或名词短语。常量值只能在一个 <code>object class</code> 定义或 <code>top-level</code> 中定义。否则,在类内部定义一个常量但定义的值必须使用一个非常量名。标量值的常量必须使用 <code>const</code> 修饰符。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">const <span class="keyword">val</span> HTTP_OK = <span class="number">200</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>{}</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">object</span> A {</div><div class="line"> const <span class="keyword">val</span> HTTP_OK = <span class="number">200</span></div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="非常量命名"><a href="#非常量命名" class="headerlink" title="非常量命名"></a>非常量命名</h4><p>非常量的名称使用骆驼拼写法。应用于实例属性、本地属性和参数名。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> variable = <span class="string">"var"</span></div><div class="line"><span class="keyword">val</span> nonConstScalar = <span class="string">"non-const"</span></div><div class="line"><span class="keyword">val</span> mutableCollection: MutableSet<String> = HashSet()</div><div class="line"><span class="keyword">val</span> mutableElements = listOf(mutableInstance)</div><div class="line"><span class="keyword">val</span> mutableValues = mapOf(<span class="string">"Alice"</span> to mutableInstance, <span class="string">"Bob"</span> to mutableInstance2)</div><div class="line"><span class="keyword">val</span> logger = Logger.getLogger(MyClass::<span class="class"><span class="keyword">class</span>.<span class="title">java</span>.<span class="title">name</span>)</span></div><div class="line"><span class="keyword">val</span> nonEmptyArray = arrayOf(<span class="string">"these"</span>, <span class="string">"can"</span>, <span class="string">"change"</span>)</div></pre></td></tr></table></figure>
<p>这些名字通常是名词或名词短语。</p>
<h4 id="备份属性"><a href="#备份属性" class="headerlink" title="备份属性"></a>备份属性</h4><p>当需要一个支持属性时,它的名字应该与真正的属性完全匹配,区别在于有一个下划线。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">private</span> <span class="keyword">var</span> _table: Map<String, <span class="built_in">Int</span>>? = <span class="literal">null</span></div><div class="line"></div><div class="line"><span class="keyword">val</span> table: Map<String, <span class="built_in">Int</span>></div><div class="line"> <span class="keyword">get</span>() {</div><div class="line"> <span class="keyword">if</span> (_table == <span class="literal">null</span>) {</div><div class="line"> _table = HashMap()</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> _table ?: <span class="keyword">throw</span> AssertionError()</div><div class="line"> }</div></pre></td></tr></table></figure>
<h4 id="类型变量名"><a href="#类型变量名" class="headerlink" title="类型变量名"></a>类型变量名</h4><p>类型变量可用以下两种风格之一进行命名:</p>
<ul>
<li>单个的大写字母,后面可以跟一个数字(如:<code>E</code>, <code>T</code>, <code>X</code>, <code>T2</code>)。</li>
<li>以类命名方式,后面加个大写的T(如:<code>RequestT</code>, <code>FooBarT</code>)。</li>
</ul>
<h4 id="驼峰式命名法-CamelCase"><a href="#驼峰式命名法-CamelCase" class="headerlink" title="驼峰式命名法(CamelCase)"></a>驼峰式命名法(CamelCase)</h4><p>驼峰式命名法分大驼峰式命名法(UpperCamelCase)和小驼峰式命名法(lowerCamelCase)。 有时,我们有不只一种合理的方式将一个英语词组转换成驼峰形式,如缩略语或不寻常的结构(例如”IPv6”或”iOS”)。Google指定了以下的转换方案。</p>
<p>名字从散文形式(prose form)开始:</p>
<ol>
<li>把短语转换为纯ASCII码,并且移除任何单引号。例如:”Müller’s algorithm”将变成”Muellers algorithm”。</li>
<li>把这个结果切分成单词,在空格或其它标点符号(通常是连字符)处分割开。<ul>
<li>推荐:如果某个单词已经有了常用的驼峰表示形式,按它的组成将它分割开(如”AdWords”将分割成”ad words”)。 需要注意的是”iOS”并不是一个真正的驼峰表示形式,因此该推荐对它并不适用。</li>
</ul>
</li>
<li>现在将所有字母都小写(包括缩写),然后将单词的第一个字母大写:<ul>
<li>每个单词的第一个字母都大写,来得到大驼峰式命名。</li>
<li>除了第一个单词,每个单词的第一个字母都大写,来得到小驼峰式命名。</li>
</ul>
</li>
<li>最后将所有的单词连接起来得到一个标识符。</li>
</ol>
<p>示例:</p>
<table>
<thead>
<tr>
<th>Prose form</th>
<th>Correct</th>
<th>Incorrect</th>
</tr>
</thead>
<tbody>
<tr>
<td>“XML HTTP request”</td>
<td>XmlHttpRequest</td>
<td>XMLHTTPRequest</td>
</tr>
<tr>
<td>“new customer ID”</td>
<td>newCustomerId</td>
<td>newCustomerID</td>
</tr>
<tr>
<td>“inner stopwatch”</td>
<td>innerStopwatch</td>
<td>innerStopWatch</td>
</tr>
<tr>
<td>“supports IPv6 on iOS?”</td>
<td>supportsIpv6OnIos</td>
<td>supportsIPv6OnIOS</td>
</tr>
<tr>
<td>“YouTube importer”</td>
<td>YouTubeImporter or YoutubeImporter* </td>
</tr>
</tbody>
</table>
<p>加星号处表示可以,但不推荐。</p>
<blockquote>
<p>注意:在英语中,某些带有连字符的单词形式不唯一。例如:”nonempty” 和 ”non-empty” 都是正确的,因此方法名 <code>checkNonempty</code> 和<code>checkNonEmpty</code> 也都是正确的。</p>
</blockquote>
<h2 id="文档"><a href="#文档" class="headerlink" title="文档"></a>文档</h2><h4 id="文档格式"><a href="#文档格式" class="headerlink" title="文档格式"></a>文档格式</h4><p>KDoc 块的基本格式如下所示:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * Multiple lines of KDoc text are written here,</div><div class="line"> * wrapped normally…</div><div class="line"> */</div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">method</span><span class="params">(arg: <span class="type">String</span>)</span></span> {</div><div class="line"> <span class="comment">// …</span></div><div class="line">}</div></pre></td></tr></table></figure>
<p>单行注释例子:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/** An especially short bit of KDoc. */</span></div></pre></td></tr></table></figure>
<p>基本形式是可以接受的。</p>
<p>当整个 KDoc 块(包括注释标记)可以放在一行上时,可以替换单行表单。</p>
<p>请注意,只有在没有诸如 @return 这样的块标记时才会这样做。</p>
<h4 id="段落"><a href="#段落" class="headerlink" title="段落"></a>段落</h4><p>空行(即:只包含最左侧星号的行) - 出现在段落和段落标记组之前的标签。</p>
<h4 id="块标签"><a href="#块标签" class="headerlink" title="块标签"></a>块标签</h4><p>标准 KDoc 出现顺序 <code>@constructor</code>, <code>@receiver</code>, <code>@param</code>, <code>@property</code>, <code>@return</code>, <code>@throws</code>, <code>@see</code>,出现这些都不会出现在空的描述。</p>
<p>当一个块标记不适合一行时,下一行从 @ 的位置缩进 8 个空格。</p>
<h4 id="摘要片段"><a href="#摘要片段" class="headerlink" title="摘要片段"></a>摘要片段</h4><p>每个类或成员的 KDoc 以一个简短的摘要片段开始。这个片段是非常重要的,在某些情况下,它是唯一出现的文本,比如在类和方法索引中。</p>
<p>这只是一个小片段,可以是一个名词短语或动词短语,但不是一个完整的句子。它不会以 “A Foo is a…” 或 “This method returns…” 开头, 它也不会是一个完整的祈使句,如 “Save the record…”。然而,由于开头大写及被加了标点,它看起来就像是个完整的句子。</p>
<h4 id="哪里需要使用-KDoc"><a href="#哪里需要使用-KDoc" class="headerlink" title="哪里需要使用 KDoc"></a>哪里需要使用 KDoc</h4><p>至少在每个 public 类及它的每个 public 和 protected 成员变量处使用 KDoc,以下是一些例外:</p>
<ul>
<li>不言自明的方法,对于简单明显的方法如 getFoo,KDoc是可选的(即:是可以不写的)。这种情况下除了写 “Returns the foo”,确实也没有什么值得写了。</li>
<li>如果有一些相关信息是需要读者了解的,那么以上的例外不应作为忽视这些信息的理由。例如,对于方法名 getCanonicalName,就不应该忽视文档说明,因为读者很可能不知道词语 canonical name 指的是什么。</li>
<li>如果一个方法重写了超类中的方法,那么 KDoc 并非必需的。</li>
</ul>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul>
<li><a href="https://android.github.io/kotlin-guides/style.html#braces" target="_blank" rel="external">Google Kotlin CodeStyle</a></li>
<li><a href="http://hax.iteye.com/blog/160003" target="_blank" rel="external">谈 literal 译名之选择</a></li>
<li><a href="https://blog.kotlin-academy.com/kotlin-programmer-dictionary-statement-vs-expression-e6743ba1aaa0" target="_blank" rel="external">Statement vs Expression</a></li>
</ul>
]]></content>
<summary type="html">
<p>Google 官方 Kotlin 编码风格翻译</p>
</summary>
</entry>
<entry>
<title>单一职责</title>
<link href="http://yoursite.com/2017/10/14/solid-spp/"/>
<id>http://yoursite.com/2017/10/14/solid-spp/</id>
<published>2017-10-14T06:55:55.000Z</published>
<updated>2017-11-14T14:30:24.022Z</updated>
<content type="html"><![CDATA[<p>单一职责 (<code>Single Responsibility Principle</code>) 记录</p>
<a id="more"></a>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://code.tutsplus.com/tutorials/solid-part-1-the-single-responsibility-principle--net-36074" target="_blank" rel="external">SOLID: Part 1 - The Single Responsibility Principle</a></p>
<h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><blockquote>
<p>一个类应该有且只有一个发生改变的原因。</p>
</blockquote>
<p>为什么这句话重要?</p>
<p>如果有两个不同的原因改变这个类的职责。我们想象一下基于这两个不同的原因,我们需要都在这个类中处理。这两个原因导致两个团队都必须对该类添加解决方案。这会导致不兼容、耗费时间和精力的现象出现。</p>
<h2 id="受众"><a href="#受众" class="headerlink" title="受众"></a>受众</h2><p>确定一个类或者模块的单一职责相当复杂。找到改变这个类的原因其中一个线索就是去分析我们这个类的受众群体。当我们系统提供特定的服务给用户时,就要料想到用户会有不同的需求。那么这些用户是我们的线索,通过这些线索我们分析我们原有类是否存在多个改变的原因。一些简单的模块和受众分析如下:</p>
<ul>
<li>持久模块(Persistence Module) - 受众包括 DBA 和软件架构师。</li>
<li>报告模块(Reporting Module) - 受众包括文员、会计师和操作人员。</li>
<li>工资系统 - 受众可能包括律师、精力和会计师。</li>
<li>图书管理系统搜索模块 - 受众可能包括图书管理员和客户</li>
</ul>
<h2 id="角色和参与者"><a href="#角色和参与者" class="headerlink" title="角色和参与者"></a>角色和参与者</h2><p>把具体的人和这些角色关联起来相当困难。在一个小公司里,一个人可能需要满足几个角色,而在大公司里,可能几个人分配一个角色。因此,更多时候我们应该思考的是这些角色。但是角色本身很难定义。</p>
<p>一个角色是什么?我们如何找到它?</p>
<p>想象一个参与者去扮演这些角色并把我们的受众和这些演员关联起来会更容易找到。</p>
<blockquote>
<p>因此,责任是一种服务于某一特定角色的一系列功能 — (Robert C. Martin)</p>
</blockquote>
<h2 id="变化的来源"><a href="#变化的来源" class="headerlink" title="变化的来源"></a>变化的来源</h2><p>在这种推理上,参与者们成为我们服务集的变化源泉。随着需求的变化,服务集也必须改变。</p>
<blockquote>
<p>一个职责对应的角色是这个职责发生改变的唯一原因。(Robert C. Martin)</p>
</blockquote>
<p>一本书的对象</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getTitle</span><span class="params">()</span></span>: String {</div><div class="line"> <span class="keyword">return</span> <span class="string">"title"</span></div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getAuthor</span><span class="params">()</span></span>: String {</div><div class="line"> <span class="keyword">return</span> <span class="string">"author"</span></div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">turnPage</span><span class="params">()</span></span> {</div><div class="line"> <span class="comment">// pointer to next page</span></div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">printCurrentPage</span><span class="params">()</span></span> {</div><div class="line"> println(<span class="string">"current page content"</span>)</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>目前看来该类挺合理,提供标题、作者、翻页和输出页面内容。现在我们开始考虑 Book 对象的受众,他们可能是谁?很容易的我们想到两个角色:图书管理员和数据显示机器(电子屏、纯文本界面)。这是两个不同的受众。将业务逻辑代码和呈现层代码混合在一起是相当糟糕的,因为他们违背了单一职责(SRP)。看看下面的代码:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getTitle</span><span class="params">()</span></span> = <span class="string">"title"</span></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getAuthor</span><span class="params">()</span></span> = <span class="string">"averson"</span></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">turnPage</span><span class="params">()</span></span> { <span class="comment">// pointer to next page }</span></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getCurrentPage</span><span class="params">()</span></span> = <span class="string">"current page content"</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Printer</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">printPage</span><span class="params">(content: <span class="type">String</span>)</span></span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">PaintTextPrinter</span> : <span class="type">Printer {</span></span></div><div class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">printPage</span><span class="params">(content: <span class="type">String</span>)</span></span> {</div><div class="line"> println(content)</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">HtmlPrinter</span> : <span class="type">Printer {</span></span></div><div class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">printPage</span><span class="params">(content: <span class="type">String</span>)</span></span> {</div><div class="line"> println(<span class="string">"<div style='single-page'><span class="variable">$content</span></div>"</span>)</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>即使是这个非常基本的例子,也展示了如何将表示与业务逻辑分离开来,并遵从 SRP,这在设计的灵活性上有很大的优势。</p>
<p>与上面的例子相似的一个例子是,对象可以从表示中保存和检索自己。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getTitle</span><span class="params">()</span></span> = <span class="string">"title"</span></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getAuthor</span><span class="params">()</span></span> = <span class="string">"averson"</span></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">turnPage</span><span class="params">()</span></span> {}</div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getCurrentPage</span><span class="params">()</span></span> = <span class="string">"current page content"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">save</span><span class="params">()</span></span> {</div><div class="line"> <span class="keyword">val</span> filename = <span class="string">"./<span class="subst">${getTitle()}</span>-<span class="subst">${getAuthor()}</span>"</span></div><div class="line"> IOUtils.save(filename, <span class="keyword">this</span>)</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>我们可以再次确定几个角色,比如图书管理系统和持久性数据管理。无论何时我们想要改变持久化,我们都需要改变这个类。当我们想要从一页翻到另一页的时候,我们必须修改这个类。这里有几个变化轴心。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getTitle</span><span class="params">()</span></span> = <span class="string">"title"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getAuthor</span><span class="params">()</span></span> = <span class="string">"author"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">turnPage</span><span class="params">()</span></span> {}</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getCurrentPage</span><span class="params">()</span></span> = <span class="string">"current page content"</span></div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">SimpleFilePresistence</span> : <span class="type">Presistence {</span></span></div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">save</span><span class="params">(book: <span class="type">Book</span>)</span></span> {</div><div class="line"> <span class="keyword">val</span> filename = <span class="string">"./<span class="subst">${book.getTitle()}</span>-<span class="subst">${book.getAuthor()}</span>"</span></div><div class="line"> IOUtils.save(filename, book)</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>将持久化操作转移到另一个类将清楚地分离职责,我们将可以灵活地改变持久性策略,而不会影响我们的 Book 类。例如,再实现一个 <code>DatabasePersistence</code> 也将是微不足道的,我们围绕 Book 对象的持久化操作将不会影响原有的业务逻辑。</p>
<h2 id="软件设计注意事项"><a href="#软件设计注意事项" class="headerlink" title="软件设计注意事项"></a>软件设计注意事项</h2><p>当我们思考我们需要编写的软件时,我们可以分析许多不同的方面。例如,设计多个需求的类代表一个变化的轴心。这些变化的轴心可能是一个单一责任的线索。软件的主要方面是变化,其次方面是功能。为了更好实现次要方面,我们需保证我们的主要方面,我们必须有一个易于更改、扩展、适应新功能并确保遵循 SRP 的设计。</p>
<p>我们可以一步一步地推理:</p>
<ol>
<li>较高的次要方面能及时引导主要方面</li>
<li>次要方面是用户的需求。</li>
<li>用户的需求意味着参与者的需求。</li>
<li>参与者的需求决定了这些参与者的需求变更。</li>
<li>参与者改变需求决定了我们的职责。</li>
</ol>
<p>所以当我们设计我们的软件时,我们应该:</p>
<ol>
<li>查找并定义参与者。</li>
<li>确定为这些参与者提供服务的责任。</li>
<li>将我们的函数和类分组,以便每个模块都只有一个集中的职责。</li>
</ol>
<h2 id="一个不太明显的例子"><a href="#一个不太明显的例子" class="headerlink" title="一个不太明显的例子"></a>一个不太明显的例子</h2><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getTitle</span><span class="params">()</span></span> = <span class="string">"title"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getAuthor</span><span class="params">()</span></span> = <span class="string">"author"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">turnPage</span><span class="params">()</span></span> {}</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getCurrentPage</span><span class="params">()</span></span> = <span class="string">"current page content"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getLocation</span><span class="params">()</span></span> { <span class="comment">// return position }</span></div><div class="line">}</div></pre></td></tr></table></figure>
<p>现在看来,这似乎是完全合理的。我们没有方法来处理持久性或展示。我们有 <code>turnPage()</code> 功能和一些方法来提供关于这本书的不同信息。然而,我们可能有一个问题。为了找到答案,我们可能需要分析我们的应用程序。函数 <code>getLocation()</code> 可能是问题所在。<code>Book</code> 类的所有方法都是关于业务逻辑的。因此,我们的观点必须从企业的角度出发。如果我们的应用程序真的被图书管理员所使用,他们正在搜索书籍,并给我们一本实体书,那么 <code>SRP</code> 可能会被违反。我们可以推断出,参与者感兴趣的操作操作是 <code>getTitle()</code>、<code>getAuthor()</code> 和 <code>getLocation()</code>。</p>
<p>客户还可以访问应用程序来选择一本书,并阅读前几页来了解这本书,并决定是否需要它。因此,所有读者的参与者除了 <code>getLocations()</code>,可能对其余的方法都感兴趣。一个普通的客户不关心书放在图书馆的什么地方。这本书将由图书管理员转交给客户。所以,我们确实有违反 SRP 的行为。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getTitle</span><span class="params">()</span></span> = <span class="string">"title"</span></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getAuthor</span><span class="params">()</span></span> = <span class="string">"averson"</span></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">turnPage</span><span class="params">()</span></span> {}</div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getCurrentPage</span><span class="params">()</span></span> = <span class="string">"current page content"</span></div><div class="line">}</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">BookLocator</span> </span>{</div><div class="line"></div><div class="line"> functin locate(book: Book) {</div><div class="line"> findBookBy(book.getTitle(), book.getAuthor())</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>在介绍图书定位器时,图书管理员将对图书定位器感兴趣。客户只会对这本书感兴趣。当然,有几种方法可以实现一个 <code>BookLocator</code>。它可以使用作者、标题或图书对象,从书中获取所需的信息。这总是取决于我们的业务。重要的是,如果图书馆被改变,图书管理员不得不在不同的图书馆找书,但图书对象就不会受到影响。同样,如果我们决定为读者提供预览的摘要,而不是让他们浏览页面,那将不会影响图书管理员,也不会影响图书的书架。但是,如果我们的业务是消除图书管理员并在我们的库中创建一个自助服务机制,那么我们可以考虑在我们的第一个示例中使用 SRP。读者也是我们的图书管理员,他们需要自己去找这本书,然后在自动化系统中查找。这也是一种可能性。重要的是要记住,你必须仔细考虑你的业务。</p>
<h2 id="再思考"><a href="#再思考" class="headerlink" title="再思考"></a>再思考</h2><p>在编写代码时,应该始终考虑单一职责原则。类和模块设计受到它的高度影响,它导致了一个低耦合的设计,并且越来越少的依赖关系。但就像任何一枚硬币一样,它有两个面。从我们应用程序的开始就很容易设计出 <code>SRP</code>。同样,我们也很容易确定我们想要或需要的演员数量。但从设计的角度来看,这实际上是危险的 —— 从一开始就试着去思考方方面面。过度的 <code>SRP</code> 考虑很容易导致过早的优化,而不是考虑更好的设计,这是导致分散的一个理由,类或模块的明确职责可能难以理解。</p>
<p>因此,无论何时,当您看到一个类或模块因不同的原因而发生变化时,不要犹豫,采取必要的步骤来尊重 <code>SRP</code>,但是不要过度,因为过早的优化可能会蒙蔽你的双眼。</p>
]]></content>
<summary type="html">
<p>单一职责 (<code>Single Responsibility Principle</code>) 记录</p>
</summary>
<category term="java" scheme="http://yoursite.com/tags/java/"/>
</entry>
<entry>
<title>全面了解 Handler</title>
<link href="http://yoursite.com/2017/08/29/handler/"/>
<id>http://yoursite.com/2017/08/29/handler/</id>
<published>2017-08-29T01:23:58.000Z</published>
<updated>2017-11-04T04:04:35.676Z</updated>
<summary type="html">
</summary>
</entry>
<entry>
<title>Android 中高效使用 Bitmap</title>
<link href="http://yoursite.com/2017/08/28/bitmap/"/>
<id>http://yoursite.com/2017/08/28/bitmap/</id>
<published>2017-08-28T01:23:39.000Z</published>
<updated>2017-08-31T10:03:59.518Z</updated>
<summary type="html">
</summary>
</entry>
<entry>
<title>Activity 源码解析</title>
<link href="http://yoursite.com/2017/08/27/activity-source-code/"/>
<id>http://yoursite.com/2017/08/27/activity-source-code/</id>
<published>2017-08-27T01:23:07.000Z</published>
<updated>2017-08-31T10:03:46.718Z</updated>
<summary type="html">
</summary>
</entry>
<entry>
<title>Android 自定义 View(一)</title>
<link href="http://yoursite.com/2017/08/23/android-custom-view-1/"/>
<id>http://yoursite.com/2017/08/23/android-custom-view-1/</id>
<published>2017-08-23T01:26:17.000Z</published>
<updated>2017-08-31T10:05:34.558Z</updated>
<summary type="html">
</summary>
</entry>
<entry>
<title>Android 架构的简单演变</title>
<link href="http://yoursite.com/2017/08/01/architecture/"/>
<id>http://yoursite.com/2017/08/01/architecture/</id>
<published>2017-08-01T01:25:05.000Z</published>
<updated>2017-08-31T10:05:34.526Z</updated>
<summary type="html">
</summary>
</entry>
<entry>
<title>RxJava</title>
<link href="http://yoursite.com/2017/07/25/rxjava/"/>
<id>http://yoursite.com/2017/07/25/rxjava/</id>
<published>2017-07-25T01:22:34.000Z</published>
<updated>2017-08-31T10:06:04.839Z</updated>
<summary type="html">
</summary>
</entry>
<entry>
<title>Java 性能优化</title>
<link href="http://yoursite.com/2017/07/20/java-optimizing/"/>
<id>http://yoursite.com/2017/07/20/java-optimizing/</id>
<published>2017-07-20T01:15:57.000Z</published>
<updated>2017-08-31T10:06:17.867Z</updated>
<summary type="html">
</summary>
</entry>
<entry>
<title>Kotlin 高阶函数</title>
<link href="http://yoursite.com/2017/07/16/kotlin-advance-func/"/>
<id>http://yoursite.com/2017/07/16/kotlin-advance-func/</id>
<published>2017-07-16T05:57:08.000Z</published>
<updated>2017-09-08T05:59:23.679Z</updated>
<content type="html"><![CDATA[<p><code>Kotlin</code> 中高阶函数和内联函数摘要</p>
<a id="more"></a>
<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul>
<li>高阶函数介绍和使用</li>
<li>内联函数</li>
</ul>
<h2 id="高阶函数介绍和使用"><a href="#高阶函数介绍和使用" class="headerlink" title="高阶函数介绍和使用"></a>高阶函数介绍和使用</h2><p><strong>高阶函数</strong>是将另一个函数作为参数或返回值的函数。在 <code>Kotlin</code> 中,函数可以表示为 <code>Lambda</code> 或函数引用。因此高阶函数可以传递 <code>Lambda</code> 和函数引用。</p>
<p>声明一个函数:</p>
<blockquote>
<p>格式 : <code>val funcReference: params -> return type? = lambda</code></p>
</blockquote>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> predicate: (num: <span class="built_in">Int</span>) -> <span class="built_in">Boolean</span> = { num -> num > <span class="number">2</span> }</div><div class="line">IntStream.range(<span class="number">1</span>, <span class="number">5</span>)</div><div class="line"> .filter(predicate)</div><div class="line"> .forEach(::println)</div></pre></td></tr></table></figure>
<p><strong>函数作为参数</strong>:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> 函数名<span class="params">(参数, 函数型参数名: (参数)</span></span> -> 返回值): 函数返回值 {</div><div class="line"> <span class="keyword">val</span> 返回值 = 函数型参数名(参数)</div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">EndlessRecyclerViewScrollListener</span></span>(</div><div class="line"> <span class="keyword">private</span> <span class="keyword">val</span> layoutManager: RecyclerView.LayoutManager,</div><div class="line"> <span class="keyword">private</span> <span class="keyword">val</span> loadMore: (totalItemsCount: <span class="built_in">Int</span>, view: RecyclerView) -> <span class="built_in">Unit</span>)</div><div class="line">: RecyclerView.OnScrollListener() {</div><div class="line"></div><div class="line"></div><div class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onScrolled</span><span class="params">(view: <span class="type">RecyclerView</span>, dx: <span class="type">Int</span>, dy: <span class="type">Int</span>)</span></span> {</div><div class="line"> loadMore(itemCount, view)</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">feed_recyclerview.apply {</div><div class="line"> addOnScrollListener(EndlessRecyclerViewScrollListener(</div><div class="line"> feed_recyclerview.layoutManager, </div><div class="line"> { _: <span class="built_in">Int</span>, _: RecyclerView -> viewModel.loadNextPage()</div><div class="line"> }))</div><div class="line">}</div></pre></td></tr></table></figure>
<p><strong>函数作为返回值</strong>:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 快递方式</span></div><div class="line"><span class="keyword">enum</span> <span class="class"><span class="keyword">class</span> <span class="title">Delivery</span> </span>{ STANDARD, EXPEDITED }</div><div class="line"></div><div class="line"><span class="comment">// 订单</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Order</span></span>(<span class="keyword">val</span> itemCount: <span class="built_in">Int</span>)</div><div class="line"></div><div class="line"><span class="comment">// 价格计算逻辑</span></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">getShippingCostCalculator</span><span class="params">(delivery: <span class="type">Delivery</span>)</span></span>: (Order) -> <span class="built_in">Double</span> {</div><div class="line"> <span class="keyword">if</span> (delivery == Delivery.EXPEDITED) {</div><div class="line"> <span class="keyword">return</span> { order -> <span class="number">6</span> + <span class="number">1.2</span> * order.itemCount }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> { order -> <span class="number">1.2</span> * order.itemCount }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 将计算准则置入一个函数中并作用于 Order 对象返回对应的费用结果</span></div><div class="line"><span class="comment">// 对于返回函数的函数来说明确指明类型感觉更清晰</span></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">main</span><span class="params">(args: <span class="type">Array</span><<span class="type">String</span>>)</span></span> {</div><div class="line"> <span class="keyword">val</span> standardCalcFunc: (Order) -> <span class="built_in">Double</span> = getShippingCostCalculator(Delivery.STANDARD)</div><div class="line"> <span class="keyword">val</span> result = standardCalcFunc(Order(<span class="number">3</span>))</div><div class="line"> println(result)</div><div class="line">}</div></pre></td></tr></table></figure>
<p>用 <code>Java 8</code> 实现一下: </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Function<Order, Double> <span class="title">getShippingCostCalculator</span><span class="params">(Delivery delivery)</span> </span>{</div><div class="line"> <span class="keyword">if</span> (delivery == Delivery.EXPEDITED) {</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Function<Order, Double>() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> Double <span class="title">apply</span><span class="params">(Order order)</span> </span>{</div><div class="line"> <span class="keyword">return</span> order.getItemCount() * <span class="number">1.2</span> + <span class="number">6</span>;</div><div class="line"> }</div><div class="line"> };</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Function<Order, Double>() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> Double <span class="title">apply</span><span class="params">(Order order)</span> </span>{</div><div class="line"> <span class="keyword">return</span> order.getItemCount() * <span class="number">1.2</span>;</div><div class="line"> }</div><div class="line"> };</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>{</div><div class="line"> Order order = <span class="keyword">new</span> Order(<span class="number">3</span>);</div><div class="line"> Function<Order, Double> function = getShippingCostCalculator(</div><div class="line"> Delivery.STANDARD);</div><div class="line"> <span class="keyword">double</span> cost = function.apply(order);</div><div class="line"> System.out.println(cost);</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="内联函数"><a href="#内联函数" class="headerlink" title="内联函数"></a>内联函数</h2><p>每次使用 <code>Lambda</code> 的时候都会创建一个额外的类。这导致了效率的低下。使用内联函数可以避免该问题。内联函数适用于参数为 <code>Lambda</code> 且频繁调用的情况。</p>
<blockquote>
<p>注意:简单函数 Kotlin 不建议使用内联</p>
</blockquote>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">inline</span> <span class="function"><span class="keyword">fun</span> <span class="title">functionName</span><span class="params">(params)</span></span> {}</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">private</span> <span class="keyword">val</span> authPrefs: SharedPreferences</div><div class="line"></div><div class="line"><span class="keyword">inline</span> <span class="function"><span class="keyword">fun</span> SharedPreferences.<span class="title">edit</span><span class="params">(preferApply: <span class="type">Boolean</span> = <span class="literal">false</span>, f: <span class="type">SharedPreferences</span>.<span class="type">Editor</span>.()</span></span> -> <span class="built_in">Unit</span>) {</div><div class="line"> <span class="keyword">val</span> editor = edit()</div><div class="line"> editor.f()</div><div class="line"> <span class="keyword">if</span> (preferApply) editor.apply() <span class="keyword">else</span> editor.commit()</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">fun</span> <span class="title">persistAuthState</span><span class="params">(state: <span class="type">AuthState</span>)</span></span> {</div><div class="line"> authPrefs.edit {</div><div class="line"> putString(<span class="string">"stateJson"</span>, state.jsonSerializeString())</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>对应的 <code>Java</code> 代码:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">persistAuthState</span><span class="params">(AuthState state)</span> </span>{</div><div class="line"> SharedPreferences $receiver$iv = <span class="keyword">this</span>.authPrefs;</div><div class="line"> <span class="keyword">boolean</span> preferApply$iv = <span class="keyword">false</span>;</div><div class="line"> Editor editor$iv = $receiver$iv.edit();</div><div class="line"></div><div class="line"> editor$iv.putString(<span class="string">"stateJson"</span>, state.jsonSerializeString());</div><div class="line"></div><div class="line"> editor$iv.commit();</div><div class="line">}</div></pre></td></tr></table></figure>
<p>如果不是 <code>inline</code> 会类似如下情况</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">persistAuthState</span><span class="params">(AuthState state)</span> </span>{</div><div class="line"> SharedPreferences $receiver$iv = <span class="keyword">this</span>.authPrefs;</div><div class="line"> <span class="keyword">boolean</span> preferApply$iv = <span class="keyword">false</span>;</div><div class="line"> Editor editor$iv = $receiver$iv.edit();</div><div class="line"></div><div class="line"> <span class="keyword">new</span> Runnable() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> editor$iv.putString(<span class="string">"stateJson"</span>, state.jsonSerializeString());</div><div class="line"> }</div><div class="line"> }.run();</div><div class="line"></div><div class="line"> editor$iv.commit();</div><div class="line">}</div></pre></td></tr></table></figure>
<p>函数的定义围绕变量和返回值。</p>
<p>内联函数的 <code>lambda</code> 参数默认也为内联函数,使用 <code>noinline</code> 关键字可以取消内联。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">inline</span> <span class="function"><span class="keyword">fun</span> <span class="title">call</span><span class="params">(p1: <span class="type">Int</span>, func: ()</span></span> -> <span class="built_in">Unit</span>) {</div><div class="line"> println(p1)</div><div class="line"> func()</div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">int</span> p1$iv = <span class="number">1</span>;</div><div class="line">String var3 = <span class="string">"action "</span> + p1$iv;</div><div class="line">System.out.println(var3);</div></pre></td></tr></table></figure>
<p>内联函数返回</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">inline</span> <span class="function"><span class="keyword">fun</span> <span class="title">call</span><span class="params">(p1: <span class="type">Int</span>, func: (<span class="type">num</span>: <span class="type">Int</span>)</span></span> -> <span class="built_in">Unit</span>) {</div><div class="line"> func(p1)</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">main</span><span class="params">(args: <span class="type">Array</span><<span class="type">String</span>>)</span></span> {</div><div class="line"> println(<span class="string">"before"</span>)</div><div class="line"> call(<span class="number">2</span>) {</div><div class="line"> it * it</div><div class="line"> <span class="keyword">return</span></div><div class="line"> }</div><div class="line"> println(<span class="string">"after"</span>)</div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">String var1 = <span class="string">"start"</span>;</div><div class="line">System.out.println(var1);</div><div class="line"><span class="keyword">int</span> p1$iv = <span class="number">2</span>;</div><div class="line"><span class="keyword">int</span> var4 = p1$iv * p1$iv;</div><div class="line">System.out.println(var4);</div></pre></td></tr></table></figure>
<p><code>Kotlin</code> 集合标准库中 <code>map</code> 和 <code>filter</code> 就是 <code>inline</code> 函数。所以这些操作无需创建额外的对象。</p>
<p>如在 <code>Java 8</code> 中我们需要额外创建 <code>IntPredicate</code> 和 <code>IntFunction</code>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">IntStream.range(<span class="number">1</span>, <span class="number">10</span>)</div><div class="line"> .filter(<span class="keyword">new</span> IntPredicate() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">test</span><span class="params">(<span class="keyword">int</span> value)</span> </span>{</div><div class="line"> <span class="keyword">return</span> value > <span class="number">5</span>;</div><div class="line"> }</div><div class="line"> })</div><div class="line"> .mapToObj(<span class="keyword">new</span> IntFunction<String>() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">apply</span><span class="params">(<span class="keyword">int</span> value)</span> </span>{</div><div class="line"> <span class="keyword">return</span> String.valueOf(value);</div><div class="line"> }</div><div class="line"> });</div></pre></td></tr></table></figure>
<p><code>Kotlin</code> 方式反编译后的源码是直接内嵌并没有创建额外的对象。当然下面的代码应根据具体情况合理使用 <code>asSequence()</code> 懒加载。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">main</span><span class="params">(args: <span class="type">Array</span><<span class="type">String</span>>)</span></span> {</div><div class="line"> IntRange(<span class="number">1</span>, <span class="number">10</span>)</div><div class="line"> .filter { it > <span class="number">5</span> }</div><div class="line"> .map(<span class="built_in">Int</span>::toString)</div><div class="line">}</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p><code>Kotlin</code> 中高阶函数和内联函数摘要</p>
</summary>
<category term="Kotlin" scheme="http://yoursite.com/tags/Kotlin/"/>
</entry>
<entry>
<title>Kotlin 类型系统</title>
<link href="http://yoursite.com/2017/07/15/kotlin-type-system/"/>
<id>http://yoursite.com/2017/07/15/kotlin-type-system/</id>
<published>2017-07-15T09:57:06.000Z</published>
<updated>2017-08-31T11:52:53.131Z</updated>
<content type="html"><![CDATA[<p><code>Kotlin</code> 类型系统摘要</p>
<a id="more"></a>
<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul>
<li>Null 基本处理</li>
<li>属性初始化延迟</li>
<li>拓展函数 Null 检查</li>
<li>Kotlin 中的原始类型</li>
<li>Kotlin 中的集合类型的处理</li>
<li>Kotlin 中的数组</li>
</ul>
<h2 id="Null-处理"><a href="#Null-处理" class="headerlink" title="Null 处理"></a>Null 处理</h2><p>毋庸置疑,空指针可谓是开发中遇到最多的异常之一。</p>
<p>看几个例子:</p>
<p><code>Map.get()</code> 如果返回 <code>null</code> 到底是对应的 <code>key</code> 没有值,还是说该 <code>key</code> 对应的值就是 <code>null</code>?</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">Map<String, Integer> nameAges = <span class="keyword">new</span> HashMap<>();</div><div class="line">nameAges.put(<span class="string">"Averson"</span>, <span class="number">18</span>);</div><div class="line">Integer bobAge = nameAges.get(<span class="string">"Bob"</span>);</div><div class="line">System.out.println(michal); <span class="comment">// null</span></div><div class="line"></div><div class="line">nameAges.put(<span class="string">"Bob"</span>, <span class="keyword">null</span>);</div><div class="line">bobAge = nameAges.get(<span class="string">"Bob"</span>);</div><div class="line">System.out.println(michal); <span class="comment">// null</span></div></pre></td></tr></table></figure>
<p>例子中的 <code>null</code> 表意不清,可以是成功可以是失败。</p>
<p>因此著名的 <code>Guava</code> 库设计了 <code>Optional</code>、<code>Preconditions</code> 等来辅助 <code>null</code> 的处理。</p>
<p><code>Optional</code> 更明确持有的数据是否有价值,是否存在,有价值如何处理,没价值如何处理。不存在说 “数据为 NULL “ 的说法。具体用法参考 <a href="https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained" target="_blank" rel="external">UsingAndAvoidingNullExplained</a>。<code>Java 8</code> 中提供了比 <code>Guava</code> 更强大的 <code>Optional</code>,可以参考 <a href="https://www.mkyong.com/java8/java-8-optional-in-depth/" target="_blank" rel="external">Java 8 Optional In Depth</a>。在最新版本的 <code>Guava</code> 中支持 <code>Guava Optional</code> 转换为 <code>Java Optional</code>。</p>
<p>言归正传,在 <code>Kotlin</code> 中是使用 “?” 操作符对类型进行直接 <code>null</code> 约束或操作。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">obj?.prop -> <span class="keyword">if</span> (obj != <span class="literal">null</span>) obj.prop <span class="keyword">else</span> <span class="literal">null</span></div><div class="line">obj?:<span class="keyword">default</span> -> <span class="keyword">if</span> (obj != <span class="literal">null</span>) obj <span class="keyword">else</span> <span class="keyword">default</span></div><div class="line">obj?:expcetion -> <span class="keyword">if</span> (obj != <span class="literal">null</span>) obj <span class="keyword">else</span> <span class="keyword">throw</span> exception</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Customer</span></span>(<span class="keyword">val</span> name: String, <span class="keyword">val</span> address: Address?)</div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Address</span></span>(<span class="keyword">val</span> name: String, <span class="keyword">val</span> postcode: <span class="built_in">Int</span>)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">fun</span> Customer.<span class="title">addressName</span><span class="params">()</span></span>: String {</div><div class="line"> <span class="keyword">return</span> address?.name ?: <span class="string">"unknown"</span></div><div class="line">}</div></pre></td></tr></table></figure>
<p>对应的 <code>Java</code> 代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">Address address = customer.getAddress();</div><div class="line">String result;</div><div class="line"><span class="keyword">if</span> (address != <span class="keyword">null</span>) {</div><div class="line"> result = address.getName();</div><div class="line"> <span class="keyword">if</span> (result != <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">return</span> result;</div><div class="line"> }</div><div class="line">}</div><div class="line">result = <span class="string">"unknown"</span>;</div><div class="line"><span class="keyword">return</span> result;</div></pre></td></tr></table></figure>
<p>必须不为 <code>null</code></p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> name: String? = <span class="literal">null</span></div><div class="line"><span class="comment">// throw NullPointExpcetion()</span></div><div class="line">println(name!!)</div></pre></td></tr></table></figure>
<p>内部实现和 <a href="https://github.com/google/guava/blob/master/guava/src/com/google/common/base/Optional.java#L104" target="_blank" rel="external"><code>Optional.of()</code></a> 异曲同工。</p>
<p>不为 <code>null</code> 时执行相关操作。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">login?.let {</div><div class="line"> ApiService.login(it.username, it.password)</div><div class="line">}</div></pre></td></tr></table></figure>
<p>因为 <code>Kotlin</code> 和 <code>Java</code> 两者代码是可以互相交互的所以 <code>?</code> 对应的 <code>Java</code> 类型如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">@Nullable Type -> Type?</div><div class="line">@NotNull Type -> Type</div></pre></td></tr></table></figure>
<h2 id="属性初始化延迟"><a href="#属性初始化延迟" class="headerlink" title="属性初始化延迟"></a>属性初始化延迟</h2><p>很多框架会在对象实例化后提供一个单独的方法给开发者进行相关的初始化。比方说 <code>Android</code> 中的 <code>Activity</code> 提供 <code>onCreate</code> 方法进行相关属性初始化。该过程中你的属性不能为 <code>null</code> 而 <code>Activity</code> 有没有构造器进行属性初始化。如果属性允许为 <code>null</code> 并在 <code>onCreate</code> 中执行初始化,那在其他方法中调用则会每次都出现 <code>!!</code> 或 <code>?.</code> 的判断。则会让代码显得非常丑陋。因此 <code>Kotlin</code> 提供 <code>lateinit</code> 关键字。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">private</span> <span class="keyword">lateinit</span> <span class="keyword">var</span> api: ApiService</div><div class="line"></div><div class="line"><span class="meta">@Before</span></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">setup</span><span class="params">()</span></span> {</div><div class="line"> api = ApiService()</div><div class="line">}</div><div class="line"></div><div class="line"><span class="meta">@Test</span></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">hasApi</span><span class="params">()</span></span> {</div><div class="line"> <span class="comment">// 不需要额外的 null 检查</span></div><div class="line"> assertNotNull(api)</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="拓展函数-Null-检查"><a href="#拓展函数-Null-检查" class="headerlink" title="拓展函数 Null 检查"></a>拓展函数 Null 检查</h2><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> String.<span class="title">isNullOrBlank</span><span class="params">()</span></span>: <span class="built_in">Boolean</span> {</div><div class="line"> <span class="keyword">return</span> isBlank();</div><div class="line">}</div></pre></td></tr></table></figure>
<p>该代码中拓展对象必须不为 <code>null</code>,也就是说下面的代码永远不会调用到 <code>isNullOrBlank</code> 方法,因为 <code>input</code> 可能为 <code>null</code>,你必须要有安全的 <code>null</code> 检查后才能调用 <code>isNullOrBlank</code> 方法。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> input: String? = <span class="literal">null</span></div><div class="line">input.isNullOrBlank()</div><div class="line"><span class="comment">// safe check</span></div><div class="line">input?.isNullOrBlank()</div></pre></td></tr></table></figure>
<p>那么 <code>isNullOrBlank</code> 这个函数名就起得不对了,<code>isNull</code> 压根就无法检查 <code>null</code>。那么我们如何允许拓展对象即便是 <code>null</code> 的情况下也可以执行相关拓展方法?</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> String?.<span class="title">isNullOrBlank</span><span class="params">()</span></span>: <span class="built_in">Boolean</span> {</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span> == <span class="literal">null</span> || isBlank()</div><div class="line">}</div></pre></td></tr></table></figure>
<p><code>String?</code> 声明了拓展对象可以是为 <code>null</code> 的情况,现在 <code>null</code> 可以执行检查了。</p>
<p>当你声明一个拓展函数的时候,你需要考虑这个拓展对象为 <code>null</code> 的情况。当然不为 <code>null</code> 的情况下会更有助于你更改代码,因为它不会造成额外的代码破坏(NPE)。所以你应该考虑清楚到底允许拓展对象是 <code>null</code> 的情况是否具备合理性。</p>
<h2 id="Kotlin-中的原始类型"><a href="#Kotlin-中的原始类型" class="headerlink" title="Kotlin 中的原始类型"></a>Kotlin 中的原始类型</h2><p>在 <code>Java</code> 中有<strong>原始类型</strong>和<strong>引用类型</strong>类型。而在 <code>Kotlin</code> 中是不区分的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">int</span> i = <span class="number">1</span>;</div><div class="line">List<Integer> items = <span class="keyword">new</span> ArrayList<>();</div><div class="line">items.add(i);</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> i: <span class="built_in">Int</span> = <span class="number">1</span></div><div class="line"><span class="keyword">val</span> items = listOf(i)</div></pre></td></tr></table></figure>
<p><code>Kotlin</code> 不会自动从一个类型转换为另外一个类型,比方说 <code>Int</code> 转为 <code>Long</code>:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> number = <span class="number">1</span></div><div class="line"><span class="keyword">val</span> l: <span class="built_in">Long</span> = number</div></pre></td></tr></table></figure>
<p>因此 <code>Kotlin</code> 提供了互相转换的方法:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Int</span> <span class="keyword">private</span> <span class="keyword">constructor</span></span>() : Number(), Comparable<<span class="built_in">Int</span>> {</div><div class="line"> <span class="keyword">public</span> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">toByte</span><span class="params">()</span></span>: <span class="built_in">Byte</span></div><div class="line"> <span class="keyword">public</span> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">toChar</span><span class="params">()</span></span>: <span class="built_in">Char</span></div><div class="line"> <span class="keyword">public</span> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">toShort</span><span class="params">()</span></span>: <span class="built_in">Short</span></div><div class="line"> <span class="keyword">public</span> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">toInt</span><span class="params">()</span></span>: <span class="built_in">Int</span></div><div class="line"> <span class="keyword">public</span> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">toLong</span><span class="params">()</span></span>: <span class="built_in">Long</span></div><div class="line"> <span class="keyword">public</span> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">toFloat</span><span class="params">()</span></span>: <span class="built_in">Float</span></div><div class="line"> <span class="keyword">public</span> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">toDouble</span><span class="params">()</span></span>: <span class="built_in">Double</span></div><div class="line">}</div></pre></td></tr></table></figure>
<p>在 <code>Java</code> 中的超类是 <code>Object</code>,<strong>而在 <code>Kotlin</code> 中则是 <code>Any</code></strong>。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> name: Any = <span class="string">"Averson"</span></div></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Object name = <span class="string">"Averson"</span>;</div></pre></td></tr></table></figure>
<p>在 <code>Kotlin</code> 中 <code>Unit</code> 对应 <code>Java</code> 中的 <code>void</code>。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">consume</span><span class="params">(content: <span class="type">Any</span>)</span></span>: <span class="built_in">Unit</span> {</div><div class="line"> println(content)</div><div class="line">}</div></pre></td></tr></table></figure>
<p>区别于 <code>Java</code>,<code>Kotlin</code> 中的 <code>Unit</code> 可以作为泛型参数。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Processor</span><<span class="type">T</span>> </span>{ </div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">process</span><span class="params">()</span></span>: T</div><div class="line">}</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">NoResultProcessor</span> : <span class="type">Processor</span><<span class="type">Unit</span>> </span>{</div><div class="line"> <span class="comment">// Unit 可以忽略不写</span></div><div class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">process</span><span class="params">()</span></span> {</div><div class="line"> <span class="comment">//no-op</span></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>实际上的原理如下:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> static <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">NoResultProcessor</span> <span class="title">implements</span> <span class="title">Processor</span> </span>{</div><div class="line"> <span class="keyword">public</span> void processor() {}</div><div class="line"></div><div class="line"> <span class="keyword">public</span> Object processor() {</div><div class="line"> <span class="keyword">this</span>.processor();</div><div class="line"> <span class="keyword">return</span> <span class="built_in">Unit</span>.INSTANCE;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>很自然我们调用的时候会返回一个 <code>Unit</code> 表示该函数是无返回类型。</p>
<h2 id="Kotlin-中的集合类型的处理"><a href="#Kotlin-中的集合类型的处理" class="headerlink" title="Kotlin 中的集合类型的处理"></a>Kotlin 中的集合类型的处理</h2><p><code>Kotlin</code> 中提供两种不同的集合接口,分别是<strong>Collection</strong>和<strong>MutableCollection</strong>。前者对元素只读,后者对元素可进行读写等操作。</p>
<p><code>Kotlin</code> 分别对 <code>Java</code> 中的集合接口提供两种实现,以 <code>Mutable</code> 作为前缀区分。<code>Kotlin</code> 中也提供了相关的静态方法快速创建相关集合。</p>
<table>
<thead>
<tr>
<th>Collection Type</th>
<th>Read-Only</th>
<th>Mutable</th>
</tr>
</thead>
<tbody>
<tr>
<td>List</td>
<td>listOf()</td>
<td>arrayListOf()</td>
<td></td>
</tr>
<tr>
<td>Set</td>
<td>setOf()</td>
<td>hashSetOf(), linkedSetOf(), sortedSetOf()</td>
</tr>
<tr>
<td>Map</td>
<td>mapOf()</td>
<td>hashMapOf(), linkedMapOf(), sortedMapOf()</td>
</tr>
</tbody>
</table>
<p>一个重要的细节,由于 <code>Kotlin</code> 和 <code>Java</code> 代码可以交互,在 <code>Java</code> 中集合不区分可读写和只读类型,因此 <code>Kotlin</code> 无法传递只读集合到 <code>Java</code> 代码中。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CollectionsUtil</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> List<String> <span class="title">uppercaseAll</span><span class="params">(List<String> items)</span> </span>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < items.size(); i++) {</div><div class="line"> items.set(i, items.get(i).toUpperCase());</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> items;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> readOnlyList = listOf(<span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>)</div><div class="line"><span class="comment">// 在 Java 中能修改只读集合</span></div><div class="line">CollectionsUtil.uppercaseAll(readOnlyList)</div><div class="line">println(readOnlyList)</div><div class="line">>> [A, B, C]</div></pre></td></tr></table></figure>
<p>所以在定义集合相关的时候应该考虑好:</p>
<ul>
<li>集合是否可以为 null</li>
<li>集合元素是否可以为 null</li>
<li>集合是否可以修改</li>
</ul>
<p>考虑下面这段代码:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">interface</span> <span class="title">FileContentProcessor</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">void</span> <span class="title">processContents</span><span class="params">(File path,</span></span></div><div class="line"> <span class="keyword">byte</span>[] binaryContents,</div><div class="line"> List<String> textContents);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>如果 <code>Kotlin</code> 实现这个接口我们需要考虑如下内容:</p>
<ul>
<li><code>List<String></code> 可以为 <code>null</code>,一些文件可能只有二进制,没有文本内容。</li>
<li><code>List<String></code> 元素不为 <code>null</code>,因为文件中的内容不为 <code>null</code></li>
<li><code>List<String></code> 为只读,因为只显示内容,并且内容不应该被修改。</li>
</ul>
<p>默认实现如下:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">FileIndexer</span> : <span class="type">FileContentProcessor {</span></span></div><div class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">processContents</span><span class="params">(path: <span class="type">File</span>?, binaryContents: <span class="type">ByteArray</span>?, textContents: <span class="type">MutableList</span><<span class="type">String</span>>?)</span></span> {</div><div class="line"> <span class="comment">// no-op</span></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>而我们的需要更改为:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">FileIndexer</span> : <span class="type">FileContentProcessor {</span></span></div><div class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">processContents</span><span class="params">(path: <span class="type">File</span>, binaryContents: <span class="type">ByteArray</span>?, textContents: <span class="type">List</span><<span class="type">String</span>>?)</span></span> {</div><div class="line"> <span class="comment">// no-op</span></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>在看一个例子:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">DataParser</span><<span class="title">T</span>> </span>{</div><div class="line"> <span class="function"><span class="keyword">void</span> <span class="title">parseData</span><span class="params">(String input, List<T> output, List<String> errors)</span></span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<p><code>Kotlin</code> 默认实现</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">FeedParser</span> : <span class="type">DataParser</span><<span class="type">Feed</span>> </span>{</div><div class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">parseData</span><span class="params">(input: <span class="type">String</span>?, output: <span class="type">MutableList</span><<span class="type">Feed</span>>?, errors: <span class="type">MutableList</span><<span class="type">String</span>>?)</span></span> {</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>考虑如下几点:</p>
<ul>
<li>输入不应该为 null</li>
<li>Feed 集合不应该为 null,并且是可以修改的,因为解析数据后需要添加进去。</li>
<li>error 集合不应该为 null,但是元素可以为 null,因为解析过程不一定会出现错误。</li>
</ul>
<p>所以更改为</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">FeedParser</span> : <span class="type">DataParser</span><<span class="type">Feed</span>> </span>{</div><div class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">parseData</span><span class="params">(input: <span class="type">String</span>, output: <span class="type">MutableList</span><<span class="type">Feed</span>>, errors: <span class="type">MutableList</span><<span class="type">String</span>?>)</span></span> {</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>到此为止 <code>Kotlin</code> 中集合的类型使用基本使用结束。</p>
<h2 id="Kotlin-中的数组"><a href="#Kotlin-中的数组" class="headerlink" title="Kotlin 中的数组"></a>Kotlin 中的数组</h2><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Array<<span class="built_in">Int</span>> = Integer[]</div><div class="line">IntArray() = int[]</div></pre></td></tr></table></figure>
<p>其余依次类推。</p>
<p>数组的使用:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Int数组容量为 5</span></div><div class="line">IntArray(<span class="number">5</span>)</div><div class="line"></div><div class="line"><span class="comment">// Int数组容量为 5,内容分别是 {1, 2, 3, 4, 5}</span></div><div class="line">intArrayOf(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)</div><div class="line"></div><div class="line"><span class="comment">// 容量为 5 并伴随一个初始数据生成的 Lambda</span></div><div class="line">IntArray(<span class="number">5</span>) { index -> index * index }</div><div class="line"></div><div class="line"><span class="comment">// List<Integer> to int[]</span></div><div class="line">listOf(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>).toIntArray()</div><div class="line"><span class="comment">// 源码</span></div><div class="line">List numberList = CollectionsKt.listOf(new Integer[]{ Integer.valueOf(<span class="number">1</span>), Integer.valueOf(<span class="number">2</span>), Integer.valueOf(<span class="number">3</span>) });</div><div class="line">int[] numberArray = CollectionsKt.toIntArray((Collection)numberList);</div><div class="line"></div><div class="line"><span class="comment">// 对于可变参数, Kotlin 提供和 Python 类似的语法 '*' 直接传入</span></div><div class="line"><span class="keyword">val</span> src = arrayListOf(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</div><div class="line"><span class="keyword">val</span> appends = arrayOf(<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>)</div><div class="line">Collections.addAll(src, *appends)</div><div class="line">>> [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>]</div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<p><code>Kotlin</code> 类型系统摘要</p>
</summary>
<category term="Kotlin" scheme="http://yoursite.com/tags/Kotlin/"/>
</entry>
<entry>
<title>Kotlin Standard</title>
<link href="http://yoursite.com/2017/07/14/let-apply-with/"/>
<id>http://yoursite.com/2017/07/14/let-apply-with/</id>
<published>2017-07-14T14:11:47.000Z</published>
<updated>2017-08-31T09:58:36.249Z</updated>
<content type="html"><![CDATA[<p><code>Kotlin Standard</code> 库中 <code>let</code>、<code>apply</code>、<code>with</code>、<code>use</code> 的使用方法。</p>
<a id="more"></a>
<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul>
<li>let 使用</li>
<li>apply 使用</li>
<li>with 使用</li>
<li>use 使用</li>
<li>综合使用</li>
</ul>
<h2 id="let-使用"><a href="#let-使用" class="headerlink" title="let 使用"></a>let 使用</h2><p><code>let</code> 操作是一个对象作为闭包的参数执行某一个操作。返回值以最后一句为标准,无返回则是 <code>Unit</code>。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> <span class="type"><T, R></span> T.<span class="title">let</span><span class="params">(f: (<span class="type">T</span>)</span></span> -> R): R = f(<span class="keyword">this</span>)</div><div class="line"></div><div class="line"></div><div class="line">feedViewModel.observe(<span class="keyword">this</span>, Observer {</div><div class="line"> it?.let { adapter.updateItems(it) }</div><div class="line">})</div></pre></td></tr></table></figure>
<p>如果是 <code>Java</code> 你或许会想到这样的代码:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">feedViewModel.observe(<span class="keyword">this</span>, feeds -> {</div><div class="line"> <span class="keyword">if</span> (feeds != <span class="keyword">null</span>) {</div><div class="line"> adapter.updateItems(feeds);</div><div class="line"> }</div><div class="line">});</div></pre></td></tr></table></figure>
<p>看完上述代码基本也就清楚 <code>let</code> 的用途了。简单来说 <code>let</code> 侧重于把对象扔给一个功能代码块<strong>处理</strong>。</p>
<h2 id="apply-使用"><a href="#apply-使用" class="headerlink" title="apply 使用"></a>apply 使用</h2><p><code>apply</code> 操作是一个对象作为闭包的参数传入,并且闭包范围内可以直接调用对象的任意方法,最后返回对象本身,适合预配置操作。</p>
<p>典型用法:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 保证缓存文件夹目录是存在后对缓存文件夹进行操作。</span></div><div class="line"><span class="keyword">val</span> cacheDir = File(cachePath).apply { mkdirs() }</div><div class="line"></div><div class="line">feedList.apply {</div><div class="line"> adapter = <span class="keyword">this</span><span class="symbol">@FeedActivity</span>.adapter</div><div class="line"> addOnScrollListener(EndlessScrollListener(</div><div class="line"> layoutManager,</div><div class="line"> { _: <span class="built_in">Int</span>, _: RecyclerView -> <span class="comment">/*load more*/</span> }</div><div class="line"> ))</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="with-使用"><a href="#with-使用" class="headerlink" title="with 使用"></a>with 使用</h2><p>和 <code>apply</code> 操作很类似,区别在于返回类型不限制。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">with(feedList) {</div><div class="line"> adapter = <span class="keyword">this</span><span class="symbol">@FeedActivity</span>.adapter</div><div class="line"> addOnScrollListener(EndlessScrollListener(</div><div class="line"> layoutManager,</div><div class="line"> { _: <span class="built_in">Int</span>, _: RecyclerView -> <span class="comment">/*load more*/</span> }</div><div class="line"> ))</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="use-使用"><a href="#use-使用" class="headerlink" title="use 使用"></a>use 使用</h2><p>类似 <code>Java</code> 中的 <code>try-with-resource</code>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">File file = <span class="keyword">new</span> File(<span class="string">"config.properties"</span>);</div><div class="line"><span class="keyword">try</span> (BufferedReader reader = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> FileReader(file))) {</div><div class="line"> reader.lines().forEach(System.out::println);</div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">BufferedReader(FileReader(File(<span class="string">"config.propertie"</span>))).use {</div><div class="line"> it.forEachLine(::println)</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="综合使用"><a href="#综合使用" class="headerlink" title="综合使用"></a>综合使用</h2><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">idConfigs[id]?.let {</div><div class="line"> it.apply {</div><div class="line"> debug = <span class="string">"TRUE"</span></div><div class="line"> level = <span class="string">"HIGH"</span></div><div class="line"> ...</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="comment">// simplify</span></div><div class="line">idConfigs[id]?.apply {</div><div class="line"> debug = <span class="string">"TRUE"</span></div><div class="line"> level = <span class="string">"HIGH"</span></div><div class="line"> ...</div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">createDefaultDownloader</span><span class="params">()</span></span>: Downloader {</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> Class.forName(<span class="string">"com.squareup.okhttp.OkHttpClient"</span>)</div><div class="line"> <span class="keyword">return</span> OkHttpDownloader()</div><div class="line"> } <span class="keyword">catch</span> (ignore: ClassNotFoundException) {}</div><div class="line"> <span class="keyword">return</span> UrlConnectionDownloader()</div><div class="line">}</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">ImageLoader</span> <span class="keyword">private</span> <span class="keyword">constructor</span></span>(</div><div class="line"> <span class="keyword">private</span> <span class="keyword">val</span> downloader: Downloader? = <span class="literal">null</span>,</div><div class="line"> <span class="keyword">private</span> <span class="keyword">val</span> debug: <span class="built_in">Boolean</span> = <span class="literal">true</span>) {</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="keyword">constructor</span>(builder: Builder)</div><div class="line"> : <span class="keyword">this</span>(builder.downloader, builder.debug)</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">load</span><span class="params">(url: <span class="type">String</span>)</span></span> { <span class="comment">/* load image */</span> }</div><div class="line"></div><div class="line"> <span class="keyword">companion</span> <span class="keyword">object</span> {</div><div class="line"> <span class="keyword">inline</span> <span class="function"><span class="keyword">fun</span> <span class="title">build</span><span class="params">(block: <span class="type">Builder</span>.()</span></span> -> <span class="built_in">Unit</span>) = Builder().apply(block).build()</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Builder</span> </span>{</div><div class="line"> <span class="keyword">var</span> downloader: Downloader? = <span class="literal">null</span></div><div class="line"> <span class="keyword">var</span> debug: <span class="built_in">Boolean</span> = <span class="literal">true</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">build</span><span class="params">()</span></span> {</div><div class="line"> ImageEngine(<span class="keyword">this</span>)</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">ImageLoader.build {</div><div class="line"> downloader = createDefaultDownloader()</div><div class="line"> debug = BuildConfig.DEBUG</div><div class="line">}</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p><code>Kotlin Standard</code> 库中 <code>let</code>、<code>apply</code>、<code>with</code>、<code>use</code> 的使用方法。</p>
</summary>
<category term="Kotlin" scheme="http://yoursite.com/tags/Kotlin/"/>
</entry>
<entry>
<title>Kotlin 中的集合操作</title>
<link href="http://yoursite.com/2017/07/12/kotlin-collections/"/>
<id>http://yoursite.com/2017/07/12/kotlin-collections/</id>
<published>2017-07-12T10:08:20.000Z</published>
<updated>2017-08-31T09:58:35.938Z</updated>
<content type="html"><![CDATA[<p><code>Kotlin</code> 集合操作记录</p>
<a id="more"></a>
<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul>
<li>集合的函数式操作</li>
<li>集合懒加载</li>
</ul>
<h2 id="集合的函数式操作"><a href="#集合的函数式操作" class="headerlink" title="集合的函数式操作"></a>集合的函数式操作</h2><p><strong>断言型操作</strong>,应用 <code>Predicate</code> 函数式接口。</p>
<p>示例:</p>
<ul>
<li>all(所有元素符合断言条件)</li>
<li>any(有元素符合断言条件)</li>
<li>count(统计符合断言条件的元素)</li>
<li>find(查找符合断言条件的元素)</li>
</ul>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> moreThan2 = { num: <span class="built_in">Int</span> -> num > <span class="number">2</span> }</div><div class="line"><span class="keyword">val</span> numbers = listOf(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)</div><div class="line"></div><div class="line"><span class="keyword">val</span> allMatched = numbers.all(moreThan2)</div><div class="line">> <span class="literal">false</span></div><div class="line"></div><div class="line"><span class="keyword">val</span> anyMatched = numbers.any(moreThan2)</div><div class="line">> <span class="literal">true</span></div><div class="line"></div><div class="line"><span class="keyword">val</span> matchedCount = numbers.count(moreThan2)</div><div class="line">> <span class="number">3</span></div><div class="line"></div><div class="line"><span class="comment">// 等价于 numbers.firstOrNull(moreThan2)</span></div><div class="line"><span class="keyword">val</span> findFirstMatched: <span class="built_in">Int</span>? = numbers.find(moreThan2)</div><div class="line">> <span class="number">3</span></div><div class="line"></div><div class="line"><span class="keyword">val</span> findLastMatched: <span class="built_in">Int</span>? = numbers.findLast(moreThan2)</div><div class="line">> <span class="number">5</span></div></pre></td></tr></table></figure>
<p><strong>分组操作</strong></p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> apples = listOf(</div><div class="line"> Apple(<span class="number">10</span>, <span class="string">"Red"</span>),</div><div class="line"> Apple(<span class="number">10</span>, <span class="string">"Green"</span>),</div><div class="line"> Apple(<span class="number">20</span>, <span class="string">"Red"</span>),</div><div class="line"> Apple(<span class="number">20</span>, <span class="string">"Green"</span>))</div><div class="line"><span class="keyword">val</span> groupByColor = apples.groupBy(Apple::color)</div><div class="line">groupByColor.values.forEach(System.<span class="keyword">out</span>::println)</div></pre></td></tr></table></figure>
<p><strong>映射操作、扁平化操作</strong></p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">data</span> <span class="class"><span class="keyword">class</span> <span class="title">Artist</span></span>(<span class="keyword">val</span> name: String, <span class="keyword">val</span> musics :List<String>)</div><div class="line"></div><div class="line"><span class="keyword">val</span> artists = listOf(</div><div class="line"> Artist(<span class="string">"Leslie"</span>, listOf(<span class="string">"当年情"</span>, <span class="string">"风继续吹"</span>, <span class="string">"追"</span>)),</div><div class="line"> Artist(<span class="string">"王杰"</span>, listOf(<span class="string">"谁明浪子心"</span>, <span class="string">"是否我真的一无所有"</span>)))</div></pre></td></tr></table></figure>
<p><strong>映射操作:</strong></p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">artists.map { it.musics }</div></pre></td></tr></table></figure>
<p><img src="https://git.oschina.net/jocens/source/raw/master/map.png" alt=""></p>
<p><strong>扁平化操作:</strong></p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">artists.map { it.musics }.flatten()</div></pre></td></tr></table></figure>
<p><img src="https://git.oschina.net/jocens/source/raw/master/flatten.png" alt=""></p>
<p><strong>两者结合就是 <code>flatMap</code> 操作:</strong></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">artists.flatMap { it.musics }</div></pre></td></tr></table></figure>
<p><img src="https://git.oschina.net/jocens/source/raw/master/flatmap.png" alt=""></p>
<h2 id="集合懒加载"><a href="#集合懒加载" class="headerlink" title="集合懒加载"></a>集合懒加载</h2><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">artists</div><div class="line"> <span class="comment">// 存储映射结果的集合</span></div><div class="line"> .map(Artist::name)</div><div class="line"> <span class="comment">// 存储过滤结果的集合</span></div><div class="line"> .filter { it.startsWith(<span class="string">"L"</span>) }</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">artists.asSequence()</div><div class="line"> .map(Artist::name)</div><div class="line"> .filter { it.startsWith(<span class="string">"L"</span>) }</div><div class="line"> <span class="comment">// 转换结果序列为一个 List 结果, 中间没有创建存储元素的集合</span></div><div class="line"> .toList()</div></pre></td></tr></table></figure>
<p>使用该操作的结果和前面是一样的,但是区别在于后者并没有在过程中创建两个集合。在 <code>Kotlin</code> 中 <code>Sequence</code> 接口是延迟收集元素操作的关键。该接口表示列举所有元素的一个序列。该接口仅提供一个方法 <code>iterator()</code>,你可以使用迭代器从序列中获取值。该接口的优势在于它实现的操作方法。序列中的元素均被延迟计算。因此,你可以使用序列来高效执行对集合的操作,而不需要存储中间的操作结果集。</p>
<p>你能通过集合的拓展函数(<code>asSequence()</code>)把集合转换为序列。调用 <code>toList()</code> 进行反向转换。为什么需要把序列转换回集合?这样不是更方便?然而答案是:<strong>有时</strong>。如果你需要遍历序列中的所有元素,就可以直接使用序列。如果你需要使用其他 <code>API</code> 的方法,如按照索引访问元素,那么就需要把序列转换为集合(<code>List</code>)了。</p>
<p>一个好的建议:当在大型集合上有链式操作的时候,使用序列化。</p>
<p>在序列上操作分为<strong>过渡操作和终止操作</strong>两种类型。<strong>过渡操作</strong>返回另外一个序列,表明如何转换原始序列。而<strong>终止操作</strong>返回一个结果,可能是集合、元素、数字和其他对象。</p>
<p><strong>序列的整个链式操作过程中</strong>如果没有出现终止符,那么就不会执行操作。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> first = artists.asSequence()</div><div class="line"> .map { it.name }</div><div class="line"> .filter { it.startsWith(<span class="string">"L"</span>) }</div></pre></td></tr></table></figure>
<p>上面的 <code>map</code> 和 <code>filter</code> 操作是不会执行的。只有<strong>终止操作符</strong>的出现才会执行,如下所示:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> artists = listOf(</div><div class="line"> Artist(<span class="string">"王杰"</span>, listOf(<span class="string">"谁明浪子心"</span>, <span class="string">"是否我真的一无所有"</span>)),</div><div class="line"> Artist(<span class="string">"Leslie"</span>, listOf(<span class="string">"当年情"</span>, <span class="string">"风继续吹"</span>, <span class="string">"追"</span>)),</div><div class="line"> Artist(<span class="string">"Lady Gaga"</span>, listOf(<span class="string">"Just Dance"</span>, <span class="string">"Poker Face"</span>)))</div><div class="line"></div><div class="line">artists.asSequence()</div><div class="line"> .map { it.name }</div><div class="line"> .filter { it.startsWith(<span class="string">"L"</span>) }</div><div class="line"> .firstOrNull()</div></pre></td></tr></table></figure>
<p>没有 <code>asSequence()</code> 的情况是基于基于整个集合。有 <code>asSequence()</code> 的懒加载模式是基于每个元素。</p>
<p>即上述代码:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">(map -> map -> map) -> (filter -> filter -> filter) -> finish</div><div class="line"></div><div class="line">(map -> filter -> next)</div><div class="line">(map -> filter -> finish)</div></pre></td></tr></table></figure>
<p>另外操作符顺序对集合性能也有影响。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> mapCount = <span class="number">0</span></div><div class="line"><span class="keyword">var</span> filterCount = <span class="number">0</span></div><div class="line"></div><div class="line">listOf(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>)</div><div class="line"> .asSequence()</div><div class="line"> .filter {</div><div class="line"> filterCount++</div><div class="line"> it % <span class="number">2</span> == <span class="number">0</span></div><div class="line"> }</div><div class="line"> .map {</div><div class="line"> mapCount++</div><div class="line"> it * it</div><div class="line"> }</div><div class="line"> .toList()</div><div class="line">println(mapCount) <span class="comment">// 2</span></div><div class="line">println(filterCount) <span class="comment">// 4</span></div><div class="line"></div><div class="line"><span class="keyword">var</span> mapCount = <span class="number">0</span></div><div class="line"><span class="keyword">var</span> filterCount = <span class="number">0</span></div><div class="line"></div><div class="line">listOf(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>)</div><div class="line"> .asSequence()</div><div class="line"> .map {</div><div class="line"> mapCount++</div><div class="line"> it * it</div><div class="line"> }</div><div class="line"> .filter {</div><div class="line"> filterCount++</div><div class="line"> it % <span class="number">2</span> == <span class="number">0</span></div><div class="line"> }</div><div class="line"> .toList()</div><div class="line">println(mapCount) <span class="comment">// 4</span></div><div class="line">println(filterCount) <span class="comment">// 4</span></div></pre></td></tr></table></figure>
<p><code>Java 8 Stream</code> 和 <code>Kotlin Sequences</code> 基本类似,之所以 <code>Kotlin</code> 重新实现是因为平台的问题,比方说 <code>Android</code> 只有 <code>API</code> 大于等于 23 才能使用 <code>Java 8</code>。并且 <code>Java 8 Stream</code> 支持并行。</p>
<p><code>Kotlin</code> 除了可以把集合转换为序列以外,还提供了相关的 <code>API</code> 生成序列。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> naturalNumbers = generateSequence(<span class="number">0</span>) { it + <span class="number">1</span> }</div><div class="line"><span class="keyword">val</span> numbersTo100 = naturalNumbers.takeWhile { it <= <span class="number">100</span> }</div><div class="line">println(numbersTo100.sum())</div></pre></td></tr></table></figure>
<ol>
<li>提供一个生成规则(定义一个初始值为 0 并每次加一且加一后的结果作为下一次的初始值的规则)</li>
<li>提供一个有限范围条件(当生成的值小于等于 100 一直获取并存为一个集合)</li>
<li>使用终止操作符开始执行</li>
</ol>
<p>集合的其余直接看源码 <code>API</code> 依次类推便可。</p>
]]></content>
<summary type="html">
<p><code>Kotlin</code> 集合操作记录</p>
</summary>
<category term="Kotlin" scheme="http://yoursite.com/tags/Kotlin/"/>
</entry>
<entry>
<title>Kotlin 中 Lambda 的使用</title>
<link href="http://yoursite.com/2017/07/10/kotlin-lambda/"/>
<id>http://yoursite.com/2017/07/10/kotlin-lambda/</id>
<published>2017-07-10T01:13:31.000Z</published>
<updated>2017-09-08T02:15:28.473Z</updated>
<content type="html"><![CDATA[<p><code>Kotlin Lambda</code> 记录</p>
<a id="more"></a>
<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul>
<li><code>Java</code> 中 <code>Lambda</code> 的简介</li>
<li><code>Kotlin</code> 中 <code>Lambda</code> 表达式介绍</li>
</ul>
<h2 id="Java-中-Lambda-的简介"><a href="#Java-中-Lambda-的简介" class="headerlink" title="Java 中 Lambda 的简介"></a>Java 中 Lambda 的简介</h2><p><code>Lambda</code> 表达式是在 <code>Java 8</code> 开始引入一项重大的改变。简而言之,<code>Lambda</code> 表达式是一小段的代码块用来简化代码和更方便面向函数式编程。</p>
<p><strong>Lambda 作为方法参数的使用</strong></p>
<p>编程中,传递和存储这些行为代码(表达式)是常见的。比方说:当某个事件触发,你需要去处理或者应用某个操作作用于数据结构的所有元素身上。</p>
<p><strong>在 <code>Java 8</code> 以下</strong>,我们通常借助匿名内部类实现:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> <span class="comment">// run a task</span></div><div class="line"> }</div><div class="line">});</div></pre></td></tr></table></figure>
<p>那么对于函数式编程来说,函数作为一等公民,我们可以视为一个值。函数可以作为值进行传递,而不是声明一个类然后将该类传递给方法。让函数作为值传递,你也可以不需要声明一个函数,相反,你可以直接传递一个代码块作为函数参数传递。</p>
<p>上述概念用数学表达式来说:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">f(x) = x + 2</div><div class="line">y(x) = f(x) + 3</div></pre></td></tr></table></figure>
<p>这里 <code>f(x)</code> 完全可以视为值(3)的另一种表达。传递给 <code>y(x)</code> 作为运算的一部分处理。</p>
<p>对应到编程上,<code>f(x)</code> 作为一个函数传递给 <code>y(x)</code> 动态影响 <code>y(x)</code> 最终结果的形成。有助于函数更灵活和方便拓展。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">new</span> Runnable() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> <span class="comment">// run a task</span></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p><strong>在 <code>Java 8</code> 中</strong>我们可以替换为:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// f(x) = x + 2</span></div><div class="line">Runnable task = () -> { <span class="comment">//run a task }</span></div></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// y(x) = f(x) + 3</span></div><div class="line"><span class="keyword">new</span> Thread(task)</div><div class="line"></div><div class="line"><span class="comment">// 也可以不声明函数仅传递代码块</span></div><div class="line"><span class="keyword">new</span> Thread( () -> { <span class="comment">// run a task } )</span></div></pre></td></tr></table></figure>
<p>其中 <code>() -> { // run a task }</code> 便是 <code>Lambda</code> 表达式,语法规则如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">函数 = ( 参数 ) -> { 运算逻辑 }</div></pre></td></tr></table></figure>
<p>有一点需要注意,<code>Lamdba</code> 更偏向于我们所认知的数学函数。即一个函数只有一个函数逻辑,同样的值产生同样的结果。</p>
<p>即:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">f(x) = x + 1</div><div class="line">x = 1</div><div class="line">f(1) = 1 + 1 = 2</div></pre></td></tr></table></figure>
<p>不存在说:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">f(x) = x + 1</div><div class="line">f(x) = x + 2 </div><div class="line">x = 1</div><div class="line">f(1) = 1 or 3</div></pre></td></tr></table></figure>
<p>因此这也对应 <code>Java 8</code> 中函数式接口注解(<code>@FunctionalInterface</code>)的概念,比如:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">@FunctionalInterface</div><div class="line">public interface Runnable {</div><div class="line"> public abstract void run();</div><div class="line">}</div></pre></td></tr></table></figure>
<p>对应数学可以理解成 <code>f(x) = x</code>,其中 <code>x = 0</code>。</p>
<p>更多函数式的接口可以看 <code>java.util.function</code> 包下的定义。</p>
<h2 id="Kotlin-中-Lambda-表达式介绍"><a href="#Kotlin-中-Lambda-表达式介绍" class="headerlink" title="Kotlin 中 Lambda 表达式介绍"></a>Kotlin 中 Lambda 表达式介绍</h2><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">{ 参数, 参数 -> 实现逻辑 }</div></pre></td></tr></table></figure>
<p>创建一个 <code>Lambda</code> 表达式:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> sum = { num1: <span class="built_in">Int</span>, num2: <span class="built_in">Int</span> -> num1 + num2 }</div><div class="line"><span class="keyword">val</span> actual = sum(<span class="number">1</span>, <span class="number">2</span>)</div><div class="line"><span class="keyword">val</span> expected = <span class="number">3</span></div><div class="line">Assert.assertEquals(expected, actual)</div></pre></td></tr></table></figure>
<p>直接执行一个 <code>Lambda</code></p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">{ println(<span class="string">"Averson"</span>) }()</div></pre></td></tr></table></figure>
<p>上述代码没有什么可读性,可以使用 <code>kotlin</code> 提供的 run 执行一个<code>lambda</code> 更具备可读性。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">run { println(<span class="string">"Averson"</span>) }</div></pre></td></tr></table></figure>
<p>完整语法:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">IntStream.range(<span class="number">1</span>, <span class="number">10</span>)</div><div class="line"> .forEach({ number: <span class="built_in">Int</span> -></div><div class="line"> println(number)</div><div class="line"> })</div></pre></td></tr></table></figure>
<p>虽然较之于匿名内部类有了很大的改变,但是就看上去还有几点显得啰嗦:</p>
<ul>
<li>太多标点符号,阅读上不痛不痒的。</li>
<li>参数类型能从上下文中推断出来,因此可以省略。</li>
<li>该代码没必要在 <code>Lambda</code> 指定一个参数名(<code>it</code>)。</li>
</ul>
<p>在 <code>Kotlin</code> 中,当 <code>Lambda</code> 表达式是函数最后一个参数时,<code>Lambda</code> 可以移出函数的圆括号外。上面的代码中只有一个参数,所以我们可以把 <code>Lambda</code> 移到函数的圆括号后面。</p>
<p>综合上面说的我们可以简化一下:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">IntStream.range(<span class="number">1</span>, <span class="number">10</span>)</div><div class="line"> .forEach() {</div><div class="line"> println(it)</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>再者当函数只有一个参数的时候 “()” 也可以直接省略了,所以最后的结果是:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">IntStream.range(<span class="number">1</span>, <span class="number">10</span>)</div><div class="line"> .forEach {</div><div class="line"> println(it)</div><div class="line"> }</div></pre></td></tr></table></figure>
<p>良好的编程风格包括 <strong>“编写更少的重复代码”</strong> 这一原则。在一些集合操作中,我们并不能完全符合这一原则。比如:常见的数据收集,我们不断重复进行 <code>for</code> 循环这些繁琐的代码。因此 <code>Kotlin</code> 提供良好的 <code>API</code> 来让我们减少这些繁琐的工作(<code>Java 8 Stream</code> 也可以做这事,主题原因不多加赘述增加篇幅)。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">data</span> <span class="class"><span class="keyword">class</span> <span class="title">Feed</span></span>(<span class="keyword">val</span> content: String, <span class="keyword">val</span> tag: String)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">listHotFeeds</span><span class="params">(feeds: <span class="type">List</span><<span class="type">Feed</span>>)</span></span>: List<Feed> {</div><div class="line"> <span class="keyword">val</span> hotFeeds = ArrayList<Feed>()</div><div class="line"> <span class="keyword">for</span> (feed <span class="keyword">in</span> feeds) {</div><div class="line"> <span class="keyword">if</span> (feed.tag == <span class="string">"Hot"</span>) {</div><div class="line"> hotFeeds.add(feed)</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> hotFeeds</div><div class="line">}</div></pre></td></tr></table></figure>
<p>对于这段代码曾几何时我们更想如果可以写成 <code>资讯.是热门资讯.收集</code> 这样该多好,因为这样的代码看起来更像一种自然的语言描述。当然这在 <code>Kotlin</code> 中是很容易实现的。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">listHotFeeds</span><span class="params">(feeds: <span class="type">List</span><<span class="type">Feed</span>>)</span></span>: List<Feed> {</div><div class="line"> <span class="keyword">return</span> feeds.filter { feed -> feed.tag == <span class="string">"Hot"</span> }.toList()</div><div class="line">}</div></pre></td></tr></table></figure>
<p>再如我们想筛选出内容最长的资讯。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">findMaxContentFeed</span><span class="params">(feeds: <span class="type">List</span><<span class="type">Feed</span>>)</span></span>: Feed? {</div><div class="line"> <span class="keyword">var</span> maxContentFeed: Feed? = <span class="literal">null</span></div><div class="line"> <span class="keyword">var</span> maxLength = <span class="number">0</span></div><div class="line"> <span class="keyword">for</span> (feed <span class="keyword">in</span> feeds) {</div><div class="line"> <span class="keyword">val</span> length = feed.content.length</div><div class="line"> <span class="keyword">if</span> (length > maxLength) {</div><div class="line"> maxLength = length</div><div class="line"> maxContentFeed = feed</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> maxContentFeed</div><div class="line">}</div></pre></td></tr></table></figure>
<p>然而对于问题的本质思考我们第一时间应该想到的自然语言描述是 <code>资讯.根据最长内容得出结果</code>,显然这在 <code>Kotlin</code> 的标准库中也提供了。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">findMaxContentFeed</span><span class="params">(feeds: <span class="type">List</span><<span class="type">Feed</span>>)</span></span>: Feed? {</div><div class="line"> <span class="keyword">return</span> feeds.maxBy { it.content.length }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>简化的目的是为了可读性,尽管 <code>Lambda</code> 可以简化代码,但有的时候 <code>Lambda</code> 并不能增进可读性。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> upperCaseLatters = IntStream.range(<span class="number">65</span>, <span class="number">90</span>)</div><div class="line"> .mapToObj { it.toChar() }</div><div class="line"> .map { Character.toString(it) }</div><div class="line"> .toList()</div><div class="line"><span class="keyword">val</span> result = upperCaseLatters.joinToString(<span class="string">"-"</span>) { it }</div><div class="line">println(result)</div></pre></td></tr></table></figure>
<p>显然上述的 <code>joinToString("-") { it }</code> 并不能有助于阅读。</p>
<p>所以我们应该写成:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">upperCaseLetter.joinToString(separator = <span class="string">"-"</span>, transform = { it })</div></pre></td></tr></table></figure>
<p>修改后我们可以很明确的知道了 “-“ 是分割符,而内容不需要转换操作,所以直接返回内容本身。</p>
<p>简而言之,具体问题具体分析,不管代码用什么方式写,只要保证一眼看上去清晰明了即可。</p>
<p>上述讲了很多都是 <code>Lambda</code> 内部的操作,现在看一下<strong>Lambda 访问局部变量</strong>。</p>
<p>在 Java 中当方法体内声明一个匿名内部类,你能在匿名内部类中引用方法参数和方法内的变量。而 <code>Lambda</code> 也能做到这些事。</p>
<p>比如我们为 Collection<string> 添加一个拓展函数用作 debug 用途:</string></p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> Collection<span class="type"><String></span>.<span class="title">log</span><span class="params">(tag: <span class="type">String</span>)</span></span> {</div><div class="line"> <span class="comment">// 在 Lambda 中访问参数 tag</span></div><div class="line"> forEach { println(<span class="string">"<span class="variable">$tag</span>: <span class="variable">$it</span>"</span>) }</div><div class="line">}</div></pre></td></tr></table></figure>
<p><code>Java</code> 和 <code>Kotlin</code> 的一个重要区别在于:在 <code>Kotlin</code> 的 <code>Lambda</code> 中不局限于访问 <code>final</code> 变量,你还可以修改变量值。</p>
<p>比方说在 <code>Java</code> 中:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">final</span> <span class="keyword">int</span> temp = <span class="number">1</span>;</div><div class="line"><span class="keyword">new</span> Thread(() -> {</div><div class="line"> println(temp); <span class="comment">// access temp must is final</span></div><div class="line"> temp = <span class="number">2</span>; <span class="comment">// not allow</span></div><div class="line">}).start();</div></pre></td></tr></table></figure>
<p>折衷的处理办法是:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">final</span> <span class="keyword">int</span>[] temp = { <span class="number">1</span> };</div><div class="line"><span class="keyword">new</span> Thread(() -> {</div><div class="line"> temp[<span class="number">0</span>] = <span class="number">2</span>;</div><div class="line">}).start();</div></pre></td></tr></table></figure>
<p>而 <code>Kotlin</code> 中你可以访问变量和修改变量(本质和折衷的方法异曲同工):</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> temp = <span class="number">1</span></div><div class="line">Thread {</div><div class="line"> println(temp)</div><div class="line"> temp = <span class="number">2</span></div><div class="line"> println(temp)</div><div class="line">}.start()</div></pre></td></tr></table></figure>
<p>因为当你访问一个 <code>val</code> 变量的时候和 <code>Java</code> 的没区别,但是对于非 <code>final</code> 的变量来说又是为什么它不用 <code>final</code> 而且还可以修改呢?本质上其实是将值包裹进一个对象里,在 <code>Lambda</code> 中操作这个对象,对象是不可变的,但是对象内部的值是可变的。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">final</span> IntRef temp = new IntRef();</div><div class="line">temp.element = <span class="number">1</span>;</div><div class="line">new Thread(() -> {</div><div class="line"> int var1 = temp.element;</div><div class="line"> System.<span class="keyword">out</span>.println(var1);</div><div class="line"> temp.element = <span class="number">2</span>;</div><div class="line"> var1 = temp.element</div><div class="line"> System.<span class="keyword">out</span>.println(var1);</div><div class="line">})</div></pre></td></tr></table></figure>
<p>需要注意一点:如果 <code>Lambda</code> 作为事件处理逻辑,或者异步执行。那么只有在执行 <code>Lambda</code> 的时候变量才会发声修改。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">countButtonClicks</span><span class="params">(button: <span class="type">Button</span>)</span></span>: <span class="built_in">Int</span> {</div><div class="line"> <span class="keyword">var</span> clicks = <span class="number">0</span></div><div class="line"> button.setOnclick {</div><div class="line"> clicks++;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> clicks</div><div class="line">}</div></pre></td></tr></table></figure>
<p>上述函数永远返回 0。尽管按钮点击修改了 <code>clicks</code>,但是你并观察不到变化,因为 <code>onClick</code> 的处理在返回 <code>clicks</code> 之后。一个正确的处理方式应该是让 <code>clicks</code> 不作为局部变量而是作为一个类的属性进行存储。</p>
<p><strong>接下来看一下成员引用</strong>,设想一下,如果你需要作为参数传递的代码块被定义为函数。那该怎么处理?</p>
<p>答案是:你可以传递一个调用该函数的 <code>Lambda</code>( { func() } ),但是这样做是多余的。在 <code>Kotlin</code> 中和 <code>Java 8</code> 一样,如果你需要把函数转化为一个值传递你可以使用 <code>::</code> 来表示。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 语法</span></div><div class="line">Class::member</div><div class="line"><span class="comment">// 使用</span></div><div class="line"><span class="keyword">val</span> getAppleWeight = Apple::weight</div><div class="line">apples.maxBy(getAppleWeight)</div></pre></td></tr></table></figure>
<p><code>Apple::weight</code> 相当于:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> getAppleWeight = { apple:Apple -> apple.weight }</div></pre></td></tr></table></figure>
<p>函数也是同理,也可以直接省去函数赋值的步骤。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">runTask</span><span class="params">()</span></span> {</div><div class="line"> <span class="keyword">val</span> welcome = ::welcome</div><div class="line"> Thread(welcome).start()</div><div class="line"> <span class="comment">// or</span></div><div class="line"> Thread(::welcome).start()</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">welcome</span><span class="params">()</span></span> {</div><div class="line"> println(<span class="string">"welcome"</span>)</div><div class="line">}</div></pre></td></tr></table></figure>
<p>构造函数:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">data</span> <span class="class"><span class="keyword">class</span> <span class="title">Apple</span></span>(<span class="keyword">val</span> weight: <span class="built_in">Int</span>, <span class="keyword">val</span> color: String)</div><div class="line"></div><div class="line"><span class="keyword">val</span> newApple = ::Apple</div><div class="line"><span class="keyword">val</span> apple = newApple(<span class="number">10</span>, <span class="string">"Green"</span>)</div><div class="line">println(apple)</div></pre></td></tr></table></figure>
<p>拓展函数:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">fun</span> Apple.<span class="title">isHealth</span><span class="params">()</span></span> = weight > <span class="number">20</span></div><div class="line"><span class="keyword">val</span> predicate = Apple::isHealth</div></pre></td></tr></table></figure>
<blockquote>
<p>在 Kotlin 1.1 之前当你引用方法和属性的时候总需要传入实例,1.1 之后已经不需要了</p>
</blockquote>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 1.0</span></div><div class="line"><span class="keyword">val</span> apple = Apple(<span class="number">10</span>, <span class="string">"Green"</span>)</div><div class="line"><span class="keyword">val</span> getAppleWeight = Apple::weight</div><div class="line"><span class="keyword">val</span> weight = getAppleWeight(apple)</div><div class="line"><span class="comment">// 1.1</span></div><div class="line"><span class="keyword">val</span> getAppleWeight = apple::weight</div><div class="line"><span class="keyword">val</span> weight = getAppleWeight()</div></pre></td></tr></table></figure>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 前者类似于</span></div><div class="line">int getWeight(Apple apple) {</div><div class="line"> <span class="keyword">return</span> apple.getWeight()</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 后者</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Wrapper</span></span>(<span class="keyword">private</span> <span class="keyword">val</span> apple: Apple) {</div><div class="line"> <span class="function"><span class="keyword">fun</span> <span class="title">getWeight</span><span class="params">()</span></span> {</div><div class="line"> <span class="keyword">this</span>.apple.weight</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>仅需记住一点,使用 <code>::</code> 语法时保证入参和返回值一致即可。 </p>
<p>最后,在 <code>Kotlin</code> 中你可以传递 <code>Lambda</code> 给参数为函数式接口的 <code>Java</code> 方法。编译器会自动转换为对应的接口实例。比如:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> cacheExecutor = Executors.newCachedThreadPool()</div><div class="line">cacheExecutor.submit({ println(<span class="string">"Hello"</span>) })</div></pre></td></tr></table></figure>
<p>传递 <code>Lambda</code> 给 <code>submit</code> 方法过程中我们创建了一个实现了 <code>Runnable</code> 接口的匿名内部类并且编译器将把 <code>lambda</code> 中的逻辑作为<strong>函数式接口</strong>中唯一方法的主体。即 <code>{ println("Hello") }</code> 看作 <code>Java</code> 中的:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> println(<span class="string">"Hello"</span>);</div><div class="line">}</div><div class="line">`</div></pre></td></tr></table></figure>
<p>当然也可以明确指定匿名内部类</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">cacheExecutor.submit(<span class="keyword">object</span> : Runnable {</div><div class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">run</span><span class="params">()</span></span> {</div><div class="line"> println(<span class="string">"Hello"</span>)</div><div class="line"> }</div><div class="line">})</div></pre></td></tr></table></figure>
<p>那么 <code>lambda</code> 和 <code>object class</code> 的区别在哪里?</p>
<p>区别在于 <code>object class</code> 每次都会创建一个匿名类实例,而对于 <code>lambda</code>,如果没有访问外部变量,那么它对应的匿名类实例是可以重用的。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">val</span> task = { println(<span class="string">"execute logic"</span>) }</div><div class="line"></div><div class="line"><span class="keyword">val</span> cacheExecutor = Executors.newCachedThreadPool()</div><div class="line">cacheExecutor.submit(task)</div><div class="line"></div><div class="line"><span class="keyword">val</span> singleExecutor = Executors.newSingleThreadExecutor()</div><div class="line">singleExecutor.submit(task)</div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">fun</span> <span class="title">runTask</span><span class="params">(tag: <span class="type">String</span>)</span></span> {</div><div class="line"> cacheExecutor.submit({ println(<span class="string">"run <span class="variable">$tag</span> task"</span>) })</div><div class="line"> <span class="comment">// or</span></div><div class="line"> cacheExecutor.submit(<span class="keyword">object</span> : Runnable {</div><div class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">run</span><span class="params">()</span></span> {</div><div class="line"> println(<span class="string">"run <span class="variable">$tag</span> task"</span>)</div><div class="line"> }</div><div class="line"> })</div><div class="line">}</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p><code>Kotlin Lambda</code> 记录</p>
</summary>
<category term="Kotlin" scheme="http://yoursite.com/tags/Kotlin/"/>
</entry>
<entry>
<title>Google Architecture Components - Lifecycle</title>
<link href="http://yoursite.com/2017/07/08/arch-lifecycle/"/>
<id>http://yoursite.com/2017/07/08/arch-lifecycle/</id>
<published>2017-07-08T14:36:28.000Z</published>
<updated>2017-07-27T14:38:20.305Z</updated>
<content type="html"><![CDATA[<p>关于 <code>Arch Lifecycle</code> 的一些摘要。</p>
<a id="more"></a>
<h2 id="Lifecycle"><a href="#Lifecycle" class="headerlink" title="Lifecycle"></a>Lifecycle</h2><p><code>Lifecycle</code> 是一个具有生命周期感知的组件。该组件可以根据 <code>Activity</code> 和 <code>Fragment</code> 的生命周期提供相关的调整行为,而好的生命周期控制能有效避免内存泄漏和应用崩溃。</p>
<p>官方文档有代码,只记录关键点,下同。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStart</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">super</span>.onStart();</div><div class="line"> locationListener.start();</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStop</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">super</span>.onStop();</div><div class="line"> locationListener.stop();</div><div class="line">}</div></pre></td></tr></table></figure>
<p>当开启定位服务前我们应该做一些相关的检查和配置之类的操作。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStart</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">super</span>.onStart();</div><div class="line"> Util.checkUserStatus(result -> {</div><div class="line"> <span class="keyword">if</span> (result) {</div><div class="line"> myLocationListener.start();</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStop</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">super</span>.onStop();</div><div class="line"> locationListener.stop();</div><div class="line">}</div></pre></td></tr></table></figure>
<p>那么上述的代码如果还没检查完,就关闭了界面呢?这就会导致上下文泄漏了,且随着这样的操作越来越多 <code>onStart</code> 和 <code>onPause</code> 方法中的代码会变得愈发臃肿,这个时候我们会考虑到把这些相关代码独立出去封装到一个类中,并根据 <code>Activity</code>(<code>Fragent</code>)的生命周期作出相关的操作,这样能有效的解耦和减少代码的臃肿,那么 <code>Lifecycle</code> 就是一个很好的帮手。</p>
<p>相关类:</p>
<ul>
<li><code>LifecycleOwner</code> 是一个提供 <code>Lifecycle</code> 的接口。</li>
<li><code>LifecycleObserver</code> 是一个接收生命周期事件和状态的观察者接口。</li>
<li><code>Lifecycle</code> 是一个感知生命周期的接口,它用来分发和维护生命周期的事件和状态。</li>
<li><code>Event</code> 分发的生命周期事件,对应 <code>Activity</code> 的生命周期回调,由 <code>Lifecycle</code> 发出</li>
<li><code>State</code> 表示 <code>LifecycleOwner</code> 当前的状态</li>
</ul>
<p><img src="https://git.oschina.net/jocens/source/raw/master/lifecycle-states.png" alt=""></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyLocationListener</span> <span class="keyword">implements</span> <span class="title">LifecycleObserver</span> </span>{</div><div class="line"> <span class="meta">@OnLifecycleEvent</span>(Lifecycle.Event.ON_START)</div><div class="line"> <span class="function"><span class="keyword">void</span> <span class="title">start</span><span class="params">()</span> </span>{ <span class="comment">// conenct }</span></div><div class="line"></div><div class="line"> <span class="meta">@OnLifecycleEvent</span>(Lifecycle.Event.ON_STOP)</div><div class="line"> <span class="function"><span class="keyword">void</span> <span class="title">stop</span><span class="params">()</span> </span>{ <span class="comment">// disconnect }</span></div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>{</div><div class="line"> <span class="keyword">super</span>.onCreate(savedInstanceState);</div><div class="line"> getLifecycle().addObserver(<span class="keyword">new</span> MyLocationListener());</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="最佳实践"><a href="#最佳实践" class="headerlink" title="最佳实践"></a>最佳实践</h2><ul>
<li>保持 UI Controller(Activity 或 Fragment)尽可能的精简。</li>
<li><code>UI</code> 层不应该尝试去操作数据,相反,应该使用一个 <code>ViewModel</code> 来完成,当数据变化由 <code>ViewModel</code> 通知 <code>UI</code> 更新。 </li>
<li>尝试以<a href="https://en.wikipedia.org/wiki/Data-driven_programming" target="_blank" rel="external">数据驱动</a>的模式来驱动 <code>UI Layer</code>,因为 <code>UI Layer</code> 更多根据数据来更新视图或将用户操作通知到 <code>ViewModel</code>。把你的数据放到 <code>ViewModel</code> 中,让 <code>ViewModel</code> 作为 <code>UI Layer</code> 和 <code>Business Logic Layer</code> 的桥梁。当然获取数据的职责不是由 <code>ViewModel</code> 负责。<code>ViewModel</code> 应该调用合适的组件来完成这项工作,并将结果返回到 <code>UI</code> 层。 </li>
<li>使用 <code>Data Binding</code> 让 <code>UI Controller</code> 和 <code>Views</code> 之间的更整洁,这可以减少你 <code>UI Controller</code> 中 <code>View</code> 相关的模板代码(eg:findViewById…),不过不喜欢 <code>Data Binding</code> 可以使用 <code>ButterKnife</code> 之类的库来代替。</li>