Scala編程規(guī)范(中文翻譯)

原文鏈接: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 + bc :: 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ī)則的唯一例外是varval,它們可以在沒有插入換行符的情況下聲明,但是只有當(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ī)則只適用于vallazy 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í)):

  1. 注釋,每個(gè)一行上
  2. 覆蓋修飾符(override
  3. 訪問修飾符(protected,private
  4. 隱式修飾符(implicit
  5. 最終修飾符(final
  6. `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è):

  1. 為了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")
}
  1. 隱式參數(shù)

當(dāng)使用隱式參數(shù)時(shí),您使用implicit關(guān)鍵字,它適用于整個(gè)參數(shù)列表。因此,如果只希望某些參數(shù)是隱式的,則必須使用多個(gè)參數(shù)列表。

  1. 用于類型推斷

當(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à):

  1. val f1 = ((a: Int, b: Int) => a + b)
  2. val f1 = (a: Int, b: Int) => a + b
  3. val f3 = (_: Int) + (_: Int)
  4. 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”分支放在ifelse關(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)該使用它們更知名的名稱foldLeftfoldRight。

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ù)可用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容