Scala oop
一、类的创建方式
- 和Java创建普通类的方式基本一样
- 不同之处在于可以在类型之后添加参数,作为类的主构造器
- 在类里定义this()方法重载构造器,作为辅助构造器(辅构造器的第一句必须为调用其他构造器)
package oop
class Point(xc: Int,yc:Int){
var x:Int=xc //成员变量
var y:Int=yc
//辅助构造器
def this()={
this(0,0) //
}
//成员方法
def move(dx:Int,dy:Int)={
x=x+dx
y=y+dy
}
def width():Int=x
}
var p1 = new Point(3,5)
println(p1.width())
println(p1.move(1, 2))
- 简便的创建类方式
class Dog(val name:String,var age:Int){
def cry()=println("wow...")
}
var dog=new Dog("dahuang",2)
二、生成getter、setter方法
- 添加@BooleanBeanProperty注解,即可调用类似java里的get set(is)方法
import scala.beans.BooleanBeanProperty
class Person(){
@BooleanBeanProperty
var name:String=""
@BooleanBeanProperty
var age:Int=0
@BooleanBeanProperty
var gender:String=""
def this(name:String,age:Int,gender:String){
this()
this.name=name
this.age=age
this.gender=gender
}
override def toString: String = s"$name,$age,$gender"
}
object Testgettersetter {
def main(args: Array[String]): Unit = {
var p2 = new Person
println(p2)
p2 = new Person("henry",22,"male")
println(p2)
p2.setName("pola")
p2.setAge(12)
p2.setGender("female")
println(p2)
}
}
其他注解
@native 标注用c实现的本地方法
@throws(classOf[Exception]) def test() {} 给方法标记要抛出的checked异常
@varargs def test(args: String*) {} 标记方法接收的是变长参数
@BeanProperty 标记生成JavaBean风格的getter和setter方法
@BooleanBeanProperty 标记生成is风格的getter方法,用于boolean类型的field
@deprecated(message = "") 让编译器提示过期警告
@unchecked 让编译器提示类型转换的警告
三、类的继承
class BlackPoint()extends Point{
private var color="black"
override def move(dx: Int, dy: Int): Unit = {
x= x+dx
y=y+dy
println(s"moved to x:$x y:$y")
}
}
var bp = new BlackPoint()
bp.x=4
bp.y=6
bp.move(1,2)
四、抽象类
abstract class Shape{
def draw():Unit
}
class Square extends Shape{
override def draw(): Unit = {
println("draw a square")
}
}
var shape = new Square
shape.draw()
五、单例对象
- 使用“object”关键字声明,可包含变量、方法与代码定义
- 单例对象中的成员变量、成员方法通过单例对象名直接调用
- 单例对象第一次被访问时初始化,并执行全部代码块
- 单例对象不能new,且无构造参数
- 程序入口main()方法必须定义在单例对象中
- 单例对象与同名类定义在同一文件中时形成绑定关系。
object Blah{
println("Blah initializing...")
def sum(l:List[Int]):Int=l.sum
}
println(Blah.sum(List[Int](1, 2, 3, 4, 5, 6)))
六、伴生对象与伴生类
- 单例对象与同名类定义在同一文件中时形成绑定关系
- 伴生类与伴生对象可相互访问各自私有成员
- 伴生对象可为伴生类增加静态成员
- 在object中一般可以为伴生类做一些初始化等操作
class Student(n:String,a:Int){
private var name = n
private var age = a
}
object Student{
def apply(n: String, a: Int): Student = new Student(n, a)
def main(args: Array[String]): Unit = {
val stu=Student("Jason",9) //通过伴生对象的apply()方法创建实例
println(stu.name)
}
}
七、特质
- 特质使用“trait”关键字定义,而特质类似Java接口,不能被实例化,没有构造参数
- 特质是字段和方法的集合,可以提供字段和方法实现
- 类和单例对象都可以扩展特质(extends)
- 实现特质中的方法使用“override”。
trait Pet{
var name:String
def cry():Unit
}
class Cat(var name:String) extends Pet{
override def cry() = println("wow = = ")
}
val cat = new Cat("henry")
import scala.collection.mutable._
val animals = ArrayBuffer.empty[Pet]
animals.append(cat)
animals.foreach(pet=>{
println(pet.name)
pet.cry()
})
八、混合特质(mixin)
- 当某个特质被用于组合类时,被称为混入
- 一个类只能有一个父类但是可以有多个混入(第一个父类或特质用exteds,多混入with)。
- 静态继承(混入),类似Java里的实现接口
- 强制动态混入(self: ),在使用的时候必须调用
- 随机动态混入,在使用的时候调用
abstract class A{
val message:String
}
class B extends A{
override val message: String = "I'm a instance of class B"
}
trait C extends A{
def loudMessage = message.toUpperCase()
}
class D extends B with C
val d = new D
println(d.message)
println(d.loudMessage)
8.1、静态动态混入案例1
trait Add{
def add(a:Int,b:Int):Int
}
trait Minus{
def minus(a:Int,b:Int):Int
}
class T extends Add with Minus{
override def add(a: Int, b: Int): Int = a+b
override def minus(a: Int, b: Int): Int = a-b
}
class X
object TX{
def main(args: Array[String]): Unit = {
println((new T).add(1, 2))//静态继承
println(new X with Add with Minus {//随机动态混入
override def add(a: Int, b: Int): Int = a * a + b * b
override def minus(a: Int, b: Int): Int = a - b
}.add(2, 3))
}
}
8.2、静态动态混入案例2
- 强制动态混入
- this:Type=> 自身类型,表示该类实例化时必须混入相应特质或子特质,self是this的别名。
class Drawing{
//this:Type=> 自身类型,表示该类实例化时必须混入相应特质或子特质,self是this的别名。
self:Shape =>
def start()=draw()
}
trait Shape{
def draw():Unit
}
trait Square extends Shape{
def draw():Unit ={
println("draw a square")
}
}
trait Triangle extends Shape{
def draw(): Unit = println("draw a triangle")
}
(new Drawing() with Triangle).start() //动态混入
九、内部类
- Scala内部类绑定到外部类的对象实例
- 每个外部类的对象实例new出来的内部类实例都不是同一类型
class Graph{
class Node{
var connectNodes:List[Node] = Nil //定义一个空集合
def connectTo(node:Node): Unit ={
if (connectNodes.find(node.equals).isEmpty) {
connectNodes = node::connectNodes //::为向链表的头部追加元素node
}
}
}
var nodes:List[Node] = Nil
def newNode:Node={
val res = new Node
nodes = res::nodes
res
}
}
val g:Graph = new Graph
val n1:g.Node = g.newNode
val n2:g.Node = g.newNode
n1.connectTo(n2)
val h:Graph = new Graph
val n3:h.Node = h.newNode
// n1.connectTo(n3)
// 报错:Type mismatch, expected: g.Node, actual: h.Node
十、样例类
- 主构造器中,使用 val 的定义的变量自动生成 getter,var 定义的变量自动生 成 getter&setter。
- 相当于伴生对象+伴生类
- 内置实现了apply,不用new
- 可以通过样例类,批量创建对象
case class Student2(var name:String,var age:Int){
def apply(name: String, age: Int): Student2 = new Student2(name, age)
}
object Student2{
def main(args: Array[String]): Unit = {
val stu = Student2("henry",18) //实例化不用new关键字
println(stu.name) //调用自动生成的getter方法
println(stu.age)
stu.name_= ("pola") //调用自动生成的setter方法
println(stu.name)
println(stu.apply("pola", 18))
}
}
case class Student3(name:String,age:Int,gender:String)
object Student3{
def main(args: Array[String]): Unit = {
var arr = Array(("a",18,"z"),("a",18,"z"),("a",18,"z"))
arr.map(x=>Student3(x._1,x._2,x._3)).foreach(x=>println(x))
}
}
十一、泛型
- class Foo[+T]协变类:对于两种类型 A 和 B,如果 A 是 B 的子类型,那么 Foo[A] 就是 Foo[B] 的子类型
- class Bar[-T]逆变类:对于两种类型 A 和 B,如果 A 是 B 的子类型,那么 Bar[B] 就是 Bar[A] 的子类型
- class Baz[T]不变类不变:默认情况下,Scala中的泛型类是不变的
class Stack[T]{
var elements:List[T] = Nil
def push(x:T): Unit ={
elements = x::elements
}
def top:T = elements.head
def pop(): Unit ={
var t = elements.head
elements = elements.tail
t
}
def showElements(): Unit ={
elements.foreach(x=>print(s"$x "))
println()
}
}
object Stack{
def main(args: Array[String]): Unit = {
val stack1 = new Stack[Int]
stack1.push(10)
stack1.push(20)
stack1.push(30)
stack1.showElements()
stack1.pop()
stack1.showElements()
val stack2 = new Stack[String]
stack2.push("a")
stack2.push("b")
stack2.push("c")
stack2.showElements()
stack2.pop()
stack2.showElements()
}
}
- 协变,下边界,类型只能更大
object GTDemo{
//逐级继承的类
class A1
class B1 extends A1
class C1 extends B1
//泛型B比B1大
class T[B>:B1]
def main(args: Array[String]): Unit = {
new T[B1]
new T[A1]
new T[C1] //因为C1更小,报错:type arguments [oop.GTDemo.C1] do not conform to class T's type parameter bounds [B >: oop.GTDemo.B1]new T[C1]
}
}
- 逆变,上边界,类型可以更小,但不能更大
object GTDemo{
//逐级继承的类
class A1
class B1 extends A1
class C1 extends B1
//泛型B比B1大
class T1[B>:B1]
class T2[B<:B1]
class T3[B]
//上边界
def main(args: Array[String]): Unit = {
new T2[B1]
new T2[A1]//因为A1更大,报错:Error:(261, 11) type arguments [oop.GTDemo.A1] do not conform to class T2's type parameter bounds [B <: oop.GTDemo.B1] new T2[A1]
new T2[C1]
}
}
- 默认时,指定使用其子类
object GTDemo{
//逐级继承的类
class A1
class B1 extends A1
class C1 extends B1
//泛型B比B1大
class T1[B>:B1]
class T2[B<:B1]
class T3[B]{
def show(v: B): Unit ={
println("abc")
}
}
def main(args: Array[String]): Unit = {
new T3[B1].show(new B1)
new T3[B1].show(new A1)//报错:Type mismatch, expected: GTDemo.B1, actual: GTDemo.A1
new T3[B1].show(new C1)
}
}
- 严进宽出,向下兼容
object GTDemo{
class A1
trait Add[A,B,C]{
def +(a:A,b:B):C
}
def main(args: Array[String]): Unit = {
val a = new A1 with Add[Short,Int,Double]{
override def +(a: Short, b: Int): Double = a+b
}
a.+(2,2.toShort)
a.+(2,2.toByte)
a.+(2,2.toFloat)//报错,默认严进宽出,向下兼容
}
}
十二、模式匹配
- 格式上和Java里的switch case类似
- 使用上和偏函数是绝配,一般用来做数值区间、正则配合、匹配数据类型、匹配格式等几种用途
案例1:正则匹配
object matchtest{
def main(args: Array[String]): Unit = {
var email = "702503235@qq.com"
val rst = email match {
case e if (e.matches("^\\w+@[0-9a-z]{2,10}\\.(com|cn|edu|org)$")) => e match {
case e if (e.matches(".*@qq\\..*")) => Some("qq")
case e if (e.matches(".*@163\\..*")) => Some("163")
case e if (e.matches(".*@hotmail\\.*")) => Some("hotmail")
case e => Some("Others")
}
case e => None
}
println(rst)
println(rst.get)
}
}
案例2:数据类型匹配
object matchtest2{
def main(args: Array[String]): Unit = {
("a",true,18,20.22,'b',20L,20.1f).productIterator.foreach(x=>println(x match {
case i:String=>s"${i} is a String"
case i:Boolean=>s"${i} is a Boolean"
case i:Int=>s"${i} is a Int"
case i:Double=>s"${i} is a Double"
case i:Char=>s"${i} is a Char"
case i:Long=>s"${i} is a Long"
case i:Float=>s"${i} is a Float"
case i=>None
}))
}
}
案例3:格式匹配
object matchtest3{
def main(args: Array[String]): Unit = {
val products = Array((1,2,3),((1,2),3),(1,(2,3)),())
products.foreach(x=>println(x match {
case (a,b,c)=>"A"
case ((a,b),c)=>"B"
case (a,(b,c))=>"C"
case e=>None
}))
}
}
案例4:样例类的模式匹配
object matchtest4{
//样例类的模式匹配
def matchTest4(x: Student2)= x match {
case Student2(name,19) => println(name)
case Student2("Tom",age) => println(age)
case Student2(name,age) => println(name,age)
case _ => println("no matches")
}
def main(args: Array[String]): Unit = {
matchTest4(Student2("Jason",19))
matchTest4(Student2("Tom",20))
matchTest4(Student2("Jimmy",20))
}
}
案例5:非样例类的模式匹配
class Student(n:String,a:Int){
private var name = n
private var age = a
}
object Student{
def apply(n: String, a: Int): Student = new Student(n, a)
def unapply(arg: Student): Option[(String, Int)] = {
if (arg==null)None else Some(arg.name,arg.age)
}
def matchtest(x:Student) = x match {
case Student(name, age) if age<20 => println("too young")
case Student(name, age) if age>20 => println("too old")
}
def main(args: Array[String]): Unit = {
val stu=Student("Jason",9) //通过伴生对象的apply()方法创建实例
println(stu.name)
matchtest(stu)
matchtest(Student("zhangsan",33))
}
}
十三、运算符重载
object oper{
case class Vec(val x: Double, val y: Double) {
def +(that: Vec) = new Vec(this.x + that.x, this.y + that.y)
def add(that: Vec) = new Vec(this.x + that.x, this.y + that.y)
}
def main(args: Array[String]): Unit = {
val vector1 = Vec(1.0, 1.0)
val vector2 = Vec(2.0, 2.0)
val vector3 = vector1 + vector2 // 或者 vector1 add vector2
println(vector3.x)
println(vector3.y)
}
}
//运算符重载
object oper2{
case class Group(eles:Int*){
def ++() = eles.sum
def +(g:Group) = (g++)+(this++)
}
def main(args: Array[String]): Unit = {
val g1 = Group(1,2,3)
val g2 = Group(4,5,6)
println(g1 ++)
println(g1+g2)
}
}
十四、隐式类
- 隐式类指的是用implicit关键字修饰的类。在对应的作用域内,带有这个关键字的类的主构造函数可用于隐式转换。
- 隐式类有以下限制条件:
只能在别的trait/类/对象内部定义
构造函数只能携带一个非隐式参数。虽然我们可以创建带有多个非隐式参数的隐式类,但这些类无法用于隐式转换。
在同一作用域内,不能有任何方法、成员或对象与隐式类同名。
object yinshiclass{
implicit class A(x:Int){
def add(y:Int)=x+y
}
def main(args: Array[String]): Unit = {
println(2 add 3)
}
}
十五、闭包
- 可以访问一个函数里面局部变量的另一个函数
- 进函数后,变量的值就不会改变
object bibao{
def main(args: Array[String]): Unit = {
var factor = 2.3
val cal = (x:Int) =>x*factor//进函数后,factor的值就不会改变
}
}
十六、正则
- 使用 String 类的 matches()方法
- 使用 Regex(scala.util.matching.Regex)的提取器进行模式匹配
- 使用 Regex API
1、matches方法
object regex{
def main(args: Array[String]): Unit = {
println("1232sdfsdf".matches("^\\d+\\w+$"))
println("1232-sdfsdf".matches("^\\d+\\w+$"))
println("1232-sdfsdf".matches("^\\d+[^a-z0-9]\\w+$"))
2、构造Regex对象,使用提取器
- 提取器有三种方式,无分组、单分组、多分组
import scala.util.matching.Regex
val decimal1=new Regex("(-)?(\\d+)(\\.\\d*)?")
//包含了很多转义符可以使用三引号
val decimal2=new Regex("""(-)?(\d+)(\.\d*)?""")
val decimal3="""(-)?(\d+)(\.\d*)?""".r
println("---Regex 的提取器-无分组---")
val p1 = "ab*c".r
//调用时把p1当做方法调用,加括号p1()
val p1m = "abbbc" match {
case p1() => true
case _=>false
}
println(p1m)
println("---Regex 的提取器-单分组---")
//前提是case p1() => true
val p2 = "a(b*)e".r
//(b*)是p2中的一个分组,所以“case p2(a1)”表示正则匹配到分组内容
// 并赋值给变量“a1”
val p2m = "abbbe" match {
case p2(a1) =>Some(a1)
case _=>None
}
println(p2m)
println("---Regex 的提取器-多分组---")
//前提是case p1() => true
val p3 = "a(b*)(c*)(d*)e".r
//(b*)是p2中的一个分组,所以“case p2(a1)”表示正则匹配到分组内容
// 并赋值给变量“a1”
val p3m = "abbbcccddde" match {
case p3(a1,a2,a3) =>Some(a1,a2,a3)
case _=>None
}
println(p3m)
3、使用RegexAPI
- findFirstMatchIn() 返回第一个匹配(Option[Match])
- findAllMatchIn() 返回所有匹配结果(Regex.Match)
- findAllIn() 返回所有匹配结果(String)
- Match 是对匹配结果的描述,主要方法有:
- groupCount:获取所有捕获的分组总数
- group():获取指定的分组,分组由 0 开始编号
- subgroups:所有捕获的分组,不包括分组 0
println("---使用RegexAPI - Regex 查找 ---")
println("---捕获分组和输出捕获的内容---")
val p4 = "a((b*))c".r
val m=p4.findFirstMatchIn("abbc").get//两个括号因此捕获了两个分组
(0 to m.groupCount).map(m.group).foreach(println)
println()
m.subgroups.foreach(println)
println("---多 Match 分组处理---")
val studentPattern:Regex="([0-9a-zA-Z-#() ]+):([0-9a-zA-Z-#() ]+)".r
val input="name:Jason,age:19,weight:100"
for(patternMatch<-studentPattern.findAllMatchIn(input)){
println(s"key: ${patternMatch.group(1)} value: ${patternMatch.group(2)}")
}
println("---Regex 字符串替换---")
println("[0-9]+".r.replaceFirstIn("234 Main Street Suite 2034", "567")) //234->567
println("[0-9]+".r.replaceAllIn("234 Main Street Suite 2034", "567")) //234、2034->567
println("[0-9]+".r.replaceAllIn("234 Main Street Suite 2034", m => {
if (m.group(0).length == 3) "567"
else m.group(0)
}))
}
}
十七、异常
- 异常有三种处理方式,分别为try catch、Either[A,B]、allCatch
object exception {
try {
//todo
} catch {
case ex: 异常类型 => {
//todo }
… …
} finally
{
//todo
}
}
//Either[A, B] 表示要么包含一个类型为 A 的实例,要么包括一个类型为 B 的 实例。
// Either 有两个子类型,Left、Right,
// 如果 Either[A, B]对象包含的是 A 的实 例,则它是 Left 实例,否则是 Right 实例
// Either 用于异常处理时,一般约定:Left 代表出错的情况,Right 代表成功 的情况
def divide(x:Int): Either[String,Int] ={
if(x==0)
Left("除数不能为 0")
else
Right(100/x)
}
def test(x: Int) = divide(x) match {
case Left(errMsg) => println(errMsg)
case Right(result) => println(result)
}
def main(args: Array[String]): Unit = {
test(0)
test(1)
}
}
object exception {
def main(args: Array[String]): Unit = {
println("---allCatch---")
//allCatch 是 scala.util.control.Exception 单例对象的方法。返回 Catch 类
println("---allCatch opt()方法---")
//将此 Catch 捕获逻辑应用于所提供的主体,将结果映射到“Option[T]”:
//如果 捕获了任何异常,则映射到“None”,否则映射到“Some(T)”。
println(scala.util.control.Exception.allCatch.opt("42".toInt))
println(scala.util.control.Exception.allCatch.opt("42a".toInt))
println("---allCatch toTry()方法---")
//如果发生异常,封装为 Failure 对象,否则执行表达式。
println(scala.util.control.Exception.allCatch.toTry("42".toInt))
println(scala.util.control.Exception.allCatch.toTry("42a".toInt))
println("---allCatch withTry()方法---")
// withTry()方法 返回 scala.util.Try 对象,其有两个子类型,Success 和 Failure,
// 分别表示运行 正常与异常,本质是调用 toTry(Success(body))。
println(scala.util.control.Exception.allCatch.withTry("42".toInt)) // Success(42)
println(scala.util.control.Exception.allCatch.withTry("42a".toInt)) // Failure (e)
println("---allCatch either()方法---")
// either()方法 返回两种类型,正确为 Right,有错误为 Left。
println(scala.util.control.Exception.allCatch.either("42".toInt)) // Right(42)
println(scala.util.control.Exception.allCatch.either("42a".toInt))
}
}