Class类
属性定义
- val 只读属性 只有getter
- var 读写属性 有getter又有setter
- private val 只能在类内部和伴生对象中访问
- private[this] val 只能在类内部访问
构造器
- 主构造器
- class Student(val name:String,val age:Int)
- 类只有一个主构造器
- 主构造器执行时, 会执行类定义中的所有语句
- 辅助构造器
- def this(name:String,age:Int,gender:String) { }
- 类可以有多个辅助构造器
- 辅助构造器的第一行必须是主构造器或者其他辅助构造器的调用
object对象
object简介
- object 相当于 class 的单个实例,通常在里面放一些静态的 field 或者 method
- object 作用:1.存放工具方法和常量2.高效共享单个不可变的实例3.单例模式
- 使用时, 不需要new, 类似java的静态类 直接 单例对象名称.方法 即可调用
伴生对象
在一个.scala文件中, 有一个Class A 还有一个同名的object A 则称这个 object是 class 的伴生对象,class 是 object 的伴生类
伴生类和伴生对象的最大特点是,可以相互访问
apply() 方法
apply 方法通常是在伴生对象中实现的, 其目的是,通过伴生类的构造函数功能,来实现伴生对象的构造函数功能
通常我们会在类的伴生对象中定义 apply 方法,当遇到类名(参数 1,…参数 n)时 apply 方法会被调用
在apply方法内部,会new一个对应的类, 然后返回
main() 方法
- scala 必须有一个main函数作为入口
- main必须定义在object中
- 也可以自定义object继承App Trait, 然后直接在类中编写原本在main方法中的代码
extends继承
子类继承父类,与 Java 一样,也是使用 extends 关键字 不支持多继承类
Java 中的访问控制权限,同样适用于 Scala
子类可继承父类的 field 和 method, 子类也可以有自己特有的 field 和 method
final
- 父类用final修饰,则父类不能被继承
- 父类的field 或 method被final修饰, 则被修饰的field和method不能被覆盖
protected
protected 子类可以直接访问父类的field和method
protected[this] 只允许在当前子类中访问父类的field和method
子类创建的对象不能直接调用父类的field和method
val var
- val 修饰的field 才允许被继承,在覆盖时,需要添加override关键字
- var 修饰的只允许被引用 可以直接引用 因为var的值可变 覆盖没有意义 或者说没有覆盖的说法
override
- 子类覆盖父类的非抽象方法时, 需要加override关键字
- 覆盖方法后, 如果想要调用父类的同名方法, 使用super关键字
isInstanceOf asInstanceOf 模糊判断
- isInstanceOf 判断对象是否为指定类或子类的对象 stu1.isInstanceOf[Student]
- asInstanceOf 将对象转换为指定类型 stu1.asInstanceOf[Student]
- 如果对象是 null,则 isInstanceOf 一定返回 false, asInstanceOf 一定返回null
getClass classOf 精准判断
- getClass 可以精确地获取对象的类
- classOf[XX] 可以精确的获取类
- stu1.getClass == classOf[Student] true
子类调用父类的constructor
只能在子类的主 constructor 中调用父类的 constructor
class Student(name:String,score:Double) extends Person(name,score)
new Student 对象时 会调用父类的构造函数
子类的辅助 constructor 是一定不可能直接调用父类的 constructor 的
如果父类的构造函数已经定义过的 field,比如 name 和 age,子类再使用时就不用 val 或 var 来修饰了,否则会被认为,子类要覆盖父类的 field,且要求一定要使用 override 关键字
class Student(override val name:String,score:Double) extends Person(name,score)
抽象类
抽象类
一个类中,如果含有一个抽象方法或抽象 field,就必须使用 abstract 将类声明为抽象类
父类中的方法编写成只含有方法签名,不含方法体的形式,这种形式就叫做抽象方法
def sayHello:String
父类中,定义了 field,但是没有给出初始值,则此 field 为抽象 field
val age:Int
在子类中覆盖抽象类的抽象方法时,可以不加 override 关键字
trait特质
- trait具有接口特性
- 在trait中可以定义抽象方法, 抽象field
- 类通过extends继承trait, 继承后, 必须实现其抽象方法
- scala不支持多继承类, 但是支持多重继承trait, 使用with进行连接
- trait具有类特性
- 在trait中也可以定义具体方法, 具体field
- 如果是继承 class 获取的 field ,实际上还是定义在父类中的
- 而继承 trait 获取的 field,就直接被添加到子类中了
- trait混入
- 在创建类的对象时, 为该对象混入某个trait, 表示只有这个对象才具有该trait中的方法, 而该类其他对象没有
- val rose = new Student(“Rose”) with trait1
- trait调用链 —> 责任链模式
- Scala 中支持让类继承多个 trait 后,可依次调用多个 trait 中的同一个方法,只要让多个 trait 中的同一个方法,在最后都依次执行 super 关键字即可
- 类中调用多个 trait 中都有的这个方法时,首先会从最右边的 trait 的方法开始执行,然后依次往左执行,形成一个调用链条
- trait混合使用具体方法 抽象方法 —> 模板设计模式
- 在 trait 中,可以混合使用具体方法和抽象方法
- 可以让具体方法依赖于抽象方法 而抽象方法则可放到继承 trait 的子类中去实现
- trait构造机制
- 在 Scala 中,trait 也是有构造代码的,即在 trait 中,不包含在任何方法中的代码
- 继承了 trait 的子类,其构造机制如下
- 父类的构造函数先执行, class 类必须放在最左边
- 多个 trait 从左向右依次执行
- 构造 trait 时,先构造父 trait,如果多个 trait 继承同一个父 trait,则父 trait 只会构造一次
- 所有 trait 构造完毕之后,子类的构造函数最后执行
- traitt继承class
- 在 Scala 中 trait 也可以继承 class,此时这个 class 就会成为所有继承该 trait 的子类的超级父类
模式匹配&样例类
Scala 有一个十分强大的模式匹配机制,可以应用到很多场合:类似 switch 语句 类型检查
Scala 还提供了样例类,对模式匹配进行了优化,可以快速进行匹配。
模式匹配
匹配字符串
name match { case "hadoop" => println("大数据分布式存储和计算框架...") case "zookeeper" => println("大数据分布式协调服务框架...") case "spark" => println("大数据分布式内存计算框架...") //表示以上情况都不满足才会走最后一个 case _ => println("我不认识你") }
匹配类型
value match { case x:Int => println("Int=>"+x) //模式匹配的时候还可以添加守卫条件。如不符合守卫条件,将掉入 case _中。 case y:Double if(y>=0) => println("Double=>"+y) case z:String => println("String=>"+z) case _ => throw new Exception("not match exception") }
匹配数组 元祖 集合
//匹配数组 val arr=Array(1,3,5) arr match{ case Array(1,_*) =>println("1...") case Array(1,x,y) =>println(x+"---"+y) case Array(0) =>println("only 0") case _ =>println("something else") } //匹配集合 val list=List(0,3,6) list match { case 0::Nil => println("only 0") ///println("x:"+x+" y:"+y+" z:"+z) case x::y::z::Nil => println(s"x:$x y:$y z:$z") case 0::tail => println("0....") case _ => println("something else") } //匹配元组 val tuple=(1,3,5) tuple match{ case (1,x,y) => println(s"1,$x,$y") case (2,x,y) => println(s"$x,$y") case _ => println("others...") }
样例类
- case class 类型,是多例的,后面要跟构造参数。 case class Student(name:String)
- case object 类型,是单例的。 case object Person
case class SubmitTask(id:String,name:String) case class HeartBeat(time:Long) case object CheckTimeOutTask case object StopTask value match{ case SubmitTask(id,name) =>println(s"$id----$name") case HeartBeat(time) =>println(s"$time") case CheckTimeOutTask =>println("check....") case StopTask =>println("stop.....") }
Option类型
在 Scala 中 Option 类型用样例类来表示可能存在或者可能不存在的值(Option 的子类有Some 和 None)
- Some 包装了某个值
- None 表示没有值
val map=Map("a"->1,"b"->2) //调用map中的get方法,传入一个key,返回一个Option类型 val v: Option[Int] = map.get("c") v match{ case Some(i) =>println("存在..."+i) case None =>println("不存在...") }
偏函数
被包在花括号内没有 match 的一组 case 语句是一个偏函数,它是 PartialFunction[A, B]的一个实例,A 代表输入参数类型,B 代表返回结果类型,常用作输入模式匹配,偏函数最大的特点就是它只接受和处理其参数定义域的一个子集。
def main(args: Array[String]) { println(func("two")) println(func2("two")) } val func:PartialFunction[String,Int]={ case "one" => 1 case "two" => 2 case _ => -1 } //定义一个普通的方法,通过match 和case 实现偏函数逻辑 def func2(x:String):Int={ x match { case "one" => 1 case "two" => 2 case _ => -1 } }
Scala 协变、逆变、非变
Java中,如果有 A 是 B 的子类,但 Card[A]却不是 Card[B] 的子类;
而 Scala 中,只要灵活使用协变与逆变,就可以解决此类 Java 泛型问题
- C[+T]:如果 A 是 B 的子类,那么 C[A]是 C[B]的子类。
- C[-T]:如果 A 是 B 的子类,那么 C[B]是 C[A]的子类。
- C[T]: 无论 A 和 B 是什么关系,C[A]和 C[B]没有从属关系。
Scala 上下界
在指定泛型类型时,有时需要界定泛型类型的范围,而不是接收任意类型。
Scala 的上下边界特性允许泛型类型是某个类的子类,或者是某个类的父类
类似java 的 <? super T> <? extends T>
- U >: T <? super T>
这是类型下界的定义,也就是 U 必须是类型 T 的父类(或本身,自己也可以认为是自己的父类)。
- S <: T ? <? extends T>
这是类型上界的定义,也就是 S 必须是类型 T 的子类(或本身,自己也可以认为是自己的子类)。