本文翻译自: https://docs.scala-lang.org/style/
1.缩进
采用2个空格,而不是tab
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Foo { def fourspaces = { val x = 4 .. } }
class Foo { def twospaces = { val x = 2 .. } }
|
换行
换行一般是80个字符。
每个连续的行应该从第一行缩进两个空格。 还要记住,Scala要求每个“换行”要么具有未闭合的括号,要么以中缀方法结束,其中没有给出正确的参数:
1 2 3
| val result = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20
|
如果没有这种尾随方式,Scala会在一行的末尾推断出一个分号,它有时会包装,有时甚至不会发出警告而抛弃编译。
有许多参数的方法
当调用一个有大量参数(5个或更多)的方法时,通常需要将方法调用包装到多行上。 在这种情况下,将每个参数单独放在一行上,从当前缩进级别缩进两个空格:
1 2 3 4 5
| foo( someVeryLongFieldName, andAnotherVeryLongFieldName, "this is a string", 3.1415)
|
当太长时,将方法名也换到下一行:
1 2 3 4 5 6 7 8 9 10 11 12 13
| val myLongFieldNameWithNoRealPoint = foo( someVeryLongFieldName, andAnotherVeryLongFieldName, "this is a string", 3.1415)
val myLongFieldNameWithNoRealPoint = foo(someVeryLongFieldName, andAnotherVeryLongFieldName, "this is a string", 3.1415)
|
命名约定
一般来说,Scala使用驼峰命名法。
1 2
| UpperCamelCase lowerCamelCase
|
首字母缩略词应视为普通词:
1 2 3 4 5 6 7
| 使用: Xhtml maxId
而不是: XHTML maxID
|
编译器实际上不禁止名称(_)中的下划线,但强烈建议不要这样,因为它们在Scala语法中具有特殊含义。
Classes/Traits
使用驼峰:
Objects
对象名称类似于类名(上层驼峰案例)。
模仿包或函数时例外。 这并不常见。 例:
1 2 3 4 5 6 7 8 9 10
| object ast { sealed trait Expr
case class Plus(e1: Expr, e2: Expr) extends Expr ... }
object inc { def apply(x: Int): Int = x + 1 }
|
Package
Scala包应遵循Java包命名约定:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package coolness
package com.novell.coolness
package com.novell package coolness
package com.novell
package object coolness { }
|
root
有时需要使用_root_完全限定导入。例如,如果另一个net
包在范围内,那么要访问net.liftweb
我们必须这样写:
1
| import _root_.net.liftweb._
|
不要过度使用_root_。通常,嵌套包解析是一件好事,对减少导入混乱很有帮助。使用_root_不仅会否定它们的好处,而且还会引入额外的混乱。
方法
方法的名称应该使用小写开头的驼峰:
get/set方法
scala没有和java一样的get/set方法,但有下面的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Foo {
def bar = ...
def bar_=(bar: Bar) { ... }
def isBaz = ... }
val foo = new Foo foo.bar foo.bar = bar2 foo.isBaz
|
私有变量:
1 2 3 4 5 6 7 8 9
| class Company { private var _name: String = _
def name = _name
def name_=(name: String) { _name = name } }
|
括号
方法声明可以有、也可以没有括号:
1 2 3
| def foo1() = ...
def foo2 = ...
|
但是,建议调用时和声明时一样。
1 2 3 4 5 6 7 8
| def birthdate = firstName
def age() = { _age = updateAge(birthdate) _age }
|
符号方法名
尽量避免,虽然可以用。例如:a+b(), c::d, >>=
.
常量、值、变量、方法
类似java的static final
, scala里常量的声明采用大写首字母的驼峰命名:
1 2 3
| object Container { val MyConstant = ... }
|
而其他命名如下:
1 2 3
| val myValue = ... def myMethod = ... var myVariable
|
泛型
不像Java的泛型用T,scala使用从A开始的字母:
1 2 3
| class List[A] { def map[B](f: A => B): List[B] = ... }
|
或者:
1 2 3 4 5 6 7 8 9 10 11
| class Map[Key, Value] { def get(key: Key): Value def put(key: Key, value: Value): Unit }
class Map[KEY, VALUE] { def get(key: KEY): VALUE def put(key: KEY, value: VALUE): Unit }
|
1 2 3 4
| class Map[K, V] { def get(key: K): V def put(key: K, value: V): Unit }
|
高级参数类型
1 2 3
| class HigherOrderMap[Key[_], Value[_]] { ... }
def doSomething[M[_]: Monad](m: M[Int]) = ...
|
注解
应该是小写:
1 2 3 4 5
| class cloneable extends StaticAnnotation
type id = javax.persistence.Id @annotation.target.field @id var id: Int = 0
|
简短的特殊性
在java里可能不是个好习惯,但scala里推荐:
1
| def add(a: Int, b: Int) = a + b
|
类型
引用类型
// TODO
注解
冒号写在value后面而不是Type前面是有原因的,如下:
可能会有Type是::
的。
Ascription
// TODO
函数类型
函数作为参数时,能省括号就省,一元参数时不加括号:
1 2 3
| def foo(f: Int => String) = ...
def bar(f: (Boolean, Double) => List[String]) = ...
|
极端例子:
1 2 3 4 5
| def foo(f: (Int) => (String) => (Boolean) => Double) = ...
def foo(f: Int => String => Boolean => Double) = ...
|
结构类型
低于50个字符就写在一行,否则分开:
1 2 3 4 5 6 7 8 9 10
| 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) = ...
|
内联:
1
| def foo(a: { val bar: String }) = ...
|
块
大括号
左大括号必须和声明的语句在同一行:
小括号
很长的语句可以换行:
1 2
| (this + is a very ++ long * expression)
|
条件语句:
1 2 3 4
| ( someCondition || someOtherCondition || thirdCondition )
|
声明
class
class/object/trait的构造方法还是看情况,能一行就一行,否则分开:
1 2 3 4 5 6 7 8 9 10 11 12
| 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 = ... }
|
继承也是一样:
1 2 3 4 5 6 7 8 9 10 11 12
| class Person( name: String, age: Int, birthdate: Date, astrologicalSign: String, shoeSize: Int, favoriteColor: java.awt.Color) extends Entity with Logging with Identifiable with Serializable { }
|
类元素的顺序
换行:
1 2 3 4 5 6 7 8
| class Foo { val bar = 42 val baz = "Daniel"
def doSomething(): Unit = { ... }
def add(x: Int, y: Int): Int = x + y }
|
方法
如下声明方式:一定加上返回类型,否则私有方法或局部方法会忽略。
1 2 3
| def foo(bar: Baz): Bin = expr
def foo(x: Int = 6, y: Int = 7): Int = x + y
|
如下:
1 2 3 4 5 6 7 8 9
| def printBar(bar: Baz) { println(bar) }
def printBar(bar: Bar): Unit = { println(bar) }
|
修饰符
声明顺序:
- 注解
Override
- 访问修饰符(
private ,protected
)
implicit
final
def
1 2 3 4 5
| @Transaction @throws(classOf[IOException]) override protected final def foo(): Unit = { ... }
|
函数体
当函数体长度小于30个字符,写在一行:
1
| def add(a: Int, b: Int): Int = a + b
|
在30到70个字符时:
1 2
| def sum(ls: List[String]): Int = ls.map(_.toInt).foldLeft(0)(_ + _)
|
但推荐更易读的方式:
1 2 3 4
| def sum(ls: List[String]): Int = { val ints = ls map (_.toInt) ints.foldLeft(0)(_ + _) }
|
对于match语句:
1 2 3 4 5 6 7 8 9 10 11 12 13
| def sum(ls: List[Int]): Int = ls match { case hd :: tail => hd + sum(tail) case Nil => 0 }
def sum(ls: List[Int]): Int = { ls match { case hd :: tail => hd + sum(tail) case Nil => 0 } }
|