scala与java有许多相似地方,scala是面向对象与面向过程编程的统一体,java在jdk8之前都是面向对象编程。因此scala具有面向对象编程的特点,即封装、继承和多态,又有面向过程的特点,使得scala语言更加灵活多变,掌握起来有一定的难度。scala编译器的作者也是java编译器的作者,因此scala能够运行在所有的jvm上,scala具有与java一样的跨平台特征。
本文整理scala语法,供学习使用。
一、基础
1.表达式。 表达式是可以运算的句子。
你可以使用println或print打印表达式的结果。例如println(1);println(1+1);println(“hello”)等等
你可以为表达式的结果起一个名字,使用val关键字修饰。这个在某种情况下类似于java中的常量,其值不可更改。例如val x = 1+1;val x : String = "hello".
你可以将表达式结果赋给一个变量。变量与常量不同之处在于变量的值可以变化。例如var x = 1+1; var s:String = "Hello"等
2.块。 你可以将多句表达式使用{}包起来,形成一语句块,scala中称为block。例如println({
val x = 1+1
x+1
})
3.函数。 函数也是表达式,是带有参数的表达式。例如匿名函数(x:Int,y:Int) => x+y,=>左边是参数列表,注意参数必须制定类型,右边是表达式或block。要调用这个匿名函数,需要为其起一个名字,这个名字可以用val或var修饰。
例如val sum = (x:Int,y:Int) => x+y;var sum = (x:Int,y:Int) => x+y。调用时就是sum(1,2)。
4.方法。方法与函数相似,但是是使用关键字def来定义的。
例如def sum(x:Int,y:Int) : Int = x+y。例子中def后面跟的是方法名、参数列表(没有可以省略)、返回值类型(默认是Unit,可以省略)、方法体。如果没有设置返回值类型‘=’就不需要了,没有参数列表“()”可以不要。
方法可以有多个参数列表,也可以不带参数列表。方法体可以是一句表达式,也可以是一个block。方法体的最后一行表达式的结果就是该方法的返回值。scala有return关键字,但是很少使用。
5.class。 scala的类使用class关键字定义。scala类的构造函数是跟类的定义一起的,类名后可以跟一个构造函数参数列表,也可以不带参数列表,默认是无参的构造函数。
例如class Sum(x:Int, y:Int) {
println("This is main constructor.")
def this(x:Int, y:Int, z:Int){
this(x,y)
println("this is assistant constructor.")
}
}
scala的构造函数分为主构造函数和辅助构造函数。主构造函数与类的定义统一在一起。辅助构造函数函数名必须是this关键字,且第一行语句必须使用this关键在调用主构造函数或其他辅助构造函数。
类的实例化使用new关键字。不带参数的构造函数,在实例化时()可以省略。
注意scala中方法的参数列表都是val的,因此在方法中使用时是不可以更改其值的。但是主构造函数的参数列表可以定义成var,使用var修饰参数就行,方法及辅助构造函数的参数列表是不可以使用var修饰的。
6.case class。case class是一种特殊的类,这种类使用case class两个关键字修饰的,且类的主构造函数的参数列表为空时,()也不能省略。 case class的主构造函数的参数列表默认是val的,即不可变值,虽然你可以指定其为var,但是这样做是不推荐的。case class设计的目的是创建一个不变类,它在编译时会自动添加一些通用的方法,例如toString,copy,hashcode,equals等等。case class在编译时,编译器会创建一个伴生类,它的作用是在创建case class的实例时可以不使用new关键字,但这仅限于使用主构造函数创建实例。除了伴生类与自动创建一些通用方法外,case class与普通class没什么不同。一般case class用于模式匹配,值比较。通常case class只有类的定义,没有类体, 例如case class A(x:String, y:String),创建实例val a= A("x", "y")。
7.object。object是一种特殊类,类似单例, 使用object关键字修饰。但是与java单例不同的是,它不允许有辅助构造函数,它的主构造函数不允许带参数。因此objce类其实是一些静态方法的集合。
例如object A {
def doSomething{
println("This is an object.")
}
}
调用方式A.doSomething即可,你也可以将A取个名字a,但a只能用val修饰,如果使用var修饰的话使用a调用A的方法时会报错。
8.trait。trait是一种特殊类,它有点类似java的抽象类,java的抽象类是单继承特性,trait也是单继承特征。但是java有interface可以扩展子类的功能,scala在不破坏单继承特性基础上,添加了组合trait的特征,关键字是with。trait的抽象方法可以有默认的实现。trait不允许有构造函数,因此它的类名是不允许带参数列表的,所以trait是不能够直接实例化的。
例如trait A{
def doSomething{
var x:Int
val y=0
var s="default"
println("this is default implement of A")
}
def printSomething
}
trait B{
def printSomething
}
子类在继承trait类时使用extends关键字,如果想实现多个trait的属性或方法,可以使用with关键字将多个trait变成一个组合trait。
子类class C extends A with B{
override var x:Int = 0
override def doSomething{
println("this is implement of C")
}
def printSomething{
println(s)
}
}
从上面可以看到,子类可以继承trait的方法及属性,如果trait中属性没有被初始化,则子类中使用时需要对其进行初始化。如果trait中方法有默认实现,则子类中想重写的话需要使用override关键字声明该方法是重写父类方法。如果多个trait中有同样结构的方法,则子类中需要override这个方法。如果trait中定义的变量有初始化,则子类中不能重新定义该变量。
9.main方法。main方法是scala应用的入口,这是因为scala使用的是jvm,jvm要求应用的入口方法必须是名字为main的方法,且没有返回值,参数是一个String数组。scala中main方法必须写在objcet类中。
二、类型层次结构
与java类似,scala中所有的值或函数都有类型。所有的类型都有一个基类Any,同时所有的类型都有一个子类Nothing,后面一点是与java不同的地方。scala的类型层次结构如下图
从上图中可以看出Ay有两个直接的子类AnyVal及AnyRef。AnyVal有点类似java的基本数据类型,AnRef类似java的Object类,我们所写的scala类均是AnyRef的子类。
AnyVal代表了值类型。scala提供了9中预定义的AnyVal类型。值类型是不能为null的。这9中值类型是Double,Float,Long,Int,Short,Byte,Char,Boolean,Unit。Unit表示无意义的信息,是函数和方法的默认返回类型,类似java的void返回类型,你可以为一个变量初始化一个值(),()就是声明一个Unit类型。
Unit类型虽然类似java的void返回类型,但是与void又不同。java的void是关键字不是类型,void方法其实是没有值返回的;但scala的Unit则不同,它实际表示是无意义的返回,它本质还是个类型,所以Unit返回类型的方法或函数其返回值是可以接收到的,只不过返回的值是(),表示没有意义的值。
AnyRef表示引用类型,一切非值类型都是引用类型。
与java不同的是scala的类型层次中有个Nothing类型,这个类型不是任何值的类型,它是一切类型的子类,但你不能创建和使用这个类型,它唯一的作用是给出一个没有结束的信号,例如发生异常、程序退出、或者是出现了无限循环问题时,它被用来表示无法获取一个值或者方法没有正常返回。
Null类型是所有引用类型的子类,即所有AnyRef的子类。Null里只有一个null值,如果你声明一个变量的类型是Null,那么它的值只能是null。Null的主要作用是用来与其他jvm语言进行协同工作的,在scala代码里基本不使用。
Nothing与Null类型均无法获取实例。
三、类型转换
值类型可以按照下面方式进行转换
从图中可以看出它遵循的是小转大,即所占字节小的可以向字节大的值类型转换。值类型转换是单向的,Boolean型不能参与类型转换。这些与java的基本数据类型向上转型一致。
四、访问控制符
scala有public、protected、private三种修饰符。不使用修饰符默认是public,这点与java不同,java不使用修饰符默认是package。scala的修饰符均可以修饰类名,这点与java也不同,java里修饰类名只能是public或者不写。
scala访问控制符作用的对象是包内成员,类或对象内成员(包括成员属性或成员方法)。
包内的成员即是类或对象,即class或object。public修饰class或object时,表示该class或object对一切对象或类是可见的。
其中,protected与provide用于修饰类名或对象名时,效果一样,都是表示该包内及包的子目录里所有类或对象能够访问该类或对象。但包外的类或对象就无法看到该类或对象。
类或对象的成员包括属性和方法。public修饰类或对象的成员表示所有能够调用该类或对象的一切类或对象可以调用。
使用provide修饰类的成员时,scala又比java更为严格。即类或对象的内部类的成员如果是私有的,则外部类是无法调用内部类的私有成员的。
使用protected修饰类的成员时,scala比java更为严格。即类或对象的成员如果是受保护的,那么他们只能够被该类或对象及其子类访问。
五、访问控制符添加作用域
这个特点是java里没有的,访问控制符本身规定了被修饰的成员的作用范围,scala又对访问控制符做了新的用法。
provide[x]及protected[x]
修饰符中括号里的参数x是包名、类或对象名。它表示该修饰符的作用范围到x包为止,就是说被这样修饰的成员,它能够被x包或类或对象的所有成员访问,出了x包则就是provide或protected的。
六、行结束符
scala的行结束符比较灵活,它可以以‘;’作为结束符,也可以省略分号。如果一句代码需要占用多行,则需要在每行开始前使用‘|’符。一行类如果有多条语句,必须要使用分号了,但是不建议这样做,这样做很不友好。scala风格一般是不使用分号做行结束符。
七、标识符
scala的标识符比java有很大不同。主要有四类标识符:字符数字标识符、符号标识符、混合标识符、字面量标识符。
1.字符数字标识符
此类标识符与java类似,命名规则也遵循java的驼峰规则。不同之处在与,$符号是scala内置关键字,最好在标识符中不要使用,以避免冲突。且最好不要以_结尾,以避免冲突。
2.符号标识符
这类标识符由有ASCII码的特殊符号组成,例如+,-,*,/,?,#等等。这类标识符在编译时,scala会将其转换成以$开头的对应的英文。例如标识符->就被编译成$minus$greater。这类标识符可以用于做方法名,当java代码要调用这类标识符命名的方法时,只能调用他们编译后的名字。
3.混合标识符
混合标识符是字符数字标识符后面跟多个符号标识符组成。其中字符数字标识符以_结尾。
4.字面量标识符
文字标识符是以``包括起来的一串随意的字符串。
八、关键字
abstract case catch class
def do else extends
false final finally for
forSome if implicit import
lazy match new null
object override package private
protected return sealed super
this throw trait try
true type val var
while with yield
scala的关键字比java要少许多,但是scala运行在jvm上,因此对于缺少的关键字,scala都提供了替代的方法。例如instanceof关键字在scala中被isInstanceOf方法替代,它是scala的内置方法。
十、运算符
scala包括算术运算、逻辑运算、位运算、赋值运算、关系运算等几种运算符。
1.算术运算符
有+、-、*、/、%几种算术运算符。scala里没有自增++和自减--运算符。
2.逻辑运算符
有&&、||、!几种种逻辑运算符。
3.关系运算符
有==、!=、>、<、>=、<=几种关系运算符。
4.位运算符
有&、|、~、^、>>、<<、>>>等几种。
5.赋值运算符
有=、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=等几种。
scala里的运算符与java的概念不同,在scala里,运算符其实就是方法名,只不过是当你不适用点号来调用方法时,这些方法就都可以被称为运算符。这个特点后面章节再细说。