文章目录

Scala 02——Scala OOP前序类1. 类的基本结构2. 普通类和数据类的区别

继承1. extends2. override

抽象类抽象类的特点

单例对象1. 定义2. 场景3. 方法3.1 方法定义3.2 方法调用

参数特质1. 抽象类和特质的区别2. 强制混入语法3. 静态混入和动态混入

内部类Java和Scala内部类的区别当类与对象存在伴生关系时,类的写法内部类定义在实例中(①):内部类定义在伴生对象中(②):

样例类枚举定义

泛型1. 定义2. 泛型边界3. 协变、逆变、不变

包与包对象1. 命名规则:2. 包的作用3. 导包的不同方式4. 多级包的导包5. 包对象5.1 定义5.2 包对象的意义

Scala 02——Scala OOP

前序

为什么说Scala是纯粹的OOP?

不支持基本类型,一切皆为对象:Byte,Int…

Scala会将基本类型视为对象,还是在JVM上运行,自动与Java的基本数据类型完成拆装箱的操作。 不支持静态关键字:static

Java和Scala的静态概念的区别

Java

Java的静态成员:在Java中,静态成员(属性或方法)是指属于类本身而非类的实例的成员,静态成员可以在没有创建类的实例的情况下被访问。 Scala

Scala的静态类型系统:Scala被称为静态语言,与其属性和方法是否属于对象无关,是因为在Scala中,所有的类型检查都是在编译时完成的,而非运行时。Scala的单例对象:为了提供类似于Java中静态成员的功能,Scala引入了单例对象。 编译时检查和运行时检查的区别

编译时检查:在程序运行之前发现错误【Java Mysql Scala…】

类型检查语法检查结构检查访问规则检查 运行时检查:在运行时检查,通常由执行环境执行(如虚拟机或解释器)【解释型语言 或 脚本…】 静态和动态的区别

静态是编译时检查,动态是运行时检查 expression = "3 * 7 + 2"

result = eval(expression)

print(result) # 输出 23

对于静态语言来说,expression的结果是一个内容是表达式的字符串。 而对于动态语言来说(以Python的eval()函数为例),eval() 函数允许执行一个字符串形式的表达式,并返回表达式的结果。是因为在运行前没有进行类型检查,编译器视其为表达式而非文本。 支持类型推断,类型预定,动静结合

动静结合 类型预定

在运行时保留类型信息 类型推断

泛型编程中的不变、协变、逆变

泛型类型

定义

容器类

列表,如List[T]集合,如Set[T]映射,如Map 函数和方法 def func[T](x:T):t = x

不变

定义 泛型类型 G[A] 和泛型类型 G[B] 的关系与类型 A 和类型 B的关系无关。 协变

定义 如果类型 A 是类型 B 的子类型,泛型类型 G[A] 是泛型类型 G[B] 的子类型。 假设我们有一个泛型类Container[+A],其中A是一个协变类型参数。如果Dog是Animal的子类,那么Container[Dog]也应该被看作是Container[Animal]的子类型。这意味着你可以用Container[Dog]的实例去替换Container[Animal]的实例。 class Animal

class Dog extends Animal

class Container[+A]

val dogs: Container[Dog] = new Container[Dog]

val animals: Container[Animal] = dogs // 正确,因为Container是协变的

适用场景 作为输出,可以安全地从Container[Animal]中读取Animal类型的对象,不管容器实际包含的是Dog还是哪种Animal的子类型。 逆变

定义 如果类型 A 是类型 B 的子类型,泛型类型 G[B] 是泛型类型 G[A] 的子类型。 假设我们有一个泛型类Container[-A],其中A是一个逆变类型参数。如果Dog是Animal的子类,那么Container[Animal]应该被看作是Container[Dog]的子类型。这意味着你可以用Container[Animal]的实例去替换Container[Dog]的实例。 class Animal

class Dog extends Animal

class Printer[-A] {

def print(value: A): Unit = println(value)

}

val animalPrinter: Printer[Animal] = new Printer[Animal]

val dogPrinter: Printer[Dog] = animalPrinter // 正确,因为Printer是逆变的

适用场景 方法是通用的,面向各种不同类型,可以用更宽泛的类型实例替代更具体的类型实例。

1. 类的基本结构

类的本质就是模板,我们根据以下代码模板学习Scala类的基本结构:

主构造器(构造参数)属性方法辅助构造器重写方法

toString的重写接近于JSON的表达方式

// 主构造器:类自身

class Point(x:Int,y:Int){ // 没有构造方法,通过构造参数列表实现对象创建

// 属性:必须赋初值,且赋的是主构造器的参数

private var _x:Int = x // 私有属性的命名方式通常为`{_参数}`

private var _y:Int = y

// 方法

def updatePoint(x:Int,y:Int): Unit = {

_x = x

_y = y

}

// 辅助构造器

// 辅助构造器必须调用主构造器

// 辅助构造器中有两个this,第一个this是辅助构造器的名称,第二个this是调用主构造器。

def this() = this(-1,-1)

// getter 和 setter 方法

def getX=_x

def getY=_y

def setX(x:Int)={

_x=x

}

def setY(y:Int)={

_y=y

}

// 重写方法

override def toString: String = {

s"(X:${_x},Y:${_y})"

}

}

修饰符类(class)伴生对象(object)子类(subclass)同包(package)全局(world)default(public)YYYYYprotectedYYYNNprivateYYNNN

2. 普通类和数据类的区别

普通类:可以有主构造器,也可以有辅助构造器,可以有属性,可以有方法,可以有重写方法,可以有get、set方法 数据类:只能有主构造器,不能有辅助构造器(统一格式,没有更复杂的初始化逻辑),只能有属性,不能有方法(违背了数据类的定义),不能有重写方法,不能有get、set方法(自动生成)

数据类就是样例类

继承

1. extends

class ColorPoint(x:Int,y:Int,color:Boolean) extends Point(x:Int,y:Int) {

var _color:Boolean = color

private def getColor = if(_color) "red" else "black"

override def move(offsetX: Int, offsetY: Int): Unit = {

_x += offsetX*2

_y += offsetY*2

println(s"$getColor point moved to {${_x},${_y}}")

}

override def toString: String = s"$getColor point ${super.toString}"

}

val cp = new ColorPoint(0,0,true)

println(cp)

cp.move(12,25)

2. override

在重写方法时:

子类重写父类抽象方法推荐添加override关键字子类重写父类非抽象方法必须添加override关键字

抽象类

abstract class Shape {

// 抽象方法

def draw()

// 普通方法

def show:Unit = println("this is Shape")

}

抽象类的特点

可以有抽象方法(Scala中没有方法体即为没有=号及=号后面的部分)无法被实例化使用abstract关键字修饰

单例对象

1. 定义

关键字:object 单例对象用于管理共享资源或共通逻辑,封装静态工具方法,提供了便携创建其他实例的工厂方法 可以直接通过单例对象名.属性或方法来访问,类同于Java的静态属性和方法 采取惰性模式,第一次被访问时被创建 main方法必须定义在单例对象中,才能被JVM识别 同名的类和单例对象形成绑定关系,并称之为伴生类和伴生对象

伴生类和其伴生对象可以相互访问对方的私有成员(包括私有方法或私有变量)

object Util{

var PI:Float = 3.14f

var count:Int = 0

def resume:Unit = println("this is my resume.")

}

// 调用

Util.resume

class Commodity(sku:String,price:Float) {

private val _sku:String = sku

private val _price:Float = price

def getSKU:String={

_sku

}

def getPrice:Float={

discount(_price)

}

// 伴生类自由访问伴生对象中的所有变量

def getSalePrice=discount(_price)

override def toString:String={

s"SKU:${_sku},PRICE:${_price}"

}

}

object Commodity{

// 商品折扣

private var _discount:Float = 1.0f

// 包裹

def apply(sku:String,price:Float):Commodity = new Commodity(sku,price)

// 拆解:伴生对象中可以自由访问伴生类中所有资源

def unapply(arg:Commodity):(String,Float) = (arg.getSKU,arg.getPrice)

// 自定义方法

def setDiscount(discount:Float)=_discount=discount

def discount(price:Float)=price*_discount

}

2. 场景

工具类

3. 方法

3.1 方法定义

apply 方法

def apply(sku:String,price:Float):Commodity = new Commodity(sku,price)

unapply 方法

def unapply(arg:Commodity):(String,Float) = (arg.getSKU,arg.getPrice)

3.2 方法调用

object Person {

// apply方法允许不使用new关键字就能创建Person实例

def apply(name: String, age: Int): Person = new Person(name, age)

// unapply方法支持模式匹配,提取Person对象的属性

def unapply(p: Person): Option[(String, Int)] = {

if (p != null)

Some((p.name, p.age))

else

None

}

}

// 使用apply方法隐式创建Person对象

val alice = Person("Alice", 25) // 实际调用 Person.apply("Alice", 25)

// unapply在case Person(n,a)处就隐式调用,unapply方法尝试将alice对象解构为其构成元素(在这个例子中是名字和年龄)

alice match {

case Person(n, a) if a >= 18 => println(s"$n is an adult, aged $a.")

case Person(n, _) => println(s"$n is a minor.")

}

参数

命名参数:(a:Int,b:String,c:Float)

实参传递时,借助参数名传入,顺序随便(b=“henry”,a=20,c=15.46f) 可变参数:(ts:T*)

有限数量参数,数量不确定 参数默认值:(param:T=V)

实参传递时,若默认值即所需,可缺省(默认值需要为所需值)

特质

1. 抽象类和特质的区别

抽象类可以传构造参数,而特质不能传构造参数;抽象类可以有构造代码(抽象属性结合参数的构造),而特质没有构造代码。 一个类可以继承多个特质但只能继承一个抽象类。 抽象类针对的是高复用性的功能,而特质更多是针对定制化的功能。 抽象类可以提供方法的默认实现,减少了子类重复相同代码的需要。

2. 强制混入语法

定义:要求其子类必须重写这个特质的方法,并允许在基类中使用特质的方法

abstract class Animal(brand:String){

var name:String

val _type:String = brand

def roar:Unit

def me = s"${_type}:$name"

}

trait ByFoot {

def walk()

def run()

}

trait ByFly{

def fly()

}

trait BySwim{

def swim()

}

class Cat(nickname:String) extends Animal("猫") with ByFoot {

override var name: String = nickname

override def roar: Unit = println(me+"喵喵喵")

override def walk: Unit = println(me+"悠闲地漫步")

override def run: Unit = println(me+"正在快速跑")

}

class Bird(nickname:String) extends Animal("鸟") with ByFly with ByFoot {

override var name: String = nickname

override def roar: Unit = println(me+"叽叽叽")

override def fly(): Unit = println(me+"正在飞")

override def walk(): Unit = println(me+"正在闲逛")

override def run(): Unit = println(me+"正在快速跑")

}

class Fish() extends Animal("鱼"){

self:BySwim=> // 等同于 this:BySwim=>

override var name: String = _

override def roar: Unit = ???

}

3. 静态混入和动态混入

静态混入和动态混入的核心区别:

静态混入:在创建对象之前,就混入特质(该类事物的通用特征)动态混入:在创建对象之后,再混入特质(特殊事物的特殊特征)

// 静态混入

val bird = new Bird("小雀")

bird.run()

bird.fly()

bird.walk()

bird.roar

// 动态混入

val fish3 = new Fish() with BySwim with ByFoot {

override def swim(): Unit = ...

override def walk(): Unit = ...

override def run(): Unit = ...

}

内部类

Java和Scala内部类的区别

Java中内部类是【外部类的成员】

InClass ic = new OutClass.InClass()

Scala中内部类是【外部类对象的成员】

val oc = new OutClass();

val ic = new oc.InClass();

当类与对象存在伴生关系时,类的写法

class OutClass {

// ①

/*class InClass{

override def toString: String = "InClass"

}*/

private val in:InClass = new InClass

override def toString: String = s"OutClass{${in}}"

}

object OutClass{

// ②

/*class InClass{

override def toString: String = "InClass"

}*/

}

内部类定义在实例中(①):

这意味着InClass与OutClass的一个具体实例关联。

val oc = new OutClass

println(oc)

val ic: oc.InClass = new oc.InClass()

内部类定义在伴生对象中(②):

这里的OutClass.InClass是一个整体,伴生对象能够通过伴生对象名称直接获取内部的属性或方法。

val oi: OutClass.InClass = new OutClass.InClass

样例类

/*

描述【不可变值】的对象

样例类构造参数默认声明为 val,自动生成 getter

样例类的构造参数若声明为 var,自动生成 getter & setter

样例类自动生成伴生对象

样例类自动实现的其他方法:toString,copy,equals,hashCode

样例类伴生对象实现的方法:apply, unapply(用于模式匹配)

*/

// 普通类的模式匹配案例

case class Student(name:String, age:Int) // 构造参数默认 val

case class Point(var x:Int,var y:Int) // var 需要显式声明

枚举

定义

单例对象通过继承Enumeration实现枚举创建,通常用于定义一个有限取值范围的常量。

class EnumTest {

object WeekDay extends Enumeration {

val MON = Value(0)

val TUE = Value(1)

val WEN = Value(2)

val THU = Value(3)

val FRI = Value(4)

val SAT = Value(5)

val SUN = Value(6)

}

val d = WeekDay.THU

val info: String = d match {

case WeekDay.MON => "Monday"

case WeekDay.TUE => "Tuesday"

case WeekDay.WEN => "Wednesday"

case WeekDay.THU => "Thursday"

case WeekDay.FRI => "Friday"

case WeekDay.SAT => "Saturday"

case WeekDay.SUN => "Sunday"

}

}

泛型

1. 定义

类型参数化,主要用于集合。

不同于 Java 泛型被定义在 [] 中,Scala泛型更为自由

支持声明类型参数的型变

2. 泛型边界

[T<:F] 表示 T 必须是F的子类 [T>:F] 表示 T 必须是F的父类

class F

class S extends F

class Many[T<:F] (t:T){

...

}

3. 协变、逆变、不变

协变:[+T] 若A是B的子类,则 C[A]为C[B]的子类逆变:[-T] 若A是B的子类,则 C[B]为C[A]的子类不变:[T] 默认

包与包对象

1. 命名规则:

可以以字母数字下划线点开头,不能以数字开头

2. 包的作用

命名空间管理:每个包提供了一个独立的命名空间作用域控制:包允许细粒度的访问控制逻辑分隔:将功能相似的类和对象放在同一个包中,使代码结构更清晰

3. 导包的不同方式

import导包语句可以出现在任何地方import可以导入包、类、类成员

import com.kgc.Person // 方便使用类 Person

import com.kgc._ // 方便使用 com.kgc 包中的所有类

import com.kgc.Person._ // 方便使用类 Person 中的所有属性和方法

import com.kgc.{Person=>PS,Book} // 只导入包中 Person和Book,并将Person重命名为PS

4. 多级包的导包

package cha03{

import cha03.util.Sorts

object PackageTest {

def main(args: Array[String]): Unit = {

val array: Array[Int] = Array(3, 1, 5, 4, 2)

Sorts.insertSort(array)

array.foreach(println)

}

}

}

package cha03.util{

object Sorts{

def insertSort(array: Array[Int]): Unit ={

import scala.util.control.Breaks._

for(i<- 1 until array.length){

val t = array(i)

var j = i-1

breakable({

while (j>=0){

if(array(j)>t){

array(j+1) = array(j)

}else{

break()

}

j-=1

}

})

array(j+1) = t

}

}

}

}

5. 包对象

5.1 定义

包中可以包含:类、对象、特质…

包对象中可以包含:除了类、对象、特质外,还可以包含变量和方法

5.2 包对象的意义

常用的函数、常量和类型可以在包对象中定义,这允许在相同包的任何地方访问这些共享资源。

package cha03.util{

import java.util.Calendar

// 包对象

package object Constants{

// 变量

val PI:Float = 3.14f

// 方法

def getQuarter(month:Int)=(month-1)/3+1

// 类

class DataFormat(

year:Int,month:Int,day:Int,

hour:Int,minute:Int,second:Int,

millis:Int){

private var _year:Int = year

private var _month:Int = month

private var _day:Int = day

private var _hour:Int = hour

private var _minute:Int = minute

private var _second:Int = second

private var _millis:Int = millis

def this(year:Int,month:Int,day:Int){

this(year,month,day,0,0,0,0)

}

def stdYMD():String = s"${_year}-${_month}-${_day}"

def stdFull():String = s"${_year}-${_month}-${_day} ${_hour}:${_minute}:${_second}.${_millis}"

def timestamp():Long = {

val cld = Calendar.getInstance()

cld.set(_year,_month,_day,_hour,_minute,_second)

cld.set(Calendar.MILLISECOND,555)

cld.getTimeInMillis

}

}

}

object DataFormat{

def apply(year: Int, month: Int, day: Int,

hour: Int, minute: Int, second: Int, millis: Int): DataFormat

= new DataFormat(year, month, day, hour, minute, second, millis)

def apply(year: Int, month: Int, day: Int): DataFormat

= new DataFormat(year, month, day)

}

}

精彩文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: