文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州
▲ 本章节目的
⚪ 了解Scala的柯里化 Currying;
⚪ 掌握Scala的类定义;
⚪ 掌握Scala的样例类、option类;
⚪ 掌握Scala的隐式转换机制;
一、柯里化 Currying
柯里化(Currying)技术 Christopher Strachey 以逻辑学家 Haskell Curry 命名的(尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的)。它是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。
案例1:
object Demo08 {
def main(args: Array[String]): Unit = {
//首先我们定义一个函数:
def f1(a:Int,b:Int):Int={a+b}
//现在我们把这个函数变一下形:
def f2(a:Int)(b:Int)={a+b}
//那么我们应用的时候,应该是这样用:f2(2)(3),最后结果都一样是5,这种方式(过程)就叫柯里化。
val r1=f2(2)(3)
//柯里化实质上会演变成这样一个函数:
//接收一个参数a,返回一个匿名函数,
//该匿名函数又接收一个参数b,函数体为a+b
def f3(a:Int)=(b:Int)=>a+b
val f4=f3(2)
//请思考 f4(3)的值是多少?答案是:5
f4(3)
}
}
示例2:
object Demo09 {
def f1(a:Int,b:Int,c:Int):Int={a+b+c}
def f2(a:Int)(b:Int)(c:Int):Int={a+b+c}
def f3(a:Int)(b:Int,c:Int):Int={a+b+c}
def f4(a:Int,b:Int)(c:Int):Int={a+b+c}
def main(args: Array[String]): Unit = {
//f1和f2的函数的体现的是传入三个数,马上得到结果
f1(1,2,3)
f2(1)(2)(3)
//f3函数则可以体现:延迟执行的思想以及固定易变因素的思想
val r1=f3(1)(2,3)
}
}
示例3:
object Demo10 {
def f1(a:Int,b:Int,f:(Int,Int)=>Int):Int={f(a,b)}
def f2(a:Int)(b:Int)(f:(Int,Int)=>Int):Int={f(a,b)}
def f3(a:Int,b:Int)(f:(Int,Int)=>Int):Int={f(a,b)}
def f4(a:Int)(b:Int,f:(Int,Int)=>Int):Int={f(a,b)}
def main(args: Array[String]): Unit = {
//调用f3
f3(2,3){(a:Int,b:Int)=>a+b}
//可简化为下面的形式,我们发现这和scala中很多函数的形式很相近,比如:for(x<-1 to 10){print(_)}
//所以,柯里化的另外一个作用是让用户灵活的自义定自建控制结构
f3(2,3){_+_}
f3(2,3){_*_}
// 延迟处理思想
def f3(a:Int)=(b:Int)=>a+b
val f4=f3(2)
f4(3)
}
}
二、类——上篇
1. 概述
1. scala中的类和java中基本类似。
2. scala中的类同样通过class来进行声明。
3. scala中的类同样可以具有成员变量和成员方法。
4. scala中的类同样可以通过new关键字来创建出对象。
2. 创建类
示例1:
//创建一个类,并定义类里的两个成员变量name和age。以及一个成员方法 eat()
//需要注意的是:scala中变量(var)声明时需要指定初始值,
class Person {
var name:String="";
var age:Int=0;
def eat(){
println("吃饭")
}
}
注:成员属性和成员方法默认都是public的 需要私有可以写private 需要保护可以写protected
示例2:
//当成员变量或成员方法是私有属性时,外部将不能直接访问,这个同java一样
class Person {
private var name:String="";
private var age:Int=0;
private def eat(){
println("吃饭")
}
}
def main(args: Array[String]): Unit = {
val p=new Person
p.name="tom";
p.age=1;
p.eat();
}
3. 类的构造
和java不同,scala中的类不需要明确声明一个构造器,而是直接将构造参数通过构造参数列表声明为类的一部分。
而直接写在类的体中的既不是类的成员变量也不是成员函数的部分,会自动收集为构造函数的体。
示例3:
//scala中的类不需要明确声明一个构造器,而是直接将构造参数通过构造参数列表声明为类的一部分
class Person(var1:String,var2:Int){
var name=var1;
var age=var2;
//而直接写在类的体中的既不是类的成员变量也不是成员函数的部分,会自动收集为构造函数的体。
println("……")
println("***")
}
def main(args: Array[String]): Unit = {
//当调用构造方法时,会打印 …… 和***
val p=new Person("tom",23)
}
4. 辅助构造器
有些时候 一个类里需要多个构造器。scala里主构造器之外的构造器被称为辅助构造器。
1. Scala的辅助构造器定义开始于 def this()
2. Scala里每个辅助构造器的第一个动作都是调用同类的构造器。
示例4:
//scala支持辅助构造器,
class Person(var1:String,var2:Int){
var name=var1;
var age=var2;
//Scala的辅助构造器定义开始于 def this()
//Scala里每个辅助构造器的第一个动作都是调用同类的构造器。
def this(var1:String){
this(var1:String,0)
}
//Scala的辅助构造器定义开始于 def this()
def this(var2:Int){
this("",var2:Int)
}
}
def main(args: Array[String]): Unit = {
val p1=new Person("tom")
val p2=new Person(23)
}
一个完整的示例:
class Person(v1:String,v2:Int){
private var name=v1
private var age=v2
def this(v1:String){
this(v1:String,0)
}
def this(v2:Int){
this("",v2)
}
def setName(name:String)={this.name=name}
def getName()={name}
def setAge(age:Int)={this.age=age}
def getAge()={age}
def eat()={println("eat")}
def say()={println("say")}
println("&&&&&&")
}
5. 单例对象 ( object )
1. scala中的类(class)不能定义静态成员(或静态方法),而代之以定义单例对象来替代。
2. 单例对象需要通过object关键字来声明
3. 一个单例对象可以单独存在,也可以绑定到一个类上
4. 单例对象当中的所有方法,都可以不需要创建对象而直接通过object单例对象的名字直接来调用,用起来感觉就像一个静态方法一样。
5. 当一个单例对象和某个类写在同一个源文件中且共享同一个名字时,他们就产生了一个绑定的关系
6. 此时单例对象称为该类的伴生对象。类称为该对象的伴生类。
7. 类和他的伴生对象可以互相访问其私有成员。
8. 以伴生的方式使为类增加静态成员称为了可能
9. 单例对象不能new,因此也没有构造参数。
10. 可以把单例对象当作是java中可能会用到的静态方法工具类。
11. 比如作为程序的入口main方法必须是静态的,所以main方法必须处在一个单例对象中,而不能写在一个类中。
12. 单例对象在第一次被访问时才会被初始化。
示例5:
//类Person
class Person{
//此私有变量,伴生类可以访问
private val namespace="1706"
}
//Person的单利对象,也即伴生类Person,
object Person{
def showTime(){
println(System.currentTimeMillis())
}
def shownamespace(){
val p=new Person
//可以访问类的私有变量
println(p.namespace)
}
}
def main(args: Array[String]): Unit = {
Person.showTime();
Person.shownamespace()
}
6. 重写和重载
1. 重写是指覆盖父类中的方法来在子类中做其他事项
override def 父类方法名 参数列表 返回值 方法体
2. 重载是指在同一个类中提供方法名相同但是参数不同的方法和java中基本一致
示例6:
class Student ( v1 : String , v2 : Int ) extends Person (v1, v2) {
override def eat () = { println ( "student eat" ) }
}
7. final的使用
可以用在成员变量、成员方法、类本身上。
作用和java中相同。
三、类——下篇
1. 抽象类
scala中同样支持抽象类的使用,抽象类的内部可以包含抽象方法和非抽象方法。
抽象类不允许被实例化,抽象类主要是用来被继承的。
示例1:
abstract class Teacher{
//抽象方法,返回值为 Unit
def teach()
//抽象方法,返回值为String
def makeNote() : String
//这不是一个抽象方法
def eat(){
}
}
class WebTeacher extends Teacher {
def teach() : Unit = {
println ( "teach web" )
}
def teach() : String = {
"teach web"
}
}
2. 支持多态
示例2:
object Demo13{
def main(args: Array[String]): Unit = {
val t1:Teacher=new TeacherChen("chen",32)
val t2:Teacher=new TeacherLiu("liu",32)
}
3. 特质 trait
1)可以类比java中的接口,但是又和接口非常不一样,特质相当于java中的接口,java中称为类实现了接口,scala中称为混入了特质。
2)和java中的接口不同的是,scala中的特质可以包含具有方法体的方法。
和抽象类不同的地方在于,scala的类只能单继承,但是可以多混入,利用这种方式可以实现类似c语言中多继承的特性。
3)在类中可以通过extends 或 with 关键字来让类混入特质,如果类没有明确继承父类,extends关键字没有被占用就可以使用extends。
4)但是如已经使用了extends显示的继承了父类,再向混入特质就要用with关键字了。
一个类的声明中只能有一个 extends,但是可以有多个with
示例3:
//trait类似于java的接口,但可以做具体方法的实现
trait Drive{
def piaoyi()
def feiche(){
}
}
trait Cook{
def tudousi()
def chaojidan()
}
//scala中,只能继承一个父类,但是可以混入多个特质(trait)
//需要实现特质中未实现的方法
//此外,需要注意的是,如果未继承任何类或抽象类,在混入特质时,比如有且仅有一个特质需要用extends来混入,而其他特质用with混入
class XueSheng extends Person with Drive with Cook{
override def piaoyi(){
}
override def tudousi(){
}
override def chaojidan(){
}
}
4. 泛型
基本和java中相同,不同的是,泛型是用方括号引起来的。
val arr = Array[String]();
5. lazy 懒值
正常情况下通过val 和 var定义的量都会直接分配空间,即使这个量要在很久以后才使用,这样就会造成内存空间白白被占用。
这种情况下可以加上lazy关键字,延后变量/常量赋值的位置。这样直到后续真正用到这个量时才真正开辟空间赋值,减少了内存的浪费。
val name = "zhang"//直接分配空间 即使一时半会用不到
lazy val name = "zhang"//并不会立即分配空间 直到后续用到了才会分配
示例1:
object Demo01 {
val val1="zhang" //> val1 : String = zhang
//lazy
lazy val val2="zhang" //> val2: => String
}
四、caseclass——样例类
1. case class - 样例类
1. 只要在声明类时 在class关键字前加上case关键字 这个类就成为了样例类
样例类必须要显式的声明一个主构造器
2. 当样例类声明一个主构造器后,会默认隐式的声明一个空构造器
3. 样例类默认实现序列化接口
4. 样例类默认自动覆盖 toString 方法
5. 样例类不需要new可以直接生成对象
示例:
//需要声明一个主构造器,主构造器可以是一个空构造器
case class Item(var1:String,var2:Int){
var name=var1;
var age=var2;
}
object Demo03 {
def main(args: Array[String]): Unit = {
//样例类不需要new,并且隐式的含有空构造器
val i1=Item
val i2=Item("tom",23)
println(i2.name+i2.age)
}
}
五、option类型
1. option
在Scala中用来表示一个结果,它可能有值,也可能没有值,它有两个子Option。
示例2:
object Demo02 {
def f1(a:Int,b:Int):Option[Int]={
if(b!=0){
Some(a/b)
}else{
None
}
}
def main(args: Array[String]): Unit = {
//表示如果有正确结果,返回正确结果,没有则返回指定的默认值
val Result=f1(4,2).getOrElse(0)
}
}
六、scala隐式转换机制
1. 概述
scala implicit关键字详解(隐式转换函数、隐式类、隐式参数、隐式值),implicit是scala中的一个关键字,关于它有着丰富的用法,使得scala更灵活和容易扩展。
2. 隐式转换函数
implicit def int2str(x:Int):String = x.toString
这段代码声明了一个函数int2str,它与正常函数唯一的区别在于前面多出的implicit关键字。这里的implicit就是它字面的含义——隐式,它告诉编译器,这个函数是一个隐式转换函数,能够把Int类型的值转换成String类型的值。
这种隐式转换的意义在于,如果在进行一个对Int类型的操作时不合法,编译器会在当前作用域寻找合适的隐式转换,来尝试使这种操作合法。
需要注意:
1.对于隐式转换函数,编译器最关心的是它的类型签名,即它将哪一种类型转换到另一种类型,也就是说它应该接受只一个参数,对于接受多参数的隐式函数来说就没有隐式转换的功能了。
implicit def int2str(x:Int):String = x.toString // 正确
implicit def int2str(x:Int,y:Int):String = x.toString // 错误
2.不能存在二义性,即同一个作用域不能定义两个相同类型的隐式转换函数,这样编译器将无法决定使用哪个转换。
implicit def int2str(x:Int):String = x.toString
implicit def anotherInt2str(x:Int):A = x.toString
3. 隐式类
scala也支持隐式类的声明,在隐式类中定义的方法都是隐式转换方法。如下:
//定义一个隐式类,用于扩展String类型的操作方法(提取后缀)
implicit class AuthMethod(x:String){
def suffix()={
x.split("\\.").last
}
}
//提取文件后缀
v4.suffix() //> res0: String = txt
4. 隐式参数
在当前作用域里存在一个Adder[Int]类型的隐式值implicit val a。
在调用f2时,编译器可以找到implicit标记过的a,所以我们不必传递隐式参数而是直接调用f2(2,3)。而如果你想要传递隐式参数的话,你也可以自定义一个传给它。
trait Adder[T]{
def add(x:T,y:T):T
}
implicit val a=new Adder[Int]{
override def add(x:Int,y:Int)=x+y
}
def f2(x:Int,y:Int)(implicit adder:Adder[Int])={
adder.add(x, y)
}
f2(2,3)(new Adder[Int]{
override def add(x:Int,y:Int)=x+y
})
f2(2,3)(a)
f2(2,3)
}
七、Scala第三天练习作业
1.对"Hello"和"World"进行拉链操作,会产生什么结果?
2.编写函数values(fun:(Int)=>Int,low:Int,high:Int),该函数输出一个集合,对应给定区间内给定函数的输入和输出。比如:
-5,-4,-3,-2,-1,0,1,2,3,4,5
values(x=>x*x,-5,5)应该产出一个对偶的集合(-5,25),(-4,16),(-3,9),…,(3,9),(5,25)
3.编写函数largest(fun:(Int)=>Int,inputs:Seq[Int]),输出在给定输入序列中给定函数的最大值。举例来说,
largest(x=>10*x-x*x,1 to 10)应该返回25
1=>x=>10*x-x*x=9
2=>x=>10*x-x*x=16
……
返回其中的最大值 25
4.修改前一个函数,返回最大的输出对应的输入。举例来说,largestAt(fun:(Int)=>Int,inputs:Seq[Int])应该返回5。
5.编写一个函数,接收一个整数数组(Array[Int]),产出一个新的数组。
包含原数组中的所有正值,以原有顺序排列,之后的元素是所有零或负值,以原有顺序排列。文章来源:https://www.toymoban.com/news/detail-652347.html
比如: Array(4, -2, 0, -3, 0, 1, 5) ,处理后的结果是:Array(4 ,1, 5, 0, 0, -2, -3)文章来源地址https://www.toymoban.com/news/detail-652347.html
到了这里,关于大数据课程J3——Scala的类定义的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!