一、scala语言有什么特点?什么是函数式编程?有什么优点?
1、特点:scala语言集成面向对象和函数式编程
2、函数是编程解释:函数式编程是一种典范,将电脑的运算视作是函数的运算。
3、优点:与过程化编程相比,函数式编程里的函数计算可以随时调用。
4、函数式编程中,函数是一等公民。
二、scala中的闭包
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
闭包的实质就是代码与用到的非局部变量的混合,即:闭包 = 代码 + 用到的非局部变量
例如:
def mulBy(factor:Double) = (x:Double) => factor * x
//开始调用
val tripe = mulBy(3)
val half = mulBy(0.5)
println(tripe(14) + " " + half(14))
这就是一个闭包
三、scala中的柯里化
柯里化技术是一个接受多个参数的函数转化为接受其中几个参数的函数。经常被用来处理高阶函数。
定义:
柯里化指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有的第二个参数作为参数的函数
例如:
def mul(x:Int,y:Int) = x * y //该函数接受两个参数
def mulOneAtTime(x:Int) = (y:Int) => x * y //该函数接受一个参数生成另外一个接受单个参数的函数
这样的话,如果需要计算两个数的乘积的话只需要调用:
mulOneAtTime(5)(4)
这就是函数的柯里化
优点:
scala柯里化风格的使用可以简化主函数的复杂度,提高主函数的自闭性,提高功能上的可扩张性、灵活性。可以编写出更加抽象,功能化和高效的函数式代码。
四、case class和class的区别
样例类(case class)的模式匹配
object CaseOps {
def main(args: Array[String]): Unit = {
caseOps7
}
/**
* 样例类(case class)的模式匹配
*/
def caseOps7: Unit = {
abstract class Expr
case class Var(name:String) extends Expr
case class UnOp(operator:String, arg:Expr) extends Expr
case class BinOp(operator:String, left:Expr, right:Expr) extends Expr
def test(expr:Expr) = expr match {
case Var(name) => println(s"Var($name)...")
case UnOp(operator, e) => println(s"$e ... $operator")
case BinOp(operator, left, right) => println(s"$left $operator $right")
case _ => println("default")
}
test(BinOp("+", Var("1"), Var("2")))
test(UnOp("-",Var("3")))
}
}
结果:
Var(1) + Var(2)
Var(3) ... -
case class:
是一个样本类,样本类是一种不可变切可分解类的语法糖,也就是说在构建的时候会自动生成一些语法糖,具有以下几个特点:
1、自动添加与类名一致的构造函数(也就是半生对象,通过apply方法实现),也就是说在构造对象的时候不需要使用new关键字,如下:
scala> case class People(name:String,age:Int)
defined class People
scala> val p = People("mobin",22) //省略了new关键字
p: People = People(mobin,22)
2、实现了unapply方法,可以通过模式匹配来获取类属性,是Scala中抽取器的实现和模式匹配的关键方法。
scala> p match { case People(x,y) => println(x,y) }
(mobin,22)
3、实现了类构造函数的getter方法即样本类中的参数默认是val关键字,不可以修改,但是可以设置关键字为var,这时就就帮你实现了setter和getter方法:
关键字为默认val的情形:
scala> p.name
res0: String = mobin
scala> p.name = "mobin1" //报错,因为构造参数被声明为val所以并没有帮你实现setter方法
<console>:10: error: reassignment to val
p.name = "mobin1"
关键字设置为var的情形:
scala> case class People(var name:String) //参数被声明为var
defined class People
scala> val p = People("mobin")
p: People = People(mobin)
scala> p.name = "mobin2"
p.name: String = mobin2
scala> p.name
res1: String = mobin2 //修改成功,并没有报错
3、默认实现了toString,equals,hashcode,copy方法
4、样本类可以通过==来比较两个对象,不在构造方法内地二属性不会用在比较上
class:
class是一个类
1、class在构造对象的时候需要使用new关键字才可以。
区别:
普通类在编译后只会生成一个类名.class文件;而样例类会生成伴生类.class文件和伴生对象的.class文件。
默认是可以序列化的,也就是实现了Serializable
五、单例对象
单例对象是一种特殊的类,如何特殊?
1、它只有一个实例。
2、和lazy 变量一样,单例对象是延迟创建的,当它被第一次使用时创建。
单例对象如何被创建出来:
当对象定义于顶层时(即没有包含在其它类中),单例对象只有一个实例。
其它:
当单例对象被定义于类或方法中时,单例对象表现的和惰性变量一样。
如何去定义一个单例对象:
一个单例对象就是一个值,单例对象的定义方式很像类,只不过用关键字object:
object Box
给单例对添加一个方法:
package logging
object Logger {
def info(message: String): Unit = println(s"INFO: $message")
}
如何通过import使用单例对象里的方法:
import logging.Logger.info
class Project(name: String, daysToComplete: Int)
class Test {
val project1 = new Project("TPS Reports", 1)
val project2 = new Project("Website redesign", 5)
info("Created projects") // Prints "INFO: Created projects"
}
import语句要求被导入的标识具有一个“稳定路径”,一个单例对象由于全局唯一,所以具有稳定路径。
重点:
如果一个 object
没定义在顶层而是定义在另一个类或者单例对象中,那么这个单例对象和其他类普通成员一样是“路径相关的”。这意味着有两种行为,class Milk
和 class OrangeJuice
,一个类成员 object NutritionInfo
“依赖”于包装它的实例,要么是牛奶要么是橙汁。 milk.NutritionInfo
则完全不同于oj.NutritionInfo
。
六、伴生类和伴生对象(及孤立对象)
参考:https://www.cnblogs.com/chorm590/p/scala_201904221054.html
简述:
在scala中,单例对象与类同名时,该对象被称为该类的伴生对象,该类被称为该对象的伴生类。
详细解释:
教材中关于伴生对象的解释是:实现类似 Java 中那种既有实例成员又有静态成员的类的功能。
为什么上面说它是一种 “功能” 呢?因为要想实现像 Java 中那样的类,光靠一个 Scala 类可不行。在 Scala 中,我们必须:
1. 定义一个 class 并在这里面实现所有的实例成员。
2. 添加一个 object ,这个 object 要与上面的 class 同名,然后在这里面实现所有的静态成员。
3. 定义的 class 与 object 必须在同一个文件内。
注意:
伴生对象和伴生类可以互相访问其私有成员
举例:
class CompanionDemo {
private var clzi = 0
def init(): Unit = {
println("variable in clz:" + clzi)
println("variable from object:" + CompanionDemo.obji)
CompanionDemo.access(this)
}
}
object CompanionDemo {
private var obji = 1
def access(clz: CompanionDemo): Unit = {
println("variable in object:" + obji)
println("variable from clz:" + clz.clzi)
}
}
并通过如下代码来访问伴生对象