1、scala语⾔有什么特点?什么是函数式编程?有什么优点?
1、scala语⾔集成⾯向对象和函数式编程 2、函数式编程是⼀种典范,将电脑的运算视作是函数的运算。 3、与过程化编程相⽐,函数式编程⾥的函数计算可以随时调⽤。 4、函数式编程中,函数是⼀等功明。
2、scala中的闭包
1、定义:你可以在任何作⽤域内定义函数:包,类甚⾄是另⼀个函数或⽅法。在函数体内,可以访问到相应作⽤域内地任 何变量。(重点)函数可以在变量不再处于作⽤域内时被调⽤。 例如:
1 def mulBy(factor:Double) = (x:Double) => factor * x
2 //开始调⽤
3 val tripe = mulBy(3)
4 val half = mulBy(0.5)
5 println(tripe(14) + " " + half(14))
这就是⼀个闭包
3、scala中的柯⾥化
定义:柯⾥化指的是将原来接受两个参数的函数变成新的接受⼀个参数的函数的过程。新的函数返回⼀个以原有的第⼆ 个参数作为参数的函数 例如:
1 def mul(x:Int,y:Int) = x * y //该函数接受两个参数
2 def mulOneAtTime(x:Int) = (y:Int) => x * y //该函数接受⼀个参数⽣成另外⼀个接受单个参数的函数
3 这样的话,如果需要计算两个数的乘积的话只需要调⽤:
4 mulOneAtTime(5)(4)
这就是函数的柯⾥化
4、scala中的模式匹配
scala的模式匹配包括了⼀系列的备选项,每个替代项以关键字⼤⼩写为单位,每个替代⽅案包括⼀个模式或多个表达 式,如果匹配将会进⾏计算,箭头符号=>将模式与表达式分离 例如:
1 obj match{
2 case 1 => "one"
3 case 2 => "two"
4 case 3 => "three"
5 case _ => default
6 }
5、case class和class的区别
case class: 是⼀个样本类,样本类是⼀种不可变切可分解类的语法糖,也就是说在构建的时候会⾃动⽣成⼀些语法糖,具有 以下⼏个特点: 1、⾃动添加与类名⼀致的构造函数(也就是半⽣对象,通过apply⽅法实现),也就是说在构造对象的时候不需要 使⽤new关键字 2、样本类中的参数默认是val关键字,不可以修改 3、默认实现了toString,equals,hashcode,copy⽅法 4、样本类可以通过==来⽐较两个对象,不在构造⽅法内地⼆属性不会⽤在⽐较上 class: class是⼀个类 1、class在构造对象的时候需要使⽤new关键字才可以。
6、谈谈scala中的隐式转换
所谓的隐式转换函数(implicit conversion function)指的事那种以implicit关键字申明的带有单个参数的函数,这样 的函数会被⾃动的应⽤,将值从⼀种类型转换为另⼀种类型 ⽐如:需要把整数n转换成分数n/1 implicit def int2Fraction(n:Int) = Fraction(n,1) 这样就可以做如下表达式求职: val result = 3 * Fraction(4,5) 此时,隐⼠转换函数将整数3转换成⼀个Fraction对象,这个对象接着⼜被乘以Fraction(4,5)
7、scala中的伴⽣类和伴⽣对象是怎么⼀回事
在scala中,单例对象与类同名时,该对象被称为该类的伴⽣对象,该类被称为该对象的伴⽣类。伴⽣类和伴⽣对象要处在同⼀个源⽂件中伴⽣对象和伴⽣类可以互相访问其私有成员不与伴⽣类同名的对象称之为孤⽴对象
1 代码示例:
2 //定义⼀个类
3 class MyClass(number: Int, text: String) {
4 private val classSecret = 42
5 def x = MyClass.objectSecret + "?" // MyClass.objectSecret->在类中可以访问伴⽣对象的⽅法,在类的外部则⽆法访问
6 }
7 //定义⼀个伴⽣对象
8 object MyClass { // 和类名称相同
9 private val objectSecret = "42"
10 def y(arg: MyClass) = arg.classSecret -1 // arg.classSecret -> 在伴⽣对象中可以访问类的常量
11 }
12 MyClass.objectSecret // ⽆法访问
13 MyClass.classSecret // ⽆法访问
14 new MyClass(-1, "random").objectSecret // ⽆法访问
15 new MyClass(-1, "random").classSecret // ⽆法访问
8、scala和java 的区别
1、变量申明: scala:只需要申明是val或是var,具体的类型(⽐如String,Int,Double等等),由编译器⾃⾏推断 java: 需要在变量前⾯先注明变量的类型 2、返回值: scala:申明返回值是在后⾯,并且不需要return语句,⾮要⽤,也不是不可以 java: 如果有返回值,需要return语句 3、结束符 scala:不需要使⽤分号作为结束符 java: 每个语句结束后需要分号作为结束符 4、循环 scala:循环语句可以⽤于守卫 java: 不可以这么写 5、通配符: scala:_ java: * 6、构造器 scala:构造器名称为this,scala的辅助构造器之前需要有⼀个主构造器或者其他辅助构造器,并且scala的 构造器参数可以直接放在类的后⾯ java: 构造器名称需要与类名称⼀样 7、内部类 scala:scala实例化的内部类是不同的,可以使⽤类型投影,例如 Network#Person表示Network的 Person类 java:内部类从属于外部类 8、接⼝ scala:scala中接⼝称为特质(trait),特质中是可以写抽象⽅法,也可以写具体的⽅法体以及状态。且类是 可以实现多个特质的。 特质中未被实现的⽅法默认就是抽象的 ⼦类的实现或继承统⼀使⽤的事extends关键字,如果需要实现或继承多个使⽤with关键字 特质中可以有构造器 特质可以继承普通的类,并且这个类称为所有继承trait的⽗类 java: java中的接⼝(interface),接⼝中的⽅法只能是抽象⽅法,不可以写具体包含⽅法体的⽅法 接⼝中不能有抽象的属性,且属性的修饰符都是public static final 类实现接⼝需要使⽤implements关键字,实现多个接⼝,需要⽤逗号隔开 接⼝中不可以有构造器 接⼝不可以继承普通的类 9、赋值 scala:scala中的赋值语句返回结果是unit的不可以串联,例如x=y=1,这样是有问题的,x并没有被赋值为 1 java: x=y=1,这样是没问题的
9、谈谈scala的尾递归
正常得递归,每⼀次递归步骤,需要保存信息到堆栈中去,当递归步骤很多的时候,就会导致内存溢出尾递归,就是为了解决上述的问题,在尾递归中所有的计算都是在递归之前调⽤,编译器可以利⽤这个属性避免堆栈错 误,尾递归的调⽤可以使信息不插⼊堆栈,从⽽优化尾递归 例如:
5 + sum(4) // 暂停计算 => 需要添加信息到堆栈
5 + (4 + sum(3))
5 + (4 + (3 + sum(2)))
5 + (4 + (3 + (2 + sum(1))))
5 + (4 + (3 + (2 + 1)))
15
tailSum(4, 5) // 不需要暂停计算
tailSum(3, 9)
tailSum(2, 12)
正常递归:def sum(n:Int):Int = {
if (n == 0){
n
}else{
n + sum(n - 1)
}
}//执⾏结果sum(5)
尾递归
@tailrec //告诉编译器,强制使⽤尾递归
def tailSum(n:Int,acc:Int = 0):Int = {
if (n ==0 ){
acc
}else{
tailSum(n - 1,acc + n)
}
}//执⾏结果tailSum(5) // tailSum(5, 0) 默认
值是0
tailSum(1, 14)
tailSum(0, 15)
15
10、 var,val和def三个关键字之间的区别?
var是变量声明关键字,类似于Java中的变量,变量值可以更改,但是变量类型不能更改。val常量声明关键字。def 关键字⽤于创建⽅法(注意⽅法和函数的区别)还有⼀个lazy val(惰性val)声明,意思是当需要计算时才使⽤,避免重复计算
1 代码示例:
2 var x = 3 // x是Int类型
3 x = 4 //
4 x = "error" // 类型变化,编译器报错'error: type mismatch'
5 val y = 3
6 y = 4 //常量值不可更改,报错 'error: reassignment to val'
7 def fun(name: String) = "Hey! My name is: " + name
8 fun("Scala") // "Hey! My name is: Scala"
9 //注意scala中函数式编程⼀切都是表达式
10 lazy val x = {
11 println("computing x")
12 3
13 }
14 val y = {
15 println("computing y")
16 10
17 }
18 x+x //
19 y+y // x 没有计算, 打印结果"computing y"
11、 trait(特质)和abstract class(抽象类)的区别?
(1)⼀个类只能集成⼀个抽象类,但是可以通过with关键字继承多个特质; (2)抽象类有带参数的构造函数,特质不⾏(如 trait t(i:Int){} ,这种声明是错误的)
12、object和class的区别?
object是类的单例对象,开发⼈员⽆需⽤new关键字实例化。如果对象的名称和类名相同,这个对象就是伴⽣对象(深⼊ 了解请参考问题Q7)
1 代码示例
2 //声明⼀个类
3 class MyClass(number: Int, text: String) {
4 def classMethod() = println(text)
5 }
6 //声明⼀个对象
7 object MyObject{
8 def objectMethod()=println("object")
9 }
10 new MyClass(3,"text").classMethod() //打印结果test,需要实例化类
11 Myclass.classMethod() //⽆法直接调⽤类的⽅法
12 MyObject.objectMethod() //打印结果object,对象可以直接调⽤⽅法
13、 case class (样本类)是什么?
样本类是⼀种不可变且可分解类的语法糖,这个语法糖的意思⼤概是在构建时,⾃动实现⼀些功能。样本类具有以下特 性: (1)⾃动添加与类名⼀致的构造函数(这个就是前⾯提到的伴⽣对象,通过apply⽅法实现),即构造对象时,不需要 new; (2)样本类中的参数默认添加val关键字,即参数不能修改; (3)默认实现了toString,equals,hashcode,copy等⽅法; (4)样本类可以通过==⽐较两个对象,并且不在构造⽅法中定义的属性不会⽤在⽐较上。
1 代码示例
2 //声明⼀个样本类
3 case class MyCaseClass(number: Int, text: String, others: List[Int]){
4 println(number)
5 }
6 //不需要new关键字,创建⼀个对象
7 val dto = MyCaseClass(3, "text", List.empty) //打印结果3
8 //利⽤样本类默认实现的copy⽅法
9 dto.copy(number = 5) //打印结果5
10 val dto2 = MyCaseClass(3, "text", List.empty)
11 pringln(dto == dto2) // 返回true,两个不同的引⽤对象
12 class MyClass(number: Int, text: String, others: List[Int]) {}
13 val c1 = new MyClass(1, "txt", List.empty)
14 val c2 = new MyClass(1, "txt", List.empty)
15 println(c1 == c2 )// 返回false,两个不同的引⽤对象
14、 unapply 和apply⽅法的区别, 以及各⾃使⽤场景?
先讲⼀个概念——提取器,它实现了构造器相反的效果,构造器从给定的参数创建⼀个对象,然⽽提取器却从对象中提 取出构造该对象的参数,scala标准库预定义了⼀些提取器,如上⾯提到的样本类中,会⾃动创建⼀个伴⽣对象(包含apply和 unapply⽅法)。 为了成为⼀个提取器,unapply⽅法需要被伴⽣对象。 apply⽅法是为了⾃动实现样本类的对象,⽆需new关键字。
15、 Scala类型系统中Nil, Null, None, Nothing四个类型的区别?
1、Null是⼀个trait(特质),是所以引⽤类型AnyRef的⼀个⼦类型,null是Null唯⼀的实例。 2、Nothing也是⼀个trait(特质),是所有类型Any(包括值类型和引⽤类型)的⼦类型,它不在有⼦类型,它也没有实 例,实际上为了⼀个⽅法抛出异常,通常会设置⼀个默认返回类型。 3、Nil代表⼀个List空类型,等同List[Nothing] 4、None是Option monad的空标识(深⼊了解请参考问题Q11)
16、Unit类型是什么?
Unit代表没有任何意义的值类型,类似于java中的void类型,他是anyval的⼦类型,仅有⼀个实例对象"( )"
17、 call-by-value和call-by-name求值策略的区别?
(1)call-by-value是在调⽤函数之前计算; (2)call-by-name是在需要时计算
1 示例代码
2 //声明第⼀个函数
3 def func(): Int = {
4 println("computing stuff....")
5 42 // return something
6 }
7 //声明第⼆个函数,scala默认的求值就是call-by-value
8 def callByValue(x: Int) = {
9 println("1st x: " + x)
10 println("2nd x: " + x)
11 }
12 //声明第三个函数,⽤=>表示call-by-name求值
13 def callByName(x: => Int) = {
14 println("1st x: " + x)
15 println("2nd x: " + x)
16 }
17 //开始调⽤
18 //call-by-value求值
19 callByValue(func())
20 //输出结果
21 //computing stuff....
22 //1st x: 42
23 //2nd x: 42
24 //call-by-name求值
25 callByName(func())
26 //输出结果
27 //computing stuff....
28 //1st x: 42
29 //computing stuff....
30 //2nd x: 42
18、Option类型的定义和使⽤场景?
在Java中,null是⼀个关键字,不是⼀个对象,当开发者希望返回⼀个空对象时,却返回了⼀个关键字,为了解决这个问 题,Scala建议开发者返回值是空值时,使⽤Option类型,在Scala中null是Null的唯⼀对象,会引起异常,Option则可以避 免。Option有两个⼦类型,Some和None(空值)
1 代码示例:
2 val person: Person = getPersonByIdOnDatabaseUnsafe(id = 4) // 如果没有id=4的person时,返回null对象
3 println(s"This person age is ${person.age}") //如果是null,抛出异常
4 val personOpt: Option[Person] = getPersonByIdOnDatabaseSafe(id = 4) // 如果没有id=4的person时,返回None类型
5 personOpt match {
6 case Some(p) => println(s"This person age is ${p.age}")
7 case None => println("There is no person with that id")
8 }
19、yield如何⼯作?
yield⽤于循环迭代中⽣成新值,yield是comprehensions的⼀部分,是多个操作(foreach, map, flatMap, filter or withFilter)的composition语法糖。(深⼊了解请参考问题Q14)
1 代码示例:
2 // <-表示循环遍历
3 scala> for (i <- 1 to 5) yield i * 2
4 res0: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)
20、解释隐示参数的优先权
在Scala中implicit的功能很强⼤。当编译器寻找implicits时,如果不注意隐式参数的优先权,可能会引起意外的错误。因 此编译器会按顺序查找隐式关键字。顺序如下: (1)当前类声明的implicits ; (2)导⼊包中的 implicits; (3)外部域(声明在外部域的implicts); (4)inheritance (5)package object (6)implicit scope like companion objects
21、comprehension(推导式)的语法糖是什么操作?
comprehension(推导式)是若⼲个操作组成的替代语法。如果不⽤yield关键字,comprehension(推导式)可以被 forech操作替代,或者被map/flatMap,filter代替。
1 示例代码:
2 // 三层循环嵌套
3 for {
4 x <- c1
5 y <- c2
6 z <- c3 if z > 0
7 } yield {...}
8 //上⾯的可转换为
9 c1.flatMap(x => c2.flatMap(y => c3.withFilter(z => z > 0).map(z => {...})))
22、谈谈对Scala的Streams的理解?
1 // Stream:Stream is lazy List;
2 // Stream惰性求值指它只确定第⼀个值,后⾯的值⽤到再求值,这样可以防⽌数据过⼤全部加载导致内存溢出
3 // 将Range转化成Stream
4 val stream = (1 to 1000).toStream
5 println(stream) // Stream(1, ?)
6 println(stream.head) // 1
7 println(stream.tail) // Stream(2, ?)
23、什么是vaule class?
开发时经常遇到这个的问题,当你使⽤integer时,希望它代表⼀些东⻄,⽽不是全部东⻄,例如,⼀个integer代表年 龄,另⼀个代表⾼度。由于上述原因,我们考虑包裹原始类型⽣成⼀个新的有意义的类型(如年龄类型和⾼度类型)。 Value classes 允许开发者安全的增加⼀个新类型,避免运⾏时对象分配。有⼀些 必须进⾏分配的情况 and 限制,但是基 本的思想是:在编译时,通过使⽤原始类型替换值类实例,删除对象分配。
24、Option ,Try 和 Either 三者的区别?
1、这三种monads允许我们显示函数没有按预期执⾏的计算结果。 2、Option表示可选值,它的返回类型是Some(代表返回有效数据)或None(代表返回空值)。 3、Try类似于Java中的try/catch,如果计算成功,返回Success的实例,如果抛出异常,返回Failure。 4、Either可以提供⼀些计算失败的信息,Either有两种可能返回类型:预期/正确/成功的 和 错误的信息。
1 代码示例:
2 //返回⼀个Either类型//返回⼀个E
3 def personAge(id: Int): Either[String, Int] = {
4 val personOpt: Option[Person] = DB.getPersonById(id) //返回Option类型,如果为null返回None,否则返回Some
5 personOpt match {
6 case None => Left(s"Could not get person with id: $id") //Left 包含错误或⽆效值
7 case Some(person) => Right(person.age) //Right包含正确或有效值
8 }
25、什么是⾼阶函数?
⾼阶函数指能接受或者返回其他函数的函数,scala中的filter map flatMap函数都能接受其他函数作为参数
相关链接
发表评论