原文鏈接:SCALA STYLE GUIDE
1 縮進(jìn)(Indentation)
每一級(jí)縮進(jìn)使用2個(gè)空格。不使用制表符(Tab)。因此,不要像這樣縮進(jìn):
// 錯(cuò)誤!
class Foo {
def fourspaces = {
val x = 4
..
}
}
你應(yīng)該像這樣縮進(jìn):
// 正確!
class Foo {
def twospaces = {
val x = 2
..
}
}
Scala語言鼓勵(lì)使用數(shù)量驚人的嵌套域和邏輯塊(函數(shù)值等等)。幫自己一個(gè)忙,不要因?yàn)榇蜷_了一個(gè)新的區(qū)塊而在語法上懲罰自己。來自Java的這種風(fēng)格確實(shí)需要一點(diǎn)時(shí)間來適應(yīng),但是非常值得。
1.1 換行(Line wrapping)
有時(shí)候,一個(gè)表達(dá)式的長度達(dá)到了難以理解的程度,就是為了將其限制在一行以內(nèi)(通常該行的長度超過了80個(gè)字符)。在這種情況下,首選的方法是通過將中間結(jié)果賦值,從而將表達(dá)式分割成多個(gè)表達(dá)式。然而,這并不總是一個(gè)實(shí)際可行的解決方案。
當(dāng)一定需要將表達(dá)式分為多行時(shí),每個(gè)后續(xù)行應(yīng)該以第一行為基準(zhǔn),縮進(jìn)兩個(gè)空格。還請(qǐng)記住,Scala要求每個(gè)“換行”要么有一個(gè)非封閉的圓括號(hào),要么以一個(gè)無右邊參數(shù)的中綴方法結(jié)束,比如:
val result = 1 + 2 + 3 + 4 + 5 + 6 +
7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 +
15 + 16 + 17 + 18 + 19 + 20
如果沒有使用這種換行方法,Scala將推斷在換行行尾存在一個(gè)分號(hào),有時(shí)甚至?xí)跊]有警告的情況下取消編譯。
1.2 具有多個(gè)參數(shù)的方法(Methods with Numerous Arguments)
當(dāng)調(diào)用一個(gè)包含許多參數(shù)(5個(gè)或更多)的方法時(shí),通常需要將方法調(diào)用封裝到多行中。在這種情況下,將每個(gè)參數(shù)單獨(dú)放在一行上,從當(dāng)前縮進(jìn)級(jí)別縮進(jìn)兩個(gè)空格:
foo(
someVeryLongFieldName,
andAnotherVeryLongFieldName,
"this is a string",
3.1415)
這樣所有參數(shù)都對(duì)齊了,即便稍后更改方法的名稱,也不需要重新對(duì)齊它們。
應(yīng)該非常小心地避免一類多參數(shù)方法的調(diào)用縮進(jìn)寫法。更具體地說,當(dāng)必須將每個(gè)參數(shù)縮進(jìn)超過50個(gè)空格才能實(shí)現(xiàn)對(duì)齊時(shí),應(yīng)該避免這種調(diào)用縮進(jìn)寫法。在這種情況下,調(diào)用本身應(yīng)該移到下一行并縮進(jìn)兩個(gè)空格:
// 正確!
val myLongFieldNameWithNoRealPoint =
foo(
someVeryLongFieldName,
andAnotherVeryLongFieldName,
"this is a string",
3.1415)
// 錯(cuò)誤!
val myLongFieldNameWithNoRealPoint = foo(someVeryLongFieldName,
andAnotherVeryLongFieldName,
"this is a string",
3.1415)
更好的做法是,盡量避免任何需要超過兩個(gè)或三個(gè)參數(shù)的方法!
2 命名約定(Naming Conventions)
一般來說,Scala使用“駝峰大小寫”命名。也就是說,除了第一個(gè)單詞外,其他每個(gè)單詞首字母都大寫:
UpperCamelCase
lowerCamelCase
首字母縮略詞應(yīng)視為正常單詞:
Xhtml
maxId
而不是:
XHTML
maxID
命名中的下劃線實(shí)際上并不是編譯器所禁止的,但是由于它們?cè)赟cala語法中具有特殊的含義,因此強(qiáng)烈不鼓勵(lì)使用下劃線(例外情況請(qǐng)參見下文)。
2.1 類/特質(zhì)(Classes/Traits)
類的名稱應(yīng)該用大寫駝峰:
class MyFairLady
這模擬了類的Java命名約定。
2.2 對(duì)象(Objects)
對(duì)象名稱類似于類名稱(大寫駝峰)。例外情況是模仿包或函數(shù)。這不是很常見。示例:
object ast {
sealed trait Expr
case class Plus(e1: Expr, e2: Expr) extends Expr
...
}
object inc {
def apply(x: Int): Int = x + 1
}
2.3 包(Packages)
Scala包應(yīng)該遵循Java包命名約定:
// 錯(cuò)誤!
package coolness
// 正確! 僅將coolness._ 放在域內(nèi)
package com.novell.coolness
// 正確! 將novell._ 和coolness._都放在域內(nèi)
package com.novell
package coolness
// 正確, 用于com.novell.coolness包對(duì)象
package com.novell
/**
* 提供與“coolness”相關(guān)的類
*/
package object coolness {
}
2.3.1 根(root)
有時(shí)需要使用_root_對(duì)導(dǎo)入進(jìn)行完全限定。例如,如果另一個(gè)net在作用域內(nèi),則訪問net.liftweb,我們必須這樣寫:
import _root_.net.liftweb._
不要過度使用_root_。通常,嵌套包解析是一件好事,對(duì)于減少導(dǎo)入混亂非常有幫助。使用_root_不僅否定了它們的優(yōu)點(diǎn),而且本身也引入了額外的混亂。
2.4 方法
方法的文本(字母)名稱應(yīng)使用小寫駝峰:
def myFairMethod = ...
本節(jié)并不全面介紹Scala中的慣用方法命名。更多信息可以在方法調(diào)用部分找到。
2.4.1 訪問器/存儲(chǔ)器(Accessors/Mutators)
Scala不遵循Java約定的預(yù)掛接set / get 存儲(chǔ)器和訪問器方法。相反使用以下約定:
- 對(duì)于屬性的訪問器,方法的名稱應(yīng)該是屬性的名稱。
- 在某些情況下,在布爾訪問符前加上“
is”是可以接受的(例如isEmpty)。只有在沒有提供相應(yīng)的存儲(chǔ)器時(shí)才會(huì)出現(xiàn)這種情況。請(qǐng)注意,Lift公約附加“_?”布爾訪問器是不標(biāo)準(zhǔn)的,不能在Lift框架之外使用。 - 對(duì)于存儲(chǔ)器,方法的名稱應(yīng)該是附加“
_=”的屬性的名稱。只要在封閉類型上定義了具有該特定屬性名稱的對(duì)應(yīng)訪問器,該約定就會(huì)啟用與之對(duì)應(yīng)的存儲(chǔ)器。注意,這不僅是一種約定,而且是語言的一種要求。
class Foo {
def bar = ...
def bar_=(bar: Bar) {
...
}
def isBaz = ...
}
val foo = new Foo
foo.bar // 訪問器
foo.bar = bar2 // 存儲(chǔ)器
foo.isBaz // 布爾屬性
不幸的是,這些約定與Java約定相沖突,Java約定根據(jù)訪問器和存儲(chǔ)器所表示的屬性來命名由訪問器和存儲(chǔ)器封裝的私有字段。例如:
public class Company {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在Scala中,字段和方法之間沒有區(qū)別。實(shí)際上,字段完全由編譯器命名和控制。如果我們想采用Scala中bean getter /setter的Java約定,這是一個(gè)相當(dāng)簡單的編碼:
class Company {
private var _name: String = _
def name = _name
def name_=(name: String) {
_name = name
}
}
雖然匈牙利表示法非常難看,但它確實(shí)具有消除_name變量歧義而不混淆標(biāo)識(shí)符的優(yōu)點(diǎn)。下劃線位于前綴而不是后綴的位置,以避免誤輸入name _而不是name_的危險(xiǎn)。由于大量使用Scala的類型推斷,這樣的錯(cuò)誤可能會(huì)導(dǎo)致非?;靵y的錯(cuò)誤。
注意,Java getter/setter范式通常用于解決缺乏對(duì)屬性和綁定的第一類支持的問題。在Scala中,有一些庫支持屬性和綁定。約定是對(duì)包含自己的getter和setter的屬性類使用不可變引用。例如:
class Company {
val string: Property[String] = Property("Initial Value")
2.4.2 括號(hào)(Parentheses)
與Ruby不同,Scala重視方法是否使用括號(hào)聲明(僅適用于無參數(shù)方法)。例如:
def foo1() = ...
def foo2 = ...
這些是編譯時(shí)的不同方法。雖然可以使用或不使用圓括號(hào)調(diào)用foo1,但是不能使用圓括號(hào)調(diào)用foo2。
因此,對(duì)于何時(shí)適合聲明沒有括號(hào)的方法以及何時(shí)不適合聲明沒有括號(hào)的方法,必須遵守適當(dāng)?shù)臏?zhǔn)則,這實(shí)際上非常重要。
作為任何類型的訪問器(封裝字段或邏輯屬性)的方法都應(yīng)該不使用括號(hào)聲明,除非它們有副作用。而Ruby和Lift使用的是!為了說明這一點(diǎn),最好使用圓括號(hào)(請(qǐng)注意,出于語法的考慮,流式API和DSL往往會(huì)違反下面給出的準(zhǔn)則。這種例外不應(yīng)被認(rèn)為是一種違反,甚至當(dāng)這些規(guī)則不適用的時(shí)候。在DSL中,語法應(yīng)該高于約定)。
此外,調(diào)用點(diǎn)應(yīng)遵循聲明;如果使用圓括號(hào)聲明,則使用圓括號(hào)調(diào)用。雖然有節(jié)省一些字符的誘惑,但是如果您遵循這條原則,您的代碼將更具可讀性和可維護(hù)性。
// 不會(huì)改變狀態(tài),以birthdate方式調(diào)用
def birthdate = firstName
// 更新了內(nèi)部狀態(tài),以age()方式調(diào)用
def age() = {
_age = updateAge(birthdate)
_age
}
2.4.3 符號(hào)化方法名(Symbolic Method Names)
避免!盡管Scala在很大程度上促進(jìn)了API設(shè)計(jì)的這一領(lǐng)域,但是不應(yīng)該輕率地定義具有符號(hào)化名稱的方法,特別是當(dāng)符號(hào)本身是非標(biāo)準(zhǔn)的時(shí)候(例如,>>#>>)。一般來說,符號(hào)方法名有兩個(gè)有效的使用場(chǎng)景:
- DSL(比如:
actor1 ! Msg) - 邏輯數(shù)學(xué)運(yùn)算(比如:
a + b或c :: d)
在前一種情況下,符號(hào)方法名可以不受懲罰地使用,只要語法是有益的。但是,在標(biāo)準(zhǔn)API設(shè)計(jì)過程中,符號(hào)方法名應(yīng)該嚴(yán)格保留給純函數(shù)操作。因此,定義用于連接兩個(gè)單元(monad)的>>=方法是可以接受的,但是定義用于寫入輸出流的<<方法是不可接受的。前者在數(shù)學(xué)上定義明確,沒有副作用,而后者兩者都不是。
作為一般規(guī)則,符號(hào)化方法名在本質(zhì)上應(yīng)該被很好地理解和自注釋。經(jīng)驗(yàn)法則如下:如果您需要解釋方法的功能,那么它應(yīng)該有一個(gè)真實(shí)的、描述性的名稱,而不是符號(hào)。在一些非常罕見的情況下,可以接受創(chuàng)建新的符號(hào)方法名稱。很可能,您的API不是這種情況!
具有符號(hào)化名稱的方法定義應(yīng)該被認(rèn)為是Scala的一個(gè)高級(jí)特性,只有那些最熟悉其缺陷的人才能使用。如果不小心,過度使用符號(hào)方法名很容易將最簡單的代碼轉(zhuǎn)換成符號(hào)湯。
2.5 常量,值,變量和方法(Constants,Values,Variable and Methods)
常量名稱應(yīng)該使用大寫駝峰。與Java的static final成員類似,如果成員是final的、不可變的,并且屬于包對(duì)象或?qū)ο?,則可以將其視為常量:
object Container {
val MyConstant = ...
}
scala.math包中的Pi是常量的例子。
方法名、值名和變量名應(yīng)小寫:
val myValue = ...
def myMethod = ...
var myVariable
2.6 類型參數(shù)(泛型)(Type Parameter(generics))
對(duì)于簡單的類型參數(shù),應(yīng)該使用單個(gè)大寫字母(來自英語字母表),以A開頭(這與Java約定的以T開頭不同)。
class List[A] {
def map[B](f: A => B): List[B] = ...
}
如果類型參數(shù)具有更具體的含義,則應(yīng)使用描述性名稱,并遵循類命名約定(與全大寫風(fēng)格相反):
// 正確
class Map[Key, Value] {
def get(key: Key): Value
def put(key: Key, value: Value): Unit
}
// 錯(cuò)誤:不要使用全大寫
class Map[KEY, VALUE] {
def get(key: KEY): VALUE
def put(key: KEY, value: VALUE): Unit
}
如果類型參數(shù)的作用域足夠小,可以使用助記符來代替較長的描述性名稱:
class Map[K, V] {
def get(key: K): V
def put(key: K, value: V): Unit
}
2.6.1 高級(jí)類型和參數(shù)化類型參數(shù)(Higher-Kinds and Parameterized Type parameters)
從理論上講,高級(jí)類型與常規(guī)類型參數(shù)沒有什么不同(除了它們的類型至少是*=>*而不是簡單的*)。命名約定大致相似,但為了清晰起見,最好使用描述性名稱,而不是單個(gè)字母:
class HigherOrderMap[Key[_], Value[_]] { ... }
用單字母形式來表示一個(gè)代碼庫中使用的基本概念,如F[_]表示函子(Functor),M[_]表示單字(Monad),是可以接受的。
在這種情況下,基本概念應(yīng)該是團(tuán)隊(duì)所熟知和理解的東西,或者有第三方說明,如以下
def doSomething[M[_]: Monad](m: M[Int]) = ...
這里,類型綁定:Monad提供了必要的證據(jù)來通知讀者M[_]是Monad的類型。
2.7 注解(Annotation)
注解,比如@volatile,應(yīng)該用小寫駝峰:
class cloneable extends StaticAnnotation
整個(gè)Scala庫都使用這種約定,盡管它與Java注釋命名不一致。
注意:即使在注解上使用類型別名,這種約定也適用。例如,當(dāng)使用JDBC時(shí):
type id = javax.persistence.Id @annotation.target.field
@id
var id: Int = 0
2.8 關(guān)于簡潔的注意事項(xiàng)(Special Note on Brevity)
由于Scala源于函數(shù)語言,所以本地名稱很短是很正常的:
def add(a: Int, b: Int) = a + b
這在Java這樣的語言中是不好的實(shí)踐,但在Scala中是很好的實(shí)踐。這種約定之所以有效,是因?yàn)檫m當(dāng)編寫的Scala方法非常短,只跨越一個(gè)表達(dá)式,很少超過幾行。很少使用本地名稱(包括參數(shù)),因此沒有必要設(shè)計(jì)很長,但具有描述性的名稱。這種約定極大地提高了大多數(shù)Scala源代碼的簡潔性。這反過來又提高了可讀性,因?yàn)榇蠖鄶?shù)表達(dá)式適合在一行中,方法的參數(shù)具有描述性類型名稱。
這個(gè)約定只適用于非常簡單方法的參數(shù)(以及非常簡單類的局部字段);公共接口中的所有內(nèi)容都應(yīng)該是描述性的。還要注意,參數(shù)的名稱現(xiàn)在是類的公共API的一部分,因?yàn)橛脩艨梢栽诜椒ㄕ{(diào)用中使用命名參數(shù)。
3 類型(Types)
3.1 推斷(Inference)
在可能的情況下使用類型推斷,但要把清晰放在首位,并在公共API中支持明確性。
您幾乎不應(yīng)該注釋私有字段或局部變量的類型,因?yàn)樗鼈兊念愋屯ǔ?huì)立即在它們的值中顯示出來:
private val name = "Daniel"
但是,您可能希望仍然顯示具有復(fù)雜或不明顯形式的已賦值值(assigned value)得類型。
所有公共方法都應(yīng)該有顯式的類型注釋。在這些情況下,類型推斷可能會(huì)破壞封裝,因?yàn)樗蕾囉趦?nèi)部方法和類細(xì)節(jié)。在沒有顯式類型的情況下,對(duì)方法或val的內(nèi)部進(jìn)行更改可以在沒有警告的情況下更改類的公共API,這可能會(huì)破壞客戶機(jī)代碼。顯式類型注釋還可以幫助改進(jìn)編譯時(shí)間。
3.1.1 函數(shù)值(Function Values)
函數(shù)值支持類型推斷的特殊情況,值得單獨(dú)調(diào)用:
val ls: List[String] = ...
ls map (str => str.toInt)
在Scala已經(jīng)知道我們聲明的函數(shù)值類型的情況下,不需要注釋參數(shù)(在本例中是str)。這是一個(gè)非常有用的推論,應(yīng)該盡可能首選。注意,對(duì)函數(shù)值操作的隱式轉(zhuǎn)換將使此推斷無效,從而強(qiáng)制顯式注釋參數(shù)類型。
3.2 注解(Annotation)
類型注解應(yīng)該根據(jù)下面的模板進(jìn)行模式化:
value: Type
這是Scala標(biāo)準(zhǔn)庫和Martin Odersky所有示例所采用的樣式。值和類型之間的空格有助于眼睛準(zhǔn)確地解析語法。之所以將冒號(hào)放在值的末尾而不是類型的開頭,是為了避免在這種情況下混淆:
value :::
這實(shí)際上是有效的Scala代碼,表示聲明一個(gè)類型為::的值。顯然,前綴樣式的注釋冒號(hào)會(huì)把事情搞得一團(tuán)糟。
3.3 歸因(Ascription)
類型歸因常常與類型注解混淆,因?yàn)镾cala中的語法是相同的。下面是歸因的例子:
Nil: List[String]Set(values: _*)"Daniel": AnyRef
因?yàn)轭愋蜋z查器的緣故,歸因基本上只是在編譯時(shí)執(zhí)行的向上轉(zhuǎn)換。它的使用并不常見,但有時(shí)也會(huì)發(fā)生。歸因最常見的情況是使用單個(gè)Seq參數(shù)調(diào)用varargs方法。這是通過賦予_*類型來實(shí)現(xiàn)的(如上面的第二個(gè)例子所示)。
類型歸因遵循類型注解約定;冒號(hào)后面有空格。
3.4 函數(shù)(Functions)
函數(shù)類型應(yīng)該在參數(shù)類型、箭頭和返回類型之間聲明一個(gè)空格:
def foo(f: Int => String) = ...
def bar(f: (Boolean, Double) => List[String]) = ...
應(yīng)該盡可能省略括號(hào)(例如一個(gè)參數(shù)的方法,Int => String)。
3.4.1 參數(shù)數(shù)量-1(Arity-1)
Scala有一種特殊的語法來聲明Arity-1函數(shù)的類型。例如:
def map[B](f: A => B) = ...
具體來說,圓括號(hào)可以從參數(shù)類型中省略。因此,我們沒有將f聲明為類型(A) => B,因?yàn)檫@樣會(huì)不必要地冗長??紤]一個(gè)更極端的例子:
// 錯(cuò)誤!
def foo(f: (Int) => (String) => (Boolean) => Double) = ...
// 正確!
def foo(f: Int => String => Boolean => Double) = ...
通過省略括號(hào),我們節(jié)省了6個(gè)字符,并顯著提高了類型表達(dá)式的可讀性。
3.5 結(jié)構(gòu)類型(Structural Types)
如果結(jié)構(gòu)類型長度小于50個(gè)字符,則應(yīng)在一行中聲明它們。否則,它們應(yīng)該被分割成多行,并(通常)分配給它們自己的類型別名:
// 錯(cuò)誤!
def foo(a: { def bar(a: Int, b: Int): String; val baz: List[String => String] }) = ...
// 正確!
private type FooParam = {
val baz: List[String => String]
def bar(a: Int, b: Int): String
}
def foo(a: FooParam) = ...
更簡單的結(jié)構(gòu)類型(少于50個(gè)字符)可以內(nèi)聯(lián)聲明和使用:
def foo(a: { val bar: String }) = ...
在內(nèi)聯(lián)聲明結(jié)構(gòu)類型時(shí),每個(gè)成員之間應(yīng)該用分號(hào)和空格分隔,左大括號(hào)后面應(yīng)該跟著空格,而右大括號(hào)前面應(yīng)該跟著空格(如上面的兩個(gè)例子所示)。
結(jié)構(gòu)類型在運(yùn)行時(shí)使用反射實(shí)現(xiàn),其性能天生低于名義類型。開發(fā)人員應(yīng)該更喜歡使用名義類型,除非結(jié)構(gòu)類型提供了明顯的好處。
4 嵌套塊(Nested Blocks)
4.1 大括號(hào)(Curly Braces)
開大括號(hào)({)必須與它們所表示的聲明位于同一行:
def foo = {
...
}
從技術(shù)上講,Scala的解析器確實(shí)支持GNU風(fēng)格的表示法,在聲明之后的行上使用大括號(hào)。但是,由于分號(hào)推斷的實(shí)現(xiàn)方式,在處理這種樣式時(shí)解析器的可預(yù)測(cè)性不是很好。只要遵循上面演示的花括號(hào)約定,就可以省去許多麻煩。
4.2 括號(hào)(Parentheses)
在少數(shù)情況下,當(dāng)括號(hào)塊換行時(shí),開括號(hào)和閉括號(hào)應(yīng)該是不間隔的,通常保持在同一行的內(nèi)容(Lisp風(fēng)格):
(this + is a very ++ long *
expression)
圓括號(hào)還可以禁用分號(hào)推斷,因此允許開發(fā)人員使用一些更喜歡的操作符開始行:
( someCondition
|| someOtherCondition
|| thirdCondition
)
出于美觀的原因,在本例中,可以接受下面一行的尾隨圓括號(hào)。
5 申明(Declaration)
5.1 類(Classes)
Class/Object/Trait構(gòu)造函數(shù)應(yīng)該全部在一行中聲明,除非行變得“太長”(大約100個(gè)字符)。在這種情況下,將每個(gè)構(gòu)造函數(shù)參數(shù)放在自己的行上,并在后面加上逗號(hào):
class Person(name: String, age: Int) {
…
}
class Person(
name: String,
age: Int,
birthdate: Date,
astrologicalSign: String,
shoeSize: Int,
favoriteColor: java.awt.Color,
) {
def firstMethod: Foo = …
}
如果一個(gè)class/object/trait擴(kuò)展了任何東西,應(yīng)用同樣的一般規(guī)則,把它放在一行上,除非它超過100個(gè)字符,然后把每一項(xiàng)放在它自己的行上,后面加逗號(hào);閉括號(hào)在構(gòu)造函數(shù)參數(shù)和擴(kuò)展之間提供了可視化的分離;應(yīng)該將空行添加到與類實(shí)現(xiàn)進(jìn)一步分離的擴(kuò)展中:
class Person(
name: String,
age: Int,
birthdate: Date,
astrologicalSign: String,
shoeSize: Int,
favoriteColor: java.awt.Color,
) extends Entity
with Logging
with Identifiable
with Serializable {
def firstMethod: Foo = …
}
5.1.1 類元素的順序(Ordering of Class Elements)
所有class/object/trait成員都應(yīng)該用換行聲明。這條規(guī)則的唯一例外是var和val,它們可以在沒有插入換行符的情況下聲明,但是只有當(dāng)沒有一個(gè)字段具有Scaladoc,并且所有字段都具有簡單的定義(最多20個(gè)字符一行)時(shí)才可以聲明:
class Foo {
val bar = 42
val baz = "Daniel"
def doSomething(): Unit = { ... }
def add(x: Int, y: Int): Int = x + y
}
域(scope)中字段應(yīng)該放在的方法之前。唯一的例外是如果val有一個(gè)塊定義(多于一個(gè)表達(dá)式),并且執(zhí)行可能被認(rèn)為是“類似方法”的操作(例如計(jì)算List的長度)。在這種情況下,非平凡的val可以在稍后的文件中聲明,作為邏輯成員排序的指示。這條規(guī)則只適用于val和lazy val!如果var聲明散布在整個(gè)類文件中,跟蹤不斷變化的別名就變得非常困難。
5.1.2 方法
方法應(yīng)該按照下面的模式聲明:
def foo(x: Int = 6, y: Int = 7): Int = x + y
您應(yīng)該為所有公共成員指定返回類型??紤]編譯器檢查的it文檔。它還有助于在面對(duì)不斷變化的類型推斷時(shí)保持二進(jìn)制兼容性(對(duì)方法實(shí)現(xiàn)的更改可能會(huì)傳播到返回類型,如果它是推斷的)。
局部方法或私有方法可能會(huì)省略它們的返回類型:
private def foo(x: Int = 6, y: Int = 7) = x + y
5.1.2.1 過程語法(Procedure Syntax)
避免過程語法,因?yàn)樗菀资谷嘶煜?,從而在簡短性方面收效甚微?/p>
// 不要這樣做
def printBar(bar: Baz) {
println(bar)
}
// 建議這樣寫
def printBar(bar: Bar): Unit = {
println(bar)
}
5.1.2.2 修飾符(Modifiers)
方法修飾符應(yīng)該按照以下順序給出(當(dāng)每種方法都適用時(shí)):
- 注釋,每個(gè)一行上
- 覆蓋修飾符(
override) - 訪問修飾符(
protected,private) - 隱式修飾符(
implicit) - 最終修飾符(
final) - `def·
@Transaction
@throws(classOf[IOException])
override protected final def foo(): Unit = {
...
}
5.1.2.3 方法體(Body)
當(dāng)一個(gè)方法體包含一個(gè)小于30個(gè)(或左右)字符的表達(dá)式時(shí),它應(yīng)該用該方法在一行中給出:
def add(a: Int, b: Int): Int = a + b
當(dāng)方法體是一個(gè)長于30(或左右)個(gè)字符但仍然短于70(或左右)個(gè)字符的表達(dá)式時(shí),應(yīng)該在下一行給出它,縮進(jìn)兩個(gè)空格:
def sum(ls: List[String]): Int =
ls.map(_.toInt).foldLeft(0)(_ + _)
這兩種情況之間的區(qū)別有些人為。一般來說,您應(yīng)該根據(jù)具體情況選擇更易于閱讀的樣式。例如,方法聲明可能很長,而表達(dá)式主體可能很短。在這種情況下,將表達(dá)式放在下一行可能比將聲明行設(shè)置得太長更具可讀性。
當(dāng)一個(gè)方法的主體不能在一行中簡潔地表達(dá),或者具有非功能性(一些可變的狀態(tài),局部的或其他)時(shí),主體必須用大括號(hào)括起來:
def sum(ls: List[String]): Int = {
val ints = ls map (_.toInt)
ints.foldLeft(0)(_ + _)
}
包含單個(gè)match表達(dá)式的方法應(yīng)按以下方式聲明:
// 正確!
def sum(ls: List[Int]): Int = ls match {
case hd :: tail => hd + sum(tail)
case Nil => 0
}
別這樣:
// 錯(cuò)誤!
def sum(ls: List[Int]): Int = {
ls match {
case hd :: tail => hd + sum(tail)
case Nil => 0
}
}
5.1.2.4 多參數(shù)列表(Multiple Parameter Lists)
通常,如果有充分的理由,您應(yīng)該只使用多個(gè)參數(shù)列表。這些方法(或類似聲明的函數(shù))具有更詳細(xì)的聲明和調(diào)用語法,經(jīng)驗(yàn)較少的Scala開發(fā)人員更難理解。
您應(yīng)該這樣做的主要原因有三個(gè):
- 為了API的流暢性
多個(gè)參數(shù)列表允許您創(chuàng)建自己的“控制結(jié)構(gòu)”:
def unless(exp: Boolean)(code: => Unit): Unit = if (!exp) code
unless(x < 5) {
println("x was not less than five")
}
- 隱式參數(shù)
當(dāng)使用隱式參數(shù)時(shí),您使用implicit關(guān)鍵字,它適用于整個(gè)參數(shù)列表。因此,如果只希望某些參數(shù)是隱式的,則必須使用多個(gè)參數(shù)列表。
- 用于類型推斷
當(dāng)僅使用一些參數(shù)列表調(diào)用方法時(shí),類型推斷器可以在調(diào)用其余參數(shù)列表時(shí)允許使用更簡單的語法??紤]折疊:
def foldLeft[B](z: B)(op: (B, A) => B): B
List("").foldLeft(0)(_ + _.length)
// If, instead:
def foldLeft[B](z: B, op: (B, A) => B): B
// above won't work, you must specify types
List("").foldLeft(0, (b: Int, a: String) => a + b.length)
List("").foldLeft[Int](0, _ + _.length)
對(duì)于復(fù)雜的DSL,或者具有較長的類型名,很難將整個(gè)簽名放在一行中。在這些情況下,對(duì)齊參數(shù)列表的開括號(hào),每行一個(gè)列表(也就是說,如果不能將所有列表都放在一行中,那么每行一個(gè)):
protected def forResource(resourceInfo: Any)
(f: (JsonNode) => Any)
(implicit urlCreator: URLCreator, configurer: OAuthConfiguration): Any = {
...
}
5.1.2.5 高階函數(shù)(Higher-Order Functions)
在聲明高階函數(shù)時(shí),值得注意的是,當(dāng)函數(shù)參數(shù)作為最后一個(gè)參數(shù)時(shí),Scala允許在調(diào)用點(diǎn)為這類函數(shù)提供更好的語法。例如,這是SML中的foldl函數(shù)
fun foldl (f: ('b * 'a) -> 'b) (init: 'b) (ls: 'a list) = ...
在Scala中,首選的樣式正好相反:
def foldLeft[A, B](ls: List[A])(init: B)(f: (B, A) => B): B = ...
通過將函數(shù)參數(shù)放在最后,我們啟用了如下所示的調(diào)用語法:
foldLeft(List(1, 2, 3, 4))(0)(_ + _)
此調(diào)用中的函數(shù)值沒有括在括號(hào)中;它在語法上與函數(shù)本身(foldLeft)完全斷開。這種款式因簡潔干凈而受到青睞。
5.1.3字段(Fields)
字段應(yīng)該遵循方法的聲明規(guī)則,特別注意訪問修飾符的順序和注釋約定。
惰性求值val應(yīng)該在val之前直接使用Lazy關(guān)鍵字:
private lazy val foo = bar()
5.2 函數(shù)值(Function Values)
Scala為聲明函數(shù)值提供了許多不同的語法選項(xiàng)。例如,下列聲明完全等價(jià):
val f1 = ((a: Int, b: Int) => a + b)val f1 = (a: Int, b: Int) => a + bval f3 = (_: Int) + (_: Int)val f4: (Int, Int) => Int = (_ + _)
在這些樣式中,(1)和(4)總是首選的。(2)在本例中較短,但是當(dāng)函數(shù)值跨越多行時(shí)(通常是這種情況),這種語法就變得非常笨拙。同樣,(3)簡潔,但遲鈍。對(duì)于未經(jīng)訓(xùn)練的眼睛來說,很難理解這甚至產(chǎn)生了一個(gè)函數(shù)值。
當(dāng)只使用樣式(1)和樣式(4)時(shí),很容易區(qū)分源代碼中使用函數(shù)值的位置。這兩種樣式都使用括號(hào),因?yàn)樗鼈冊(cè)谝恍兄锌雌饋砗芨蓛簟?/p>
5.2.1 空格(Spacing)
括號(hào)和它們所包含的代碼之間不應(yīng)該有空格。
花括號(hào)應(yīng)該與其中的代碼間隔一個(gè)空格,以給視覺繁忙的花括號(hào)“喘息的空間”。
5.2.2 多表達(dá)函數(shù)(Multi-Expression Functions)
大多數(shù)函數(shù)值都沒有上面給出的例子那么簡單。許多包含多個(gè)表達(dá)式。在這種情況下,將函數(shù)值分割成多行通常更具可讀性。當(dāng)發(fā)生這種情況時(shí),應(yīng)該只使用風(fēng)格(1),用大括號(hào)替換圓括號(hào)。當(dāng)包含大量代碼時(shí),風(fēng)格(4)變得非常難以遵循。聲明本身應(yīng)該松散地遵循方法的聲明樣式,左大括號(hào)與賦值或調(diào)用位于同一行,而右大括號(hào)位于函數(shù)的最后一行之后的自己的行上。參數(shù)應(yīng)該與左大括號(hào)位于同一行,“箭頭”也應(yīng)該位于同一行(=>)
val f1 = { (a: Int, b: Int) =>
val sum = a + b
sum
}
如前所述,函數(shù)值應(yīng)該盡可能利用類型推斷。
6 控制結(jié)構(gòu)(Control Structures)
所有控制結(jié)構(gòu)都應(yīng)該在定義關(guān)鍵字后面加上空格:
// 正確!
if (foo) bar else baz
for (i <- 0 to 10) { ... }
while (true) { println("Hello, World!") }
// 錯(cuò)誤!
if(foo) bar else baz
for(i <- 0 to 10) { ... }
while(true) { println("Hello, World!") }
6.1 花括號(hào)(Curly-Braces)
如果控制結(jié)構(gòu)表示純功能操作,并且控制結(jié)構(gòu)的所有分支(與if/else相關(guān))都是單行表達(dá)式,則應(yīng)該省略花括號(hào)。請(qǐng)記住以下準(zhǔn)則
if- 如果有else子句,則省略大括號(hào)。否則,即使內(nèi)容只有一行,也要用花括號(hào)包圍內(nèi)容。while- 永遠(yuǎn)不要省略括號(hào)(while不能以純函數(shù)的方式使用)。for- 如果有yield子句,則省略大括號(hào)。否則,即使內(nèi)容只有一行,也要用大括號(hào)將內(nèi)容包圍起來。case- 總是在case子句中省略大括號(hào)。
val news = if (foo)
goodNews()
else
badNews()
if (foo) {
println("foo was true")
}
news match {
case "good" => println("Good news!")
case "bad" => println("Bad news!")
}
6.2 推導(dǎo)式(Comprehensions)
Scala能夠使用多個(gè)生成器(通常是多個(gè)<-符號(hào))表示for推導(dǎo)式。在這種情況下,有兩種可供選擇的語法:
// wrong!
for (x <- board.rows; y <- board.files)
yield (x, y)
// right!
for {
x <- board.rows
y <- board.files
} yield (x, y)
雖然后一種樣式更冗長,但通常認(rèn)為它更容易閱讀,而且更“可伸縮”(這意味著它不會(huì)隨著理解的復(fù)雜性增加而變得模糊)。對(duì)于多個(gè)生成器的所有for推導(dǎo)式,您應(yīng)該更喜歡這種形式。只有一個(gè)生成器的理解(例如for (i <- 0 to10) yield i)應(yīng)該使用第一種形式(圓括號(hào)而不是大括號(hào))。
這個(gè)規(guī)則的例外是缺少yield子句時(shí)的for推導(dǎo)式。在這種情況下,構(gòu)造實(shí)際上是一個(gè)循環(huán),而不是函數(shù)推導(dǎo)式,通常將生成器串在括號(hào)之間比使用語法混亂的} {構(gòu)造更具可讀性:
// 錯(cuò)誤!
for {
x <- board.rows
y <- board.files
} {
printf("(%d, %d)", x, y)
}
// 正確!
for (x <- board.rows; y <- board.files) {
printf("(%d, %d)", x, y)
}
最后,for推導(dǎo)式更優(yōu)于對(duì)map,flatMap,和filter的鏈?zhǔn)秸{(diào)用,因?yàn)檫@很難讀(這是增強(qiáng)for推導(dǎo)式的目的之一)。
6.3 平凡條件(Trivial Conditionals)
在某些情況下,創(chuàng)建一個(gè)簡短的if/else表達(dá)式用于在較大的表達(dá)式中嵌套使用是很有用的。在Java中,這類情況通常由三元運(yùn)算符(?/:)處理,這是Scala缺少的一種語法設(shè)備。在這些情況下(實(shí)際上在任何時(shí)候你有一個(gè)非常簡短的if/else表達(dá)式),允許把“then”和“else”分支放在if和else關(guān)鍵字相同的行上:
val res = if (foo) bar else baz
這里的關(guān)鍵是可讀性不會(huì)因?yàn)閷蓚€(gè)分支都內(nèi)聯(lián)到if/else中而受到阻礙。注意這種風(fēng)格不應(yīng)該與命令式if表達(dá)式一起使用,也不應(yīng)該使用花括號(hào)。
7 方法調(diào)用(Method Invocation)
一般來說,Scala中的方法調(diào)用遵循Java約定。換句話說,調(diào)用目標(biāo)和點(diǎn)(.)之間不應(yīng)該有空格,點(diǎn)和方法名之間也不應(yīng)該有空格,方法名和參數(shù)分隔符(圓括號(hào))之間也不應(yīng)該有空格。每個(gè)參數(shù)應(yīng)該用逗號(hào)(,)后面的空格隔開:
foo(42, bar)
target.foo(42, bar)
target.foo()
從2.8版開始,Scala現(xiàn)在支持命名參數(shù)。方法調(diào)用中的命名參數(shù)應(yīng)被視為規(guī)則參數(shù)(在逗號(hào)后面相應(yīng)地間隔),等號(hào)兩邊各有一個(gè)空格:
foo(x = 6, y = 7)
雖然這種樣式確實(shí)在命名參數(shù)和變量賦值方面造成了視覺上的模糊,但是另一種方法(等號(hào)周圍沒有空格)會(huì)導(dǎo)致代碼非常難以閱讀,特別是對(duì)于實(shí)值的非平凡表達(dá)式。
7.1 無參數(shù)(Arity-0)
Scala允許在無參數(shù)方法上省略括號(hào)(沒有參數(shù)):
reply()
// 等同于
reply
但是,只有當(dāng)所討論的方法沒有副作用(純函數(shù))時(shí),才應(yīng)該使用這種語法。換句話說,在調(diào)用queue.size時(shí)省略括號(hào)是可以接受的。但在調(diào)用println()時(shí)不是。這個(gè)約定反映了上面給出的方法聲明約定。
虔誠地遵守這一約定將極大地提高代碼的可讀性,并使其更容易理解任何給定方法的最基本操作。不要為了節(jié)省兩個(gè)字符而省略圓括號(hào)!
7.1.1 中綴記號(hào)(Infix Notation)
Scala有一個(gè)特殊的無標(biāo)點(diǎn)語法,用于調(diào)用帶有一個(gè)參數(shù)的方法。許多Scala程序員使用這種符號(hào)命名方法:
// 推薦
a + b
// 合法,但可讀性弱
a+b
// 合法,但很奇怪
a.+(b)
但幾乎所有字母命名的方法都要避免使用它:
// 推薦
names.mkString(",")
// 有時(shí)也會(huì)看到;有爭議的
names mkString ","
灰色區(qū)域是短的、類似操作符式的方法,如max,特別有可交換性:
// 很常見
a max b
接受多個(gè)參數(shù)的符號(hào)方法(它們確實(shí)存在!)仍然可以使用中綴表示法調(diào)用,用空格分隔:
foo ** (bar, baz)
然而,這種方法相當(dāng)少見,通常在API設(shè)計(jì)期間應(yīng)該避免使用。例如,應(yīng)該避免使用/:和:\方法,而應(yīng)該使用它們更知名的名稱foldLeft和foldRight。
7.1.2 后綴記號(hào)(Postfix Notation)
Scala允許使用后綴表示法調(diào)用不帶參數(shù)的方法:
// 推薦
names.toList
// 不鼓勵(lì)
names toList
這種樣式不安全,不應(yīng)該使用。由于分號(hào)是可選的,因此編譯器將嘗試將其視為中綴方法(如果可能的話),可能從下一行中提取一個(gè)術(shù)語。
names toList
val answer = 42 // 將不會(huì)編譯!
8 文件
通常,文件應(yīng)該包含一個(gè)邏輯編譯單元。我所說的“邏輯”指的是類、特質(zhì)或?qū)ο蟆_@個(gè)準(zhǔn)則的一個(gè)例外是,類或特質(zhì)具有伴生對(duì)象。應(yīng)該在同一個(gè)文件中使用對(duì)應(yīng)的類或特質(zhì)對(duì)伴生對(duì)象進(jìn)行分組。這些文件應(yīng)該根據(jù)它們所包含的類、特質(zhì)或?qū)ο髞砻?/p>
package com.novell.coolness
class Inbox { ... }
// 半生對(duì)象
object Inbox { ... }
這些編譯單元應(yīng)該放在com/novell/coolness目錄下,名為Inbox.scala的文件中。簡而言之,應(yīng)該首選Java文件命名和定位約定,盡管Scala在這方面允許更大的靈活性。
8.1 多單元文件(Multi-Unit Files)
不管上面說了什么,但是在一些重要的情況下,需要在一個(gè)文件中包含多個(gè)編譯單元。一個(gè)常見的例子是一個(gè)封閉的特質(zhì)和幾個(gè)子類(通常模擬功能語言中可用的ADT語言特性):
sealed trait Option[+A]
case class Some[A](a: A) extends Option[A]
case object None extends Option[Nothing]
由于密封超類(和traits)的性質(zhì),所有子類型必須包含在同一個(gè)文件中。因此,這種情況絕對(duì)可以作為應(yīng)該忽略對(duì)單個(gè)單元文件的首選項(xiàng)的實(shí)例。
另一種情況是,多個(gè)類邏輯上形成一個(gè)具有內(nèi)聚性的組,共享概念,將它們包含在一個(gè)文件中,從而極大地促進(jìn)了維護(hù)。這些情況比前面提到的密封超類型異常更難預(yù)測(cè)。一般來說,如果在一個(gè)文件中對(duì)多個(gè)單元執(zhí)行長期維護(hù)和開發(fā)比在多個(gè)單元中執(zhí)行更容易,那么這些類應(yīng)該首選這種組織策略。但是,請(qǐng)記住,當(dāng)一個(gè)文件中包含多個(gè)單元時(shí),當(dāng)需要進(jìn)行更改時(shí),通常很難找到特定的單元。
所有多單元文件都應(yīng)該使用駝峰命名,并以小寫字母開頭。
這是一個(gè)非常重要的約定。它將多文件與單個(gè)單元文件區(qū)別開來,極大地簡化了查找聲明的過程。這些文件名可能基于它們包含的重要類型(例如option.scala對(duì)于上面的例子),或者描述所有單元共享的邏輯屬性(例如,ast.scala)。
9 Scaladoc
為所有包、類、特質(zhì)、方法和其他成員提供文檔是很重要的。Scaladoc通常遵循Javadoc的約定,但是提供了許多額外的特性,可以簡化Scala代碼的文檔編寫。
一般來說,比起格式,你更關(guān)心內(nèi)容和寫作風(fēng)格。Scaladocs需要對(duì)代碼的新用戶和經(jīng)驗(yàn)豐富的用戶有用。實(shí)現(xiàn)這一點(diǎn)非常簡單:在編寫過程中增加細(xì)節(jié)和解釋的層次,從一個(gè)簡短的摘要開始(對(duì)于有經(jīng)驗(yàn)的用戶很有用,可以作為參考),同時(shí)在詳細(xì)部分中提供更深入的示例(經(jīng)驗(yàn)豐富的用戶可以忽略這些示例,但是對(duì)于新手來說是非常寶貴的)。
Scaladoc工具不要求文檔注釋樣式。
下面的示例演示了三種常見的縮進(jìn)樣式中的單行摘要和詳細(xì)文檔。
Javadoc風(fēng)格:
/**
* Provides a service as described.
*
* This is further documentation of what we're documenting.
* Here are more details about how it works and what it does.
*/
def member: Unit = ()
Scaladoc風(fēng)格,排水溝星號(hào)對(duì)齊在第二列:
/** Provides a service as described.
*
* This is further documentation of what we're documenting.
* Here are more details about how it works and what it does.
*/
def member: Unit = ()
Scaladoc風(fēng)格,排水溝星號(hào)對(duì)齊在第三列:
/** Provides a service as described.
*
* This is further documentation of what we're documenting.
* Here are more details about how it works and what it does.
*/
def member: Unit = ()
由于注釋標(biāo)記對(duì)空格敏感,工具必須能夠推斷出左邊的空白。
當(dāng)只需要簡單、簡短的描述時(shí),可以使用一行格式:
/** Does something very simple */
def simple: Unit = ()
注意,與Javadoc約定相反,Scaladoc樣式中的文本從注釋的第一行開始。這種格式節(jié)省了源文件中的垂直空間。
在任何一種Scaladoc樣式中,所有的文本行都對(duì)齊在第5列上。由于Scala源代碼通常由兩個(gè)空格縮進(jìn),所以文本以一種視覺上令人愉悅的方式與源代碼縮進(jìn)對(duì)齊。
有關(guān)格式化Scaladoc的更多技術(shù)信息,請(qǐng)參見Scaladoc獲取庫作者。
9.1 一般性規(guī)范(General Style)
與Scaladoc保持一致的樣式非常重要。同樣重要的是,要將Scaladoc瞄準(zhǔn)那些不熟悉您的代碼的用戶和只需要快速參考的經(jīng)驗(yàn)豐富的用戶。以下是一些一般的指導(dǎo)方針:
- 盡可能快地說到點(diǎn)子上。例如,說“returns true if some condition”而不是“if some condition return true”。
- 將方法的第一句話格式化為“Returns XXX”,如在“Returns the first element of the List”中,與“this method Returns”或“get the first”等相反。方法通常返回直接需要的東西。
- 這同樣適用于類;省略“This class do XXX”;就說" do XXX "
- 使用方括號(hào)語法創(chuàng)建指向引用的Scala庫類的鏈接,例如
[[Scala . option]] - 在
@return注釋中總結(jié)方法的返回值,為主Scaladoc留下更長的描述。 - 如果方法的文檔是該方法返回內(nèi)容的一行描述,則不要使用
@return注釋重復(fù)它。 - 記錄方法做了什么,而不是方法應(yīng)該做什么。換句話說,“returns the result of applying f to x”return the result of applying f to x”。細(xì)微的,但是很重要。
- 當(dāng)引用類的實(shí)例時(shí),使用“this XXX”或“this”,而不是“the XXX”。對(duì)于對(duì)象,說“this object”。
- 使代碼示例與本指南一致。
- 盡可能使用wiki樣式的語法而不是HTML。
- 根據(jù)需要,示例應(yīng)該使用完整的代碼清單或REPL(包含REPL代碼的最簡單方法是在REPL中開發(fā)示例并將其粘貼到Scaladoc中)。
- 自由使用
@macro來引用需要特殊格式化的常見重復(fù)值。
9.2 包(Packages)
為每個(gè)包提供Scaladoc。它放在包的目錄下,一個(gè)名為package.scala的文件中。它看起來像這樣(parent.package.name.mypackage):
package parent.package.name
/** This is the Scaladoc for the package. */
package object mypackage {
}
包的文檔應(yīng)該首先記錄哪些類是包的一部分。其次,記錄包對(duì)象本身提供的一般內(nèi)容。
雖然包文檔不需要是關(guān)于使用包中的類的完整教程,但它應(yīng)該提供主要類的概述,以及一些如何使用包中的類的基本示例。確保使用方括號(hào)符號(hào)引用類:
package my.package
/** Provides classes for dealing with complex numbers. Also provides
* implicits for converting to and from `Int`.
*
* ==Overview==
* The main class to use is [[my.package.complex.Complex]], as so
* {{{
* scala> val complex = Complex(4,3)
* complex: my.package.complex.Complex = 4 + 3i
* }}}
*
* If you include [[my.package.complex.ComplexConversions]], you can
* convert numbers more directly
* {{{
* scala> import my.package.complex.ComplexConversions._
* scala> val complex = 4 + 3.i
* complex: my.package.complex.Complex = 4 + 3i
* }}}
*/
package complex {}
9.3 類,對(duì)象,以及特質(zhì)(Classes,Objects,and Traits)
記錄所有類、對(duì)象和特質(zhì)。Scaladoc的第一句話應(yīng)該提供類或trait的功能概述。用@tparam記錄所有類型參數(shù)。
9.3.1 類(Classes)
如果一個(gè)類應(yīng)該使用它的同伴對(duì)象來創(chuàng)建,那么在類的描述之后就這樣指出(盡管構(gòu)造的細(xì)節(jié)留給同伴對(duì)象)。不幸的是,目前沒有辦法內(nèi)聯(lián)地創(chuàng)建指向伴生對(duì)象的鏈接,但是生成的Scaladoc將在類文檔輸出中為您創(chuàng)建一個(gè)鏈接。
如果應(yīng)該使用構(gòu)造函數(shù)創(chuàng)建類,則使用@constructor語法記錄它:
/** A person who uses our application.
*
* @constructor create a new person with a name and age.
* @param name the person's name
* @param age the person's age in years
*/
class Person(name: String, age: Int) {
}
根據(jù)類的復(fù)雜性,提供一個(gè)常見用法的示例。
9.3.2 對(duì)象(Objects)
由于對(duì)象可以用于各種目的,所以記錄如何使用對(duì)象(例如作為工廠,用于隱式方法)是很重要的。如果這個(gè)對(duì)象是其他對(duì)象的工廠,請(qǐng)?jiān)谶@里這樣表示,將apply方法的細(xì)節(jié)推遲到Scaladoc。如果你的對(duì)象沒有使用apply作為工廠方法,一定要注明實(shí)際的方法名:
/** Factory for [[mypackage.Person]] instances. */
object Person {
/** Creates a person with a given name and age.
*
* @param name their name
* @param age the age of the person to create
*/
def apply(name: String, age: Int) = {}
/** Creates a person with a given name and birthdate
*
* @param name their name
* @param birthDate the person's birthdate
* @return a new Person instance with the age determined by the
* birthdate and current date.
*/
def apply(name: String, birthDate: java.util.Date) = {}
}
如果您的對(duì)象包含隱式轉(zhuǎn)換,請(qǐng)?jiān)赟caladoc中提供一個(gè)例子:
/** Implicit conversions and helpers for [[mypackage.Complex]] instances.
*
* {{{
* import ComplexImplicits._
* val c: Complex = 4 + 3.i
* }}}
*/
object ComplexImplicits {}
9.3.3 特質(zhì)(Traits)
在概述特質(zhì)的功能之后,提供必須在混合使用特質(zhì)的類中指定的方法和類型的概述。如果有已知的類使用這個(gè)特質(zhì),那么引用它們。
9.4 方法和其他成員(Methods and Other Members)
文檔記錄所有方法。與其他可文檔化實(shí)體一樣,第一句話應(yīng)該是方法功能的總結(jié)。后面的句子解釋得更詳細(xì)。記錄每個(gè)參數(shù)以及每個(gè)類型參數(shù)(使用@tparam)。對(duì)于柯里化函數(shù),請(qǐng)考慮提供有關(guān)預(yù)期用法或慣用用法的更詳細(xì)示例。對(duì)于隱式參數(shù),要特別注意解釋這些參數(shù)來自何處,以及用戶是否需要做額外的工作來確保這些參數(shù)可用。