Scala 面向对象

Scala 面向对象编程

Class & object

由于Scala没有静态方法和静态类,通过object去定义静态方法或者静态对象。当object和Class放在一个文件中时候称该object为当前Class的伴生对象。

单例类

单例类使用object修饰,所有声明在object中的方法都是静态方法,类似于Java中声明的工具类的作用。

object HelloUtil {
 def sayHello(name:String):Unit={
 println("hello~ "+name)
 }
}

class User {
 var id:Int =_
 var name:String = _
 var age:Int = _
 //扩展构造
 def this(id:Int,name:String,age:Int){
 this()
 this.id=id
 this.name=name
 this.age=age
 }
}

必须要求在扩展构造方法的第一行显式调用this()-默认构造,其中_表示参数赋值为默认值

def this(参数)定义扩展构造器

class User {
 var id:Int =_
 var name:String = _
 var age:Int = _
 def this(age:Int){
 this()
 this.age=age
 }
 def this(id:Int,name:String,age:Int){
 this(age)
 this.name=name
 this.id=id
 }
}

使用该种方式声明构造类似于Java编程,但是要求在构造的第一行必须显示调用this方法。

这本质上是因为如上声明的类,等价声明了class User(){}类似使用this()调用构造器。

类上声明(默认构造器)

一旦类上声明了参数,在使用this声明其他的构造器的时候,必须在第一行调用类上的构造器。

class User(var id:Int,var name:String,var age:Int) {
 
}

伴生对象

如果类和object在一个scala源文件中,则称为object User 是class User的伴生对象,使用伴生对象可以方便的创建类的对象,只需要覆盖对应的apply方法,如下:

class User(id:Int,name:String,age:Int) {
 override def toString: String = {
 this.id+" "+this.name+" "+this.age
 }
}
object User{
 def apply(id: Int, name: String, age: Int): User = new User(id, name, age)
}
var user=User(1,"zhangsan",18)//等价 new User(1,"zhangsan",18)

这里可以理解apply是一个工厂方法,该方法的作用是生产User实例对象。

使用unapply方法能够将对象中的一些属性反解出来

object User{
 def apply(id: Int, name: String, age: Int): User = new User(id, name, age)
 def unapply(user: User): Option[(Int, String, Int)] = {
 Some(user.id,user.name,user.age)
 }
}
var user=User(1,"zhangsan",18)
var User(id,name,age)=user
println(id+" "+name+" "+age)

注意:一个伴生对象中只能有一个unapply方法,这不同于apply方法,因为apply方法可以有多个。

抽象类

abstract class Animal(name:String) {
 def eat():Unit={
 println("animal can eat...")
 }
 def sleep():String
}

Trait(接口)

trait Speakable {
 def speek():Unit
}
trait Flyable{
 def fly():Unit
}

继承&实现

//写一个Dog类继承Animal并且实现Speakable特质
class Dog(name:String) extends Animal(name:String) with Speakable {
 override def sleep(): String = {
 	"i'm a dog I sleep 8 hours"
 }
 override def speek(): Unit = {
 	println("wang wang ~~")
 }
 override def eat(): Unit = {
 	println("啃⻣头")
 }
}
object Dog{
 def apply(name: String): Dog = new Dog(name) 
}

Trait动态植入

假如说现在有一个Bird继承自Animal,在使用的时候会发现该类需要具备Flyable的功能,可以写法如下:

class Bird(name:String) extends Animal(name :String) {
 	override def sleep(): String = {
	 	"bird is sleeping"
	}
}
var b=new Bird("麻雀") with Flyable{
 	override def fly(): Unit = {
 		println("小鸟会飞!")
 	}
}
b.fly()

①在覆盖有实现的方法必须添加overwrite;

②一个类只能继承一个类with多个trait例如:Class A extends B with C with D{ }

self

等价于this关键字,在this出现混淆的时候使用self给this关键字起别名。

class User {
 self =>
 var id:Int = _
 var name:String = _
 var age:Int = _
 def this( id:Int, name:String, age:Int){
 	this()
 	self.id=id
 	self.name=name
 	self.age=age
 }
}
  • Trait强制混合

    以下案例就是要求所有Flyable的子类必须实现Bird接口。

trait Flyable{
 this:Bird => //可以将 该this看做成Bird类
 def fly():Unit={
 	println(this.name+" 可以⻜!")
 }
}
trait Bird{
 def name:String
}
class FlyFish(n:String) extends Flyable with Bird {
 	override def name= this.n 
}

Case class

case class就像常规类,case class适用于对不可变数据进行建模。

case class UserCase(id:Int,name:String) {
 def sayHi(): Unit ={
 println(this)
 }
}
var uc1=new UserCase(1,"zhangsan")
var uc2=UserCase(1,"zhangsan")

与普通的class不同的是,Case Class创建的对象 ==比较的是对象的内容,其次CaseClass的所有属性都是只读,不允许修改。通常用于只读数据的建模。可以简单的使用copy来实现两个对象间的值得传递

var uc1=new UserCase(1,"zhangsan")
var uc2=uc1.copy(id=uc1.id,name=uc1.name)
uc2.sayHi()

注:case class之间不存在继承关系。

可见性

private - 私有的

  • 修饰属性、方法
class Student02 {
 private var id:Int=_
 var name:String=_
 def this(id:Int,name:String){
 	this()
 	this.id=id
 	this.name=name
 }
 private def sayHello(): String ={
	s"hello $name"
 }
 override def toString: String = {
 	s"$id\t$name"
 }
}
object Student02{
 def main(args: Array[String]): Unit = {
 val stu = new Student02(3,"zhaoliu")
 	stu.id=4
 	println(stu.sayHello())
 	println(stu)
 }
}

该类内部和伴生对象可以访问,其他类均不可以访问

  • 修饰扩展构造
class Student03 {
 var id:Int=_
 var name:String=_
 private def this(id:Int,name:String){
 	this()
	this.id=id
 	this.name=name
 }
}

表示该扩展构造方法只能对伴生对象可见

  • 修饰默认构造
private class Student03 {
 var id:Int=_
 var name:String=_
 private def this(id:Int,name:String){
 	this()
 	this.id=id
 	this.name=name
 }
}

仅仅只能在本类的内部,以及伴生对象内部使用,其他类不可见

protected

  • 修饰属性、方法
class Student04 {
 protected var id:Int=_
 var name:String=_
 def this(id:Int,name:String){
 	this()
 	this.id=id
 	this.name=name
 }
 protected def sayHello(): String ={
 	s"hello $name"
 }
 override def toString: String = {
 	s"$id\t$name"
 }
}

针对 本类、以及伴生对象可见、子类可见

  • 修饰扩展构造
class Student05 {
 protected var id:Int=_
 var name:String=_
 protected def this(id:Int,name:String){
 	this()
 	this.id=id
	this.name=name
 }
}

对本类以及该类的伴生对象可见、子类可见

  • 修饰默认构造
protected class Student05 {
 protected var id:Int=_
 var name:String=_
 protected def this(id:Int,name:String){
 	this()
 	this.id=id
 	this.name=name
 }
}

该类在其他类中可以使用,但是构造仅仅只对本类以及伴生对象可见。

this限定

可以和private或者protected联合使用,可以讲可见性缩小为本类的内部,去除伴生对象。

class Student06 {
 private[this] var id:Int=_
 var name:String=_
 def this(id:Int,name:String){
 	this()
 	this.id=id
 	this.name=name
 }
 private[this] def sayHello(): String ={
 	s"hello $name"
 }
 override def toString: String = {
 	s"$id\t$name"
 }
}

包限定

class Student05 {
 private[demo12] var id:Int=10
}
class SmallStudent05 extends Student05 {
 def sayHello():Unit={
 println(s"${id}")
 }
}

当使用包限定的时候,就打破了private|protected的语义,全部以包为准

final限定

  • 修饰类:最终类,不可以被继承

  • 修饰方法:不能被覆盖

  • 修饰属性:只能修饰val属性,不可以修饰局部变量(scala已经提供了val表示常量),当final修饰成员变量,属性不可以被遮盖

    scala中只用val的成员变量才可以被覆盖

final关键字跟Java中final关键字不同,这里只是为了让属性不可以被遮盖使用

class Animal {
 final val name:String="动物"//必须是常量,才可以被子类覆盖,添加上final以后,允许遮盖
 val age:Int=18
 def eat():Unit={
 }
}
class Parrot extends Animal {
 override val age: Int = 20
 override def eat(): Unit = super.eat()
}
object Parrot{
 def main(args: Array[String]): Unit = {
 val parrot = new Parrot
 println(parrot.name)
 println(parrot.age)
 }
}

final和var连用没有任何意义。因为var不允许被遮盖;

sealed-密封

Traitclass可以标记为sealed,这意味着必须在同一个Scala源文件中声明所有子类型这样就确保了所有的子类型都是已知的。

sealed trait Speekable {//必须保证此接口的实现都在同一个源文件中
 def speek():Unit
}
class Xxx1 extends Speekable{
 override def speek(): Unit = {
 println("--------")
 }
}
class Xxx2 extends Speekable{
 override def speek(): Unit = {
 println("--------")
 }
}

lazy加载

Scala中使用关键字lazy来定义惰性变量,实现延迟加载(懒加载)。 惰性变量只能是不可变变量,并且只有在调用惰性变量时,才会去实例化这个变量。

def getName():String={
 	println("----getName-----")
 	"zhangsan"
}
lazy val name:String=getName()
println(name)

函数对象(lambda表达式)

函数式接口

事实上在Java1.8中也引入了函数式接口,类似Scala中的函数式对象。例如在Java中定义一个函数式接口:

@FunctionalInterface
public interface GreetingService {
 String sayHello(String name);
}

要求接口中只能有一个方法声明。只有函数式接口才可以使用lambda表达式做匿名类实现。

GreetingService gs=(String name) -> "hello "+ name;
String results = gs.sayHello("zhangsan");
System.out.println(results);

类似的这种写法在Java 8的集合stream编程中非常常见。例如:

List<String> lines= Arrays.asList("this is a demo","good good study","day dayup","come on baby");
lines.stream()
 .flatMap(line->Arrays.asList(line.split(" ")).stream())
 .forEach(word->System.out.print(word+" | "));

更多有关Java8 lambda信息请参考:https://howtodoinjava.com/java-8-tutorial

Scala这门语言在lambda编程的灵活性上是java无法媲美的。因为Scala在声明函数式对象(等价java函数式接口)是非常轻松的。

在这里插入图片描述
例如以上案例的GreetingService如果使用Scala声明可以简写为如下:

val sayHello:(String)=>String = (name) => "hello ~"+name
println(sayHello("zhangsan"))

部分应用函数

在Scala中同样对所有的函数都可以理解为是一个函数对象。例如在Scala中可以将任意一个函数转变成对象。

例如如下定义一个sum函数:

def sum(x:Int,y:Int):Int={
 x+y 
}

在Scala中可以尝试将一个函数转变成一个对象类型,如下:

scala> def sum(x:Int,y:Int):Int={
 | x+y
 | }
sum: (x: Int, y: Int)Int

scala> var sumFun = sum _
sumFun: (Int, Int) => Int = <function2>
scala> sumFun(1,2)
res0: Int = 3

通常将sumFun称为sum函数的部分应用函数,不难看出sumFun事实上是一个变量。该变量的类型是(Int, Int) => Int通常将该类型称为函数式对象。事实上以上的(Int, Int) => IntFunction2的变体形式。因为Scala最多支持 Funtion0~22 种形式变体。

例如:

class SumFunction extends ((Int,Int)=>Int){
 override def apply(v1: Int, v2: Int): Int = {
 v1+v2
 }
}
//	等价写法:
class SumFunction extends Function2[Int,Int,Int]{
 override def apply(v1: Int, v2: Int): Int = {
 v1+v2
 }
}
scala> sumFun.isInstanceOf[(Int,Int)=>Int]
res2: Boolean = true
scala> sumFun.isInstanceOf[Function2[Int,Int,Int]]
res3: Boolean = true

isInstanceOf该方法等价于java中的instanceof关键字。用于判断类型。

PartitalFunction(偏函数)

偏函数主要适用于处理指定类型的参数数据,通常用于集合处理中。定义一个函数,而让它只接受和处理其参数定义域范围内的子集,对于这个参数范围外的参数则抛出异常,这样的函数就是偏函数(顾名思异就是这个函数只处理传入来的部分参数)。偏函数是个特质其的类型为PartialFunction[A,B],其中接收一个类型为A的参数,返回⼀个类型为B的结果。

通俗讲 就是只处理我想要的数据,其他的数据不要,返回一个新的数组集合,原数组值不变

scala> val pf1=new PartialFunction[Any,Int] {
     |  override def isDefinedAt(x: Any): Boolean = {
     |  x.isInstanceOf[Int]
     |  }
     |  override def apply(v1: Any): Int = {
     |  v1.asInstanceOf[Int]
     |  }
     | }
pf1: PartialFunction[Any,Int] = <function1>

scala> val pf2:PartialFunction[Any,Int] = {case x:Int => x*2}
pf2: PartialFunction[Any,Int] = <function1>

scala> val a = Array(1,"a",2,"b",true,3)
a: Array[Any] = Array(1, a, 2, b, true, 3)

//for(i<-a;if(i.isInstanceOf[Int])) yield i
scala> a.collect(pf1)//偏函数变体写法
res0: Array[Int] = Array(1, 2, 3)

scala> a.collect(pf2)//偏函数变体写法
res1: Array[Int] = Array(2, 4, 6)

scala> a
res2: Array[Any] = Array(1, a, 2, b, true, 3)

隐式值注入/转换

隐式值

object CustomImplicits {
 	//声明隐式值
    implicit val a:Int= 1 
}
object TestImplicits {
 def main(args: Array[String]): Unit = {
     //引⼊隐式值
 	import CustomImplicits._
 	//获取上下⽂中的⼀个Int 隐式值,要求类型唯⼀
 	var b:Int= implicitly[Int]
 	print(s"b值:${b}")
 }
}

implicit声明隐式值, implicitly 获取隐式值

隐式注入

object CustomImplicits {
 //声明隐式值
 implicit val a:Int= 1
 implicit val msg:String="哈喽"
}
object TestImplicits {
    def main(args: Array[String]): Unit = {
 		//引入隐式值
 		import CustomImplicits._
 		sayHello("张三")
 		sayHello("lisi")("Hello")
 	}
 		//柯里化
 	def sayHello(name:String)(implicit msg:String):Unit={
 		println(s"${msg} ~ ${name}")
 	}
}

要求上下文中,必须有一个String类型隐式值,系统会自动注入

隐式转换(把不兼容改为兼容)

object CustomImplicits {
 //声明隐式转换
 implicit def s2d(s:String):Date={
 println("-------转换了------")
 val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
 sdf.parse(s)
 }
}
object TestImplicits {
 def main(args: Array[String]): Unit = {
 //引⼊隐式值
 import CustomImplicits._
 
 showMeTime(new Date())
 // String --> Date
 showMeTime("2018-12-13 10:30:00") //等价 showMeTime(s2d("2018-12-13 10:30:00"))
 }
 def showMeTime(date:Date):Unit={
 println("北京时间:"+date.toLocaleString)
 }
}

隐式增强(把不可能变成可能)

object CustomImplicits {
 //声明隐式增强
 implicit class PigImplicits(pig:Pig){
 	def fly():Unit={
 	println(s"${pig.name} 会⻜了 !")
 }
 
 def speek():Unit={
 	println(s"${pig.name} mu mu~")
 }
}
object TestImplicits {
     def main(args: Array[String]): Unit = {
     //引⼊隐式值
     import CustomImplicits._
     val pig1 = new Pig("猪坚强") with Flyable{
     override def fly(): Unit = {
     	println(s"${name} 会飞 ~")
     }
 	}
 	pig1.eat()
 	pig1.sleep()
 	pig1.fly() //自己飞方法
 	pig1.speek()//隐式增强
 
 	val pig = new Pig("佩奇")
 	pig.eat()
 	pig.sleep()
 	pig.fly()//隐式增强
 	pig.speek()//隐式增强
 }

异常处理

  • Java:区分已检查(必须处理)和未检查(可选)异常、捕获有顺序限制,由小 => 大
public class JavaExceptions {
    public static void main(String[] args) {
        try {
            throw new IOException("我自己抛出的~");
        } catch (IOException e) { //由精准--> 模糊
            e.printStackTrace();
        }catch (ArithmeticException e){
            e.printStackTrace();
        }catch (Throwable e){
            e.printStackTrace();
        }finally {
            System.out.println("最终执行");
        }
    }
}
  • Scala:不区分已检查和未检查异常,捕获按照写case 进行匹配
object ScalaExceptions {
     def main(args: Array[String]): Unit = {
     try {
     	throw new IOException("我自己抛出的~")
     }catch {
     	case e:ArithmeticException =>{
     		println("ArithmeticException")
     		e.printStackTrace()
     	}
     	case e: Throwable =>{
     		println("Throwable")
     		e.printStackTrace()
     	}
     	case e: IOException =>{
     		println("IOException")
     		e.printStackTrace()
     	}
     } finally {
    	System.out.println("最终执行")
     }
     }
}

泛型

<:上边界限定

//只能饲养Dog或者Dog的⼦类 上边界限定
def keepDog[T <: Dog](t:T):Unit={
	println(t) 
}
def main(args: Array[String]): Unit = {
 	val animal = new Animal("原始动物")
 val dog = new Dog("⼤⻩狗")
 val smallDog = new SmallDog("⼩狗狗")
 keepDog(dog)
 keepDog(smallDog)
 keepDog(animal)//错误
}

###>:下边界限定

//只能饲养Dog或者Dog的父类 下边界限定 bug
def keepAnimal[T >: Dog](t:T):Unit={
 println(t) 
}
def main(args: Array[String]): Unit = {
 val animal = new Animal("原始动物")
 val dog = new Dog("大黄狗")
 val smallDog = new SmallDog("小狗狗")
 keepAnimal(dog)
 keepAnimal(animal)
 keepAnimal(smallDog)//本不应该成功!这⾥我们理解为是个Bug
 }
//只允许是Dog或者Dog的父类
trait Keeper[T >: Dog] {
 def keep(t:T):Unit={
 println(t)
 }
}
def main(args: Array[String]): Unit = {
 val animal = new Animal("原始动物")
 val dog = new Dog("⼤⻩狗")
 val smallDog = new SmallDog("⼩狗狗")
 
 val k1= new Keeper[Dog] {
 override def keep(t: Dog): Unit = {
 println(t)
 }
 }
 val k2= new Keeper[Animal] {
 override def keep(t: Animal): Unit = {
 println(t)
 }
 }
 val k3= new Keeper[SmallDog] { //错误
 override def keep(t: SmallDog): Unit = {
 println(t)
 }
 }
}

这个泛型修饰在方法上的时候就会产生一个Bug,所以一般这个泛型会应用于接口中;

###<%视图限定

例如T <% U,要求上下文必须有一个隐式转换能够将T 转换为 U类型 有点像指鹿为马

class SmallDog(name:String) extends Dog (name:String){
 override def speek(): Unit = {
 	println(s"$name ⼩狗叫~")
 }
}
//可以将T看做是⼩狗
def keeperSmallDog[T <% SmallDog](t:T):Unit={
 t.speek()
}
object MyImlipcits {
 //定义⼀个String -> SmallDog转换
 implicit def s2sd(name:String):SmallDog={
 new SmallDog(name)
 }
}
def main(args: Array[String]): Unit = {
 
 keeperSmallDog(new SmallDog("⼩花花"))
 import MyImlipcits._
 
 keeperSmallDog("佩奇") 
}

T:A上下文绑定

表示上下文中环境中必须存在这种隐式值A[T]隐式值,否则程序编译出错.这样可以在上下文中还没有隐式值得时候确保方法能编译成功。

class Student[T] {
 def showMessage(msg:T):Unit={
 	msg match {
 	case name:String => println("name:"+name)
	case age:Int => println("age:"+age)
 	case _ => println("不知道")
 	}
 }
}
//上下⽂中 必须得有 Student[T] 类型隐式值 
def sayInformation[T:Student](t:T):Unit={
 val stu = implicitly[Student[T]] 
 stu.showMessage(t) 
}
def main(args: Array[String]): Unit = {
 import MyImlipcits._
 sayInformation("zhangsan")
 sayInformation(18)
 sayInformation(true) 
}
object MyImlipcits {
 implicit val stu1=new Student[String]
 implicit val stu2=new Student[Int] 
}

+A协变

//管理T或者T的⼦类
trait Manager[+T] {}
//将子类的泛型引用赋值给父类
var m1=new Manager[Animal] {}
var m2=new Manager[Dog] {}
var m3=new Manager[SmallDog] {}
m1=m2
m2=m3
m2=m1//错误

-A逆变

//管理T或者T的⽗类
trait Manager[-T] {}
//将父类的泛型引用赋值给子类
var m1=new Manager[Animal] {}
var m2=new Manager[Dog] {}
var m3=new Manager[SmallDog] {}
m1=m2//错误
m2=m3//错误
m2=m1
m3=m1

A不变

//管理T或者T的⽗类
trait Manager[T] {}
var m1=new Manager[Animal] {}
var m2=new Manager[Dog] {}
var m3=new Manager[SmallDog] {}
m1=m2//错误
m2=m3//错误
m2=m1//错误
m3=m1//错误
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小七_七七

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值