代理登录网站,wordpress 导入md,大淘客网站如何做seo,网站建设算软件还是硬件面向对象编程
Scala是一门完全面向对象的语言#xff0c;摒弃了Java中很多不是面向对象的语法。
虽然如此#xff0c;但其面向对象思想和 Java的面向对象思想还是一致的
Scala包
1#xff09;基本语法
Scala中基本的package包语法和 Java 完全一致
例如#xf…面向对象编程
Scala是一门完全面向对象的语言摒弃了Java中很多不是面向对象的语法。
虽然如此但其面向对象思想和 Java的面向对象思想还是一致的
Scala包
1基本语法
Scala中基本的package包语法和 Java 完全一致
例如
package com.clear.bigdata.scala2Scala包的三大作用和Java一样
区分相同名字的类当类很多时可以很好的管理类控制访问范围
3包的命名规则
只能包含数字、字母、下划线、小圆点但是不能以数字开头也不要使用关键字
命令规范一般是小写字母 小圆点
扩展语法
Java中package包的语法比较单一Scala对此进行扩展
Scala有两种包的管理风格一种和Java的包管理风格相同每个源文件一个包包名和源文件所在路径不要求必须一致包名用”.“进行分隔以表示包的层级关系如
com.clear.scala另一种风格通过嵌套的风格表示层级关系
Scala中的包和类的物理路径没有关系package关键字可以嵌套声明使用一个源文件中可以声明多个package
package com
package clear {package bigdata {package scala {object ScalaPackage {def test(): Unit {println(test...)}}}}
}同一个源码文件中子包可以直接访问父包中的内容而无需import但是父包访问子包需要import
// 用嵌套风格定义包
package com {import com.clear.scala.Inner// 在外层包中定义单例对象object Outer {val out: String outdef main(args: Array[String]): Unit { println(Inner.in) // 父包访问子包需要import}}package clear {package scala{// 内存包中定义单例对象object Inner{val in: String indef main(args: Array[String]): Unit {Outer.out outer // 子包中直接访问父包中的内容println(Outer.out)}}} }
}Scala中package也可以看作对象即包对象并声明属性和函数
package com
package object clear { // 在 com.clear 包下创建的 clear包对象val name : String zhangsandef test(): Unit {println( name )}
}
package com {package clear {package scala { // 在 com.clear.scala 在访问包对象object ScalaPackage {def test(): Unit {}}}}
}包对象
在Scala中可以为每个包定义一个同名的包对象定义在包对象中的成员作为其对应包下所有的 class 和 object 的共享变量可以直接被访问
说明
若使用java的包管理风格则包对象一般定义在其对应的package.scala文件中包对象名与保持一致。
package com.clear // 默认包对象的名称必须和 包名一致全局只有一份
// 使用 package修饰
package object oop { // 我们在 com.clear.oop包下创建了一个包对象// 定义当前包共享的属性和方法val commonValue 中国def commonMethod(): Unit {println(s你好 ${commonValue})}
}package com.clear.oopobject Test { // 在 com.clear.oop 包下创建了一个Test单例对象去访问包对象定义的属性和方法def main(args: Array[String]): Unit {commonMethod() }
}导包说明
通配符导入Scala中基本的import导入语法和 Java完全一致
import java.util.List
import java.util._ // Scala中使用下划线代替Java中的星号Java中 import 导入的语法比较单一Scala对此进行扩展
局部导入Scala中的 import 语法可以在任意位置使用这一点在python中也可以
object ScalaImport{def main(args: Array[String]): Unit {import java.util.ArrayListnew ArrayList() }
}Scala中可以导包而不是导类
object ScalaImport{def main(args: Array[String]): Unit {import java.utilnew util.ArrayList()}
}导入相同包的多个类Scala中可以在同一行中导入相同包中的多个类简化代码这一点在python中也可以
import java.util.{List, ArrayList}屏蔽类Scala中可以屏蔽某个包中的类
import java.util._
import java.sql.{ Date_, Array_, _ } // 屏蔽Date、Array类给类起别名Scala中可以给类起别名简化使用这一点在python中也可以
import java.util.{ArrayListAList}object ScalaImport{def main(args: Array[String]): Unit {new AList()}
}导入包的绝对路径Scala中可以使用类的绝对路径而不是相对路径
new _root_.java.util.ArrayList默认情况下Scala中会导入如下包和对象
import java.lang._
import scala._
import scala.Predef._ // println就是在该类类
面向对象编程中类可以看成一个模板而对象可以看成是根据模板所创建的具体事物
定义类
基本语法
// 声明类访问权限 class 类名 { 类主体内容 }
[修饰符] class User { // 类的主体内容
}
// 对象new 类名(参数列表)
new User()说明
在Scala中类class 默认就是public全局公有可见的但是类并不声明为public
Scala中一个源文件中可以声明多个公共类这一点是Java所不具有的
package com.clear.oopimport scala.beans.BeanPropertyobject Test2 {def main(args: Array[String]): Unit {val student new Student()// student.name // 编译错误不能直接访问 private属性student.sex 男println(student.sex)}
}// 定义一个类
//public class Stu { // 报错
//
//}class Student { // 对于Scala的底层它其实是会将属性包装成private然后提供了get、set从而避免了想Java一样有很多像get、set这样的冗余代码// 定义属性//public var name: String 张三 // 编译报错默认就是public无须修饰private var name: String 张三BeanProperty // 显式地实现像Java一样的提供属性的get、set方法var age: Int 18var sex: String _ // _ 表示初始值为空注意这种情况必须使用 var
}如下就是Student类反编译的源码
package com.clear.oop;import scala.reflect.ScalaSignature;ScalaSignature(bytes\006\005I3A!\....0\t)public class Student
{private String name 张三;private int age 18;private String sex;private String name(){ return this.name; } private void name_$eq(String x$1) {this.name x$1; } public int age() {return this.age;} public void age_$eq(int x$1) {this.age x$1; } public String sex() {return this.sex; } public void sex_$eq(String x$1) {this.sex x$1; }public int getAge(){return age(); } public void setAge(int x$1) { age_$eq(x$1);}
}属性
基本语法
class User {var name : String _ // 类属性其实就是类变量var age : Int _ // 下划线表示类的属性默认初始化
}扩展语法
Scala中的属性其实在编译后也会生成方法
class User {var name : String _val age : Int 30private var email : String _ // 一般情况下不建议声明privateScala的属性默认底层就是privateBeanProperty var address : String _ // BeanProperty 会显式的生成get、set方法
}User类反编译后如下
import scala.reflect.ScalaSignature;ScalaSignature(bytes\006\0...\001)
public class User
{private String name;private final int age 30;private String email;private String address;public String name(){return this.name; } public void name_$eq(String x$1) { this.name x$1; } public int age() { return this.age; } private String email() { return this.email; } private void email_$eq(String x$1) { this.email x$1; } public String address() { return this.address; } public void address_$eq(String x$1) { this.address x$1; } public String getAddress() { return address(); } public void setAddress(String x$1) { address_$eq(x$1); }
}面向对象三大特性
封装
封装就是把抽象出的数据和对数据的操作封装在一起数据被保护在内部程序的其它部分只有通过被授权的操作成员方法才能对数据进行访问。Java封装操作如下 将属性进行私有化 提供一个公共的 set 方法用于对属性赋值 提供一个公共的 get 方法用于获取属性的值
Scala中的 public 不声明默认就是属性底层实际就是 private并通过 get 方法obj.field()和 set方法obj.field_(value) 对其进行操作。所以 Scala并不推荐我们将属性设置为 private再为其设置 public的get 和 set 方法。但是由于很多Java框架都利用反射调用 getXXX 和 setXXX 方法有时候为了兼容这些框架也会为Scala的属性设置 BeanProperty 注解以显式实现 getXXX 和 setXXX 方法
访问权限
在Java中访问权限分为public、private、protected 和 默认。在Scala中你可以通过类似的修饰符到达同样的效果。但是使用上有所区别 Scala 中的属性和方法的默认访问权限为public公有访问权限但Scala中无 public 关键字所以不能显式的声明。 private 为私有权限只在类的内部和伴生对象中可用 protected 为受保护权限Scala中受保护权限比Java中还严格同类、子类可以访问同包不能访问。 private[包名] 增加了 包访问权限包名下的其他类也可用使用
package com.clear.oopobject PackagingTest {
}// 定义一个父类
class Person {private var idCard: String 2445 // 只有本类和伴生对象可以使用protected var name: String 张三 // 只有本类和子类可以访问var sex: String 男 // 无修饰符在Scala中默认为public公有private[oop] var age: Int 18 // 包访问权限同包下都可以访问def printInfo(): Unit {println(sPerson: $idCard $name $sex $age)}
}package com.clear.oop// 定义一个子类
class Worker extends Person {override def printInfo(): Unit {// println(idCard) // 报错私有属性即使是子类也访问不到name 李四 // protected修饰只能在当前类 或 子类访问age 20 // 包访问权限可访问sex 女 // 无修饰符可访问println(sWorker: $name $sex $age)}
}object PackagingAccess {def main(args: Array[String]): Unit {// 创建对象val person: Person new Person// person.idCard // 父类私有属性无法访问// person.name // 报错 protected修饰其他类不能访问println(person.age) // 包访问权限在同一个包下可以访问println(person.sex)person.printInfo() // Person: 2445 张三 男 18val worker: Worker new Worker()}
}方法
基本语法
def 方法名(参数列表)[: 返回值类型] {// 方法体
}说明
在Scala中如果方法的返回值是 Unit这样的方法称之为 过程procedure
过程 的等号 可以省略不写例如
def sayHello() println(hello world)
// 等价于def sayHello() { println(hello world) } // {} 不可以省略创建对象
Scala中的对象和 Java 是类似的
val | var 对象名 [类型] new 类型()
var user : User new User()构造器
和 Java 一样Scala中构造对象也需要调用类的构造方法来创建。并且一个类中可以有任意多个不相同的构造方法即重载。
这些构造方法可以分为2大类主构造函数和辅助构造函数。
基本语法
class 类名(形参列表) { // 主构造器// 类体def this(形参列表) { // 辅助构造函数使用this关键字声明// 辅助构造函数应该直接或间接调用主构造函数}def this(形参列表) { // 辅助构造器可以有多个可重载// 构造器调用其他另外的构造器要求被调用构造器必须提前声明}
} 说明
辅助构造器函数的名称必须为 this可以有多个编译器通过参数的个数及类型来区分。辅助构造方法不能直接构造对象必须直接或简介调用主构造方法构造器调用其他另外的构造器要求被调用构造器必须提前声明
例如
package com.clear.oopobject Constructor {def main(args: Array[String]): Unit {// val s1 new Student() // ()可省略val s1 new Students1.student()val s2 new Student(张三)val s3 new Student(张三,18)}
}// 定义一个类
class Student() { // 如果主构造器没有参数列表()可省略// 定义属性var name: String _var age: Int _println(1 主构造方法被调用)// 声明辅助构造方法def this(name: String) {// 辅助构造方法应该直接或间接调用主构造方法this() // 直接调用主构造器println(2 辅助构造方法一被调用)this.name nameprintln(sname: $name age: $age)}def this(name: String, age: Int) {this(name) // 间接调用主构造器println(3 辅助构造方法二被起调用)this.age ageprintln(sname: $name age: $age)}def student(): Unit { // 定义一个与类名一样的方法但是它只是一个普通方法println(普通方法被调用)}
}构造器参数
Scala类的主构造器函数的形参包括三种类型未用任何修饰、var修饰、val修饰
未用任何修饰符修饰这个参数就是一个局部变量var修饰参数作为类的成员属性使用可以修改val修饰参数作为类只读属性使用不能修改
例如
object ConstructorParams {def main(args: Array[String]): Unit {val s1 new Student2println(ss1 name: ${s1.name} ${s1.age})val s2 new Student3(张三, 18)println(ss1 name: ${s2.name} ${s2.age})}
}// 定义一个类
class Student2 { // 无参构造器// 单独定义属性var name: String _var age: Int _
}// 上面定义等价于这种方式
class Student3(var name: String, var age: Int) // var修饰参数作为类的成员属性使用可以修改class Student4(_name: String, _age: Int) { // 主构造器参数无修饰这些参数就是一个局部变量var name name // 这种属于鸡肋语法受Java毒害太深了Scala不推荐这种方式var age age}
class Student5(val name: String, val age: Int) // var修饰参数作为类的成员属性使用可以修改继承与多态
继承
和 Java一样Scala中的继承也是单继承且使用extends关键字。
子类继承父类的属性和方法
继承的调用顺序父类构造器 子类构造器
基本语法
class 子类名 extends 父类名 { 类体 }例如·
package com.clear.oopobject Inherit {def main(args: Array[String]): Unit {val son1 new Son(张三,18)println(--------------------------)val son2 new Son(张三,18,666)}
}// 定义一个父类
class Father() {var name: String _var age: Int _println(1 父类主构造器被调用)def this(name: String, age: Int) {this() // 直接调用主构造器println(2 父类辅助构造器被调用)this.name namethis.age age}def printInfo(): Unit {println(sFather: $name $age)}
}class Son(name: String, age: Int) extends Father { // 这里会调用父类主构造器var idCard: String _println(3 子类主构造器被调用)def this(name: String, age: Int, idCard: String) {this(name, age)println(4 子类辅助构造器被调用)this.idCard idCard}override def printInfo(): Unit {println(sSon: $name $age $idCard)}
}结果
1 父类主构造器被调用
3 子类主构造器被调用 // 调用子类构造器前先调用父类构造器
--------------------------
1 父类主构造器被调用
3 子类主构造器被调用
4 子类辅助构造器被调用多态
在 Scala 中多态Polymorphism可以通过两种方式实现子类型多态Subtype Polymorphism和参数多态Parametric Polymorphism。
子类型多态Subtype Polymorphism 子类型多态是指在父类的引用变量中可以存储子类的对象即父类引用指向子类对象并且可以通过父类的引用变量调用子类的方法。在 Scala 中子类型多态可以通过继承和方法重写来实现。
class Animal {def sound(): Unit {println(Animal makes a sound)}
}class Dog extends Animal {override def sound(): Unit {println(Dog barks)}
}class Cat extends Animal {override def sound(): Unit {println(Cat meows)}
}
object PolymorphismTest{def main(args: Array[String]): Unit {val animal: Animal new Dog () // 父类引用指向子类对象animal.sound () // 编译看左边执行看右边实际上都是调用子类的实现所以输出 Dog barksval anotherAnimal: Animal new Cat ()anotherAnimal.sound () // Cat meows}
}参数多态Parametric Polymorphism 参数多态是指在函数或类中使用泛型类型参数使其可以适用于多种类型。在 Scala 中参数多态可以通过类型参数和高阶函数来实现。
object PolymorphismTest{def main(args: Array[String]): Unit {def printList[A](list: List[A]): Unit { // printList 函数使用了类型参数 A使其可以接受不同类型的 Listlist.foreach(println) // 函数定义中使用类型参数实现了参数多态}val intList: List[Int] List(1, 2, 3, 4, 5)val stringList: List[String] List(apple, banana, orange)printList(intList) // 输出1 2 3 4 5printList(stringList) // 输出apple banana orange}
}抽象类
抽象属性与抽象方法
1基本语法
定义抽象类
Scala将一个不完整的类称之为抽象类。
abstract class Person { // 通过abstract关键字标记抽象类
}定义抽象属性
Scala中如果一个属性只有声明没有初始化那么是抽象属性因为它不完整。
abstract class Person {var|val name:String // 该属性没有初始化就是抽象属性// 只有在抽象类中才可以
}定义抽象方法
Scala中如果一个方法只有声明而没有实现那么是抽象方法因为它不完整。
abstract class Person {def test():Unit // 只声明而没有实现的方法就是抽象方法
}注意
只要出现了抽象属性或抽象方法那么该类一定是抽象类
抽象类在也可以有普通属性、普通方法
2继承与重写
通过父类为抽象类那么子类需要将抽象的属性和方法实现否则子类也需要声明为抽象类
abstract class Person {var name:String
}
class User extends Person {var name : String zhangsan // 实现父类的抽象属性、方法否则子类也需要声明为抽象类
}重写非抽象方法需要用 override 修饰重写抽象方法则可以不加override子类中调用父类的方法需要使用 super 关键字子类对抽象属性进行实现父类抽象属性可以用 var 修饰子类对非抽象属性重写父类非抽象属性只支持 val 类型不支持 var 因为 var 修饰的为可变变量子类继承之后就可以直接使用没有必要重写
package com.clear.oopobject AbstractCass {def main(args: Array[String]): Unit {}
}// 定义抽象类
abstract class Person2 {// 非抽象属性val name: String personvar idCard 666 // var修饰的// 抽象属性未初始化值var age: Int// 非抽象方法def eat(): Unit {println(person eat)}// 抽象方法没有书写方法体def sleep(): Unit
}// 定义子类
class User2 extends Person2 {// todo 子类实现抽象属性、抽象方法var age 18override def sleep(): Unit { // 重写抽象方法 override 可以省略println(user sleep)}// todo 子类重写非抽象属性、方法override val name: String useroverride def eat(): Unit {println(user eat)}// todo 重写 var 修饰的非抽象属性// override var idCard 777 // 报错idea中没有提示但是编译报错
}匿名子类
和 Java一样可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类
在Scala中可以使用匿名子类来创建一个没有命名的子类。匿名子类可以用于实现接口、扩展类或重写方法。
基本格式
val 变量名: 父类或接口的类型 new 父类或接口的类型 {// 子类的实现代码
}例1
package com.clear.oopobject AnonymousClass {def main(args: Array[String]): Unit {val dog : Animal2 new Animal2 { // 抽象类不能直接实例化我们可以创建一个他的一个匿名子类override var name: String dogoverride def play(): Unit {println(汪汪汪)}}// 直接调用println(dog.name)println(dog.play())}
}// 定义抽象类
abstract class Animal2 {var name: String // 抽象属性def play(): Unit // 抽象方法
}例2
object AnonymousClass2 {def main(args: Array[String]): Unit {// 创建匿名子类实现了特质重写了抽象方法val animal: Animal3 new Animal3 {override def sound(): String Animal makes sound}println(animal.sound()) // Animal makes sound}
}// 定义一个特质类似于Java的接口
trait Animal3 {// 抽象方法def sound(): String
}单例对象伴生对象
所谓的单例对象就是在程序运行过程中指定类的对象只能创建一个而不能创建多个。这样的对象可以由特殊的设计方式获得也可以由语言本身设计得到比如 object 伴生对象Scala 语言是完全面向对象的语言所以并没有静态的操作即在Scala中没有静态的概念。但是为了能够 和Java 语言交互因为Java中有静态概念就产生了一种特殊的对象来模拟类对象该对象为单例对象。若单例对象名与类名一致则称该单例对象这个类的伴生对象这个类的所有“静态”内容都可以放置在它的伴生对象中声明然后通过伴生对象名称直接调用如果类名和 伴生对象名称保持一致那么这个类称之为伴生类。Scala编译器可以通过伴生对象的 apply 方法创建伴生类对象。apply方法可以重载并传递参数且可由Scala编译器自动识别。所以在使用时其实是可以省略的。在Scala中可以使用单例对象Singleton Object来创建一个只有一个实例的类。单例对象在Scala中非常常见用于实现全局共享的状态、提供工具方法、作为应用程序的入口点等要使用单例对象只需通过对象名直接调用其中的方法或访问其中的字段就像调用普通类的静态方法一样与java类似单例对象的特点是在程序运行期间只有一个实例存在因此可以在不同的地方共享状态和数据。此外单例对象还可以实现接口、扩展类、混入特质等具有与普通类相同的灵活性单例对象不能被实例化因为它们已经是单例。因此不能使用new关键字来创建单例对象的实例
object TestMain {def main(args: Array[String]): Unit {// 实例化Student类//val student new Student(张三,18) // 编译错误private类不能被外部访问// 通过 伴生对象名.属性 方式实例化类val student Student.apply(张三,18) // 通过伴生对象的apply方法构造伴生类实例student.printInfo()// todo scala编译器省略apply方法自动完成调用val student2 Student(李四,19) student2.printInfo()}
}// 一个类也是伴生类
class Student private(val name: String, val age: Int) {def printInfo(): Unit {println(sstudent [name$name, age$age, school ${Student.school}])// 其中 Student.school 是直接通过 伴生对象名.属性 方式访问的}
}// 一个单例对象名称与类一致也称伴生对象
object Student {val school: String xs// todo 在伴生对象中可以直接访问private// 定义一个方法实例化对象def apply(name: String, age: Int): Student new Student(name, age) // 构造伴生类实例
}说明
单例对象采用 object 关键字声明单例对象对应的类称之为伴生类伴生对象的名称应该与伴生类一致。单例对象的属性和方法都可以通过伴生对象名类名直接调用
单例对象与伴生对象的区别
在Scala中单例对象Singleton Object和伴生对象Companion Object是两个不同的概念但它们经常一起使用。单例对象是一个独立的对象它在程序运行期间只有一个实例存在。它可以包含方法、字段和其他成员可以用于实现全局共享的状态、提供工具方法等。单例对象的定义使用object关键字。伴生对象是与某个类关联的对象它与类名相同并且在同一个源文件中定义。伴生对象和类之间可以相互访问对方的私有成员。伴生对象通常用于定义与类相关的静态成员、工厂方法等。伴生对象的定义使用object关键字。
特质
Scala将多个类的相同特征从类中剥离出来形成一个独立的语法结构称之为“特质”特征。
这种方式在Java中称之为接口但是 Scala中没有接口的概念。所以 scala 中没有 interface关键字而是采用特殊的关键字 trait 来声明特质。
Scala中的 tarit中可以有抽象的属性、方法也可以有普通的属性、方法。
如果一个类符合某一个特征特质那么就可以将这个特征特质“混入”到类中。这种混入的操作可以在声明类时使用也可以在创建类对象时动态使用。
Scala引入 trait 特质第一可以替代Java的接口第二个也是对单继承机制的一种补充。
特质可以被类继承使用extends关键字也可以被其他特质混入使用with关键字。
特质的特点
多重继承一个类可以继承多个特质从而获得多个特质的功能。抽象成员特质可以定义抽象方法和字段需要在继承或混入的类中实现。默认实现特质可以提供方法的默认实现继承或混入的类可以选择重写或继承默认实现。动态混入特质可以在对象创建时动态混入为对象提供额外的功能。
基本语法
基本语法
trait 特质名称{// 定义方法、字段、抽象成员
}class 类名 extends 父类特质1 with 特质2 with 特质3 trait Operator {// extends 继承特质 // with 混入特质
}例如
// 定义一个特质
trait Young{// 声明抽象属性val name: String young// 声明抽象方法、非抽象方法def dating(): Unitdef play(): Unit {println(young people is playing)}
}// 定义User类继承Person混入Young特质
class User extends Person with Young {// todo 重写冲突的属性因为Person、Young中都有val string 李四// 实现抽象方法override def dating(): Unit {println(user...)}
}特质混入 特质混入Trait Mixin是Scala中一种灵活的代码组合机制通过将特质混入到类中可以为类提供额外的功能。 特质混入使用with关键字将特质添加到类的定义中。一个类可以混入多个特质特质的顺序决定了方法的调用顺序 特质混入的顺序很重要如果多个特质中有相同的方法名那么最后一个混入的特质的方法会覆盖之前的方法 例如如果MyClass类同时混入了两个特质且两个特质都有一个名为print()的方法那么最后一个混入的特质的print()方法会被调用。 特质混入是Scala中实现代码复用和组合的重要机制可以根据需要选择混入不同的特质灵活地组合和扩展类的功能。
例
trait Printable {def print(): Unit
}trait Loggable {def log(message: String): Unit
}// 混入两个特质
class MyClass extends Printable with Loggable {def print(): Unit {println(Printing...)}def log(message: String): Unit {println(sLogging: $message)}
}val obj new MyClass()
obj.print() // 输出Printing...
obj.log(Hello) // 输出Logging: Hello动态混入
动态混入Dynamic Mixing是指在运行时为对象添加特质的能力。在Scala中可以使用with关键字将特质动态混入到对象中
例如
// SomeClass是一个类SomeTrait是一个特质
// 通过使用with关键字我们可以在创建SomeClass的实例时动态地将SomeTrait混入到该实例中
val obj new SomeClass with SomeTrait通过动态混入特质我们可以为对象添加特定的行为而不需要修改原始类的定义。这种方式可以在不改变类层次结构的情况下为对象提供额外的功能。需要注意的是动态混入特质只对当前对象有效不会影响其他对象或类。每个对象可以根据自己的需要选择要混入的特质从而实现个性化的行为
特质叠加
由于一个类可以混入mixin多个trait且 trait 中可以有多个具体的属性和方法若混入的特质中有相同的方法方法名、形参列表、返回值均相同那必然会导致继承冲突问题。冲突分为两种
1一个类sub混入的两个trait 中两个相同的具体方法且两个trait之间没有任何的关系解决这类冲突直接在类中重写冲突方法即可
2一个类sub混入的两个trait 中两个相同的具体方法且两个trait都继承自同一个trait即钻石问题解决这类冲突Scala采用了特质叠加的策略。
在Scala中特质叠加Trait Stacking是一种特质组合的方式通过将多个特质叠加在一起形成一个新的特质从而将多个特质的功能合并到一个特质中。特质叠加类似于Java中的接口继承特质叠加使用with关键字将多个特质按顺序叠加在一起。特质的叠加顺序是从右往左的。也就是说当一个类混入多个特质时特质的代码会按照从右到左的顺序被叠加到类中。通过合理的特质叠加可以实现代码的复用和组合提高代码的灵活性和可维护性
trait A {def foo(): Unit {println(A foo)}
}trait B {def foo(): Unit {println(B foo)}
}// 特质C叠加特质A特质B
trait C extends A with B {override def foo(): Unit {super[A].foo() // 调用特质A的foo方法super[B].foo() // 调用特质B的foo方法println(C foo)}
}class MyClass extends C {def bar(): Unit {foo() // 调用特质C的foo方法}
}val obj new MyClass()
obj.bar() // 当调用MyClass的bar()方法会按照从右到左的顺序调用特质的方法即先调用特质B中的foo()方法然后调用特质A中的foo()方法// 总结来说特质的叠加顺序是从右往左的可以使用super关键字来指定调用特定特质的方法。特质混入与特质叠加
特质混入Trait Mixin和特质叠加Trait Stacking是Scala中两种不同的特质组合方式。
特质混入是将一个或多个特质添加到类的定义中通过使用with关键字将特质混入到类中。一个类可以混入多个特质特质的顺序决定了方法的调用顺序。特质混入可以为类提供额外的功能类可以使用混入的特质中定义的方法和字段。
特质叠加是将多个特质按顺序叠加在一起形成一个新的特质。叠加的顺序决定了方法的调用顺序从左到右依次调用。特质叠加可以将多个特质的功能组合在一起形成一个新的特质提供更丰富的功能。
特质混入和特质叠加都可以实现代码的复用和组合提高代码的灵活性和可维护性。它们在使用方式和效果上有一些区别
特质混入是将特质添加到类的定义中类可以使用混入的特质中定义的方法和字段。特质混入是一种静态组合方式特质的功能在编译时就确定了。特质叠加是将多个特质按顺序叠加在一起形成一个新的特质。特质叠加是一种动态组合方式特质的功能在运行时才确定。
特质混入和特质叠加可以根据具体的需求选择使用。特质混入适用于在类定义时就确定需要使用的特质而特质叠加适用于在运行时根据需要动态组合特质的功能。
特质自身类型
在Scala中特质Trait可以具有自身类型Self Type用于指定特质可以被哪些类型的类混入
自身类型可以实现依赖注入的功能
自身类型可以通过以下方式定义
trait MyTrait { _: SomeType // 表示特质只能混入到SomeType类中// Trait body
}自身类型的作用是限制特质的使用范围确保特质只能被指定的类型的类混入。这样可以在特质中使用类型SomeType的成员而不需要在特质中重新定义这些成员。
自身类型的使用
trait Logger {self: Person // 指定自身类型为Person即只有Person类能够混入该特质def log(message: String): Unit {println(s${self.name}: $message) // self.name 在特质中访问Person类name属性// this.name 也可以}
}class Person(val name: String) extends Logger { // 混入Logger特质def sayHello(): Unit {log(Hello!)println(sMy name is $name.)}
}object Main extends App {val person new Person(Alice)person.sayHello()
}结果
Alice: Hello!
My name is Alice.特质与抽象类的区别
在Scala中特质Trait和抽象类Abstract Class都可以用于定义可复用的代码片段和行为。它们有一些相似之处但也有一些重要的区别。
主要区别
继承关系一个类可以继承多个特质但只能继承一个抽象类。这是因为Scala支持多重继承特质但不支持多重继承抽象类。构造函数特质不能有构造函数参数而抽象类可以有构造函数参数。这是因为特质在被混入到类中时是作为一个独立的模块存在的而抽象类是作为一个类的一部分存在的。实例化特质不能被实例化而抽象类可以被实例化。特质只能被混入到类中使用而抽象类可以作为独立的类被实例化。默认实现特质可以提供方法的默认实现而抽象类可以提供方法的具体实现。特质中的方法可以是抽象的也可以是具体的而抽象类中的方法可以有具体的实现。继承顺序特质的混入顺序是从左到右而抽象类的继承顺序是从父类到子类。当一个类混入多个特质时特质的方法会按照混入的顺序被调用。使用场景特质通常用于定义可复用的行为而抽象类通常用于定义具有共同特征的类的基类。特质更适合用于组合多个行为而抽象类更适合用于定义类的继承关系。
建议
优先使用特质一个类扩展多个特质是很方便但是只有扩展一个抽象类
如果需要使用构造器参数使用抽象类。因为抽象类可以有带参数的构造器而特质不行。
扩展
类型检查和转换
1obj.isInstanceOf[T]判断 obj 是不是 T 类型
2obj.asInstanceOf[T]将 obj 强转为 T 类型
3classOf 获取对象的类名
Scala提供了两种类型转换的方式隐式类型转换和显式类型转换。
隐式类型转换Implicit Conversion隐式类型转换是指在编译器自动进行的类型转换无需显式地调用转换方法。隐式类型转换通常用于解决类型不匹配的问题使得代码更加简洁和易读。例如将一个整数转换为浮点数
val x: Int 10
val y: Double x // 隐式类型转换将Int类型转换为Double类型显式类型转换Explicit Conversion显式类型转换是指通过调用转换方法来显式地将一个对象从一种类型转换为另一种类型。显式类型转换使用asInstanceOf方法进行转换。例如将一个Any类型的对象转换为String类型
class Person{
}
object Person {def main(args: Array[String]): Unit {val person new Person//1判断对象是否为某个类型的实例val bool: Boolean person.isInstanceOf[Person]if ( bool ) {//2将对象转换为某个类型的实例显式类型转换val p1: Person person.asInstanceOf[Person]println(p1)}//3获取类的信息val pClass: Class[Person] classOf[Person]println(pClass)}
}注意显式类型转换可能会导致类型转换异常ClassCastException因此在进行显式类型转换时需要确保对象的实际类型与目标类型是兼容的。
另外Scala还提供了一种特殊的类型转换方式即模式匹配Pattern Matching。模式匹配可以根据对象的类型进行匹配并执行相应的操作。模式匹配可以用于类型转换、条件判断等场景。例如将一个Any类型的对象转换为String类型并进行处理
val x: Any Hello
x match {case s: String println(s) // 如果x是String类型则打印字符串case _ println(Not a string) // 如果x不是String类型则打印Not a string
}总结来说Scala中的类型转换可以通过隐式类型转换和显式类型转换来实现还可以使用模式匹配进行类型判断和转换。在进行类型转换时需要注意类型的兼容性并避免类型转换异常的发生。
枚举类和应用类
枚举类Enumeration可以使用Enumeration关键字定义枚举类。
枚举类的每个取值都是该类的实例对象
// 定义枚举类对象
object Weekday extends Enumeration {type Weekday Valueval Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday Value
}
// 可以通过 Weekday.Monday 等方式访问枚举值应用类Application应用类是一种特殊的类它可以直接运行而无需显式地定义main方法。
在Scala中可以通过继承App特质来定义应用类。应用类的代码会在程序启动时自动执行
object MyApp extends App {println(Hello, World!)
}注意
应用类只能有一个且必须是顶级对象即不能是内部类。应用类的代码会在程序启动时执行因此通常用于编写简单的脚本或测试代码。
Type定义新类型
使用type关键字可以定义新的数据数据类型名称本质上就是类型的一个别名
type Age Int
val age: Age 25type Name String
val name: Name Johntype Person (Name, Age)
val person: Person (John, 25)