Scala是一门完全面向对象的语言,摒弃了Java中很多不是面向对象的语法。
虽然如此,但其面向对象思想和 Java的面向对象思想还是一致的
1)基本语法
Scala中基本的package包语法和 Java 完全一致
例如:
package com.clear.bigdata.scala
2)Scala包的三大作用(和Java一样)
3)包的命名规则
只能包含数字、字母、下划线、小圆点,但是不能以数字开头,也不要使用关键字
命令规范:一般是小写字母 + 小圆点
Java中package包的语法比较单一,Scala对此进行扩展
Scala有两种包的管理风格,一种和Java的包管理风格相同,每个源文件一个包(包名和源文件所在路径不要求必须一致),包名用”.“进行分隔以表示包的层级关系,如:
com.clear.scala
另一种风格,通过嵌套的风格表示层级关系:
package com
package clear {
package bigdata {
package scala {
object ScalaPackage {
def test(): Unit = {
println("test...")
}
}
}
}
}
// 用嵌套风格定义包
package com {
import com.clear.scala.Inner
// 在外层包中定义单例对象
object Outer {
val out: String = "out"
def main(args: Array[String]): Unit = {
println(Inner.in) // 父包访问子包需要import
}
}
package clear {
package scala{
// 内存包中定义单例对象
object Inner{
val in: String = "in"
def main(args: Array[String]): Unit = {
Outer.out = "outer" // 子包中直接访问父包中的内容
println(Outer.out)
}
}
}
}
}
package com
package object clear { // 在 com.clear 包下创建的 clear包对象
val name : String = "zhangsan"
def 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.oop
object Test { // 在 com.clear.oop 包下创建了一个Test单例对象,去访问包对象定义的属性和方法
def main(args: Array[String]): Unit = {
commonMethod()
}
}
import java.util.List
import java.util._ // Scala中使用下划线代替Java中的星号
Java中 import 导入的语法比较单一,Scala对此进行扩展
object ScalaImport{
def main(args: Array[String]): Unit = {
import java.util.ArrayList
new ArrayList()
}
}
object ScalaImport{
def main(args: Array[String]): Unit = {
import java.util
new util.ArrayList()
}
}
import java.util.{List, ArrayList}
import java.util._
import java.sql.{ Date=>_, Array=>_, _ } // 屏蔽Date、Array类
import java.util.{ArrayList=>AList}
object ScalaImport{
def main(args: Array[String]): Unit = {
new AList()
}
}
new _root_.java.util.ArrayList
import java.lang._
import scala._
import scala.Predef._ // println就是在该类
面向对象编程中类可以看成一个模板,而对象可以看成是根据模板所创建的具体事物
// 声明类:访问权限 class 类名 { 类主体内容 }
[修饰符] class User {
// 类的主体内容
}
// 对象:new 类名(参数列表)
new User()
说明:
在Scala中,类class 默认就是public全局公有可见的,但是类并不声明为public
Scala中一个源文件中可以声明多个公共类(这一点是Java所不具有的)
package com.clear.oop
import scala.beans.BeanProperty
object 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 = 18
var 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 = 30
private var email : String = _ // 一般情况下,不建议声明private,Scala的属性默认底层就是private
@BeanProperty 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封装操作如下:
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.oop
object 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(s"Person: $idCard $name $sex $age")
}
}
package com.clear.oop
// 定义一个子类
class Worker extends Person {
override def printInfo(): Unit = {
// println(idCard) // 报错,私有属性即使是子类也访问不到
name = "李四" // protected修饰,只能在当前类 或 子类访问
age = 20 // 包访问权限,可访问
sex = "女" // 无修饰符,可访问
println(s"Worker: $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 张三 男 18
val worker: Worker = new Worker()
}
}
基本语法
def 方法名(参数列表)[: 返回值类型] = {
// 方法体
}
Scala中的对象和 Java 是类似的
val | var 对象名 [:类型] = new 类型()
var user : User = new User()
和 Java 一样,Scala中构造对象也需要调用类的构造方法来创建。并且一个类中可以有任意多个不相同的构造方法(即重载)。
这些构造方法可以分为2大类:主构造函数和辅助构造函数。
基本语法
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造函数,使用this关键字声明
// 辅助构造函数应该直接或间接调用主构造函数
}
def this(形参列表) { // 辅助构造器可以有多个(可重载)
// 构造器调用其他另外的构造器,要求被调用构造器必须提前声明
}
}
说明:
例如:
package com.clear.oop
object Constructor {
def main(args: Array[String]): Unit = {
// val s1 = new Student() // ()可省略
val s1 = new Student
s1.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 = name
println(s"name: $name age: $age")
}
def this(name: String, age: Int) {
this(name) // 间接调用主构造器
println("3 辅助构造方法二被起调用")
this.age = age
println(s"name: $name age: $age")
}
def student(): Unit = { // 定义一个与类名一样的方法,但是它只是一个普通方法
println("普通方法被调用")
}
}
Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰
例如:
object ConstructorParams {
def main(args: Array[String]): Unit = {
val s1 = new Student2
println(s"s1 name: ${s1.name} ${s1.age}")
val s2 = new Student3("张三", 18)
println(s"s1 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.oop
object 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 = name
this.age = age
}
def printInfo(): Unit = {
println(s"Father: $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(s"Son: $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 barks
val 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,使其可以接受不同类型的 List
list.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 5
printList(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" // 实现父类的抽象属性、方法,否则子类也需要声明为抽象类
}
package com.clear.oop
object AbstractCass {
def main(args: Array[String]): Unit = {
}
}
// 定义抽象类
abstract class Person2 {
// 非抽象属性
val name: String = "person"
var idCard = "666" // var修饰的
// 抽象属性(未初始化值)
var age: Int
// 非抽象方法
def eat(): Unit = {
println("person eat")
}
// 抽象方法(没有书写方法体)
def sleep(): Unit
}
// 定义子类
class User2 extends Person2 {
// todo 子类实现抽象属性、抽象方法
var age = 18
override def sleep(): Unit = { // 重写抽象方法 override 可以省略
println("user sleep")
}
// todo 子类重写非抽象属性、方法
override val name: String = "user"
override def eat(): Unit = {
println("user eat")
}
// todo 重写 var 修饰的非抽象属性
// override var idCard = "777" // 报错,idea中没有提示,但是编译报错
}
和 Java一样,可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类
在Scala中,可以使用匿名子类来创建一个没有命名的子类。匿名子类可以用于实现接口、扩展类或重写方法。
基本格式
val 变量名: 父类或接口的类型 = new 父类或接口的类型 {
// 子类的实现代码
}
例1:
package com.clear.oop
object AnonymousClass {
def main(args: Array[String]): Unit = {
val dog : Animal2 = new Animal2 { // 抽象类,不能直接实例化,我们可以创建一个他的一个匿名子类
override var name: String = "dog"
override 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
}
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(s"student [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
关键字。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(): Unit
def 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(s"Logging: $message")
}
}
val obj = new MyClass()
obj.print() // 输出:Printing...
obj.log("Hello") // 输出:Logging: Hello
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采用了特质叠加的策略。
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(s"My name is $name.")
}
}
object Main extends App {
val person = new Person("Alice")
person.sayHello()
}
结果:
Alice: Hello!
My name is Alice.
在Scala中,特质(Trait)和抽象类(Abstract Class)都可以用于定义可复用的代码片段和行为。它们有一些相似之处,但也有一些重要的区别。
主要区别:
建议:
优先使用特质,一个类扩展多个特质是很方便,但是只有扩展一个抽象类
如果需要使用构造器参数,使用抽象类。因为抽象类可以有带参数的构造器,而特质不行。
(1)obj.isInstanceOf[T]:判断 obj 是不是 T 类型
(2)obj.asInstanceOf[T]:将 obj 强转为 T 类型
(3)classOf 获取对象的类名
Scala提供了两种类型转换的方式:隐式类型转换和显式类型转换。
val x: Int = 10
val y: Double = x // 隐式类型转换,将Int类型转换为Double类型
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 = Value
val Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday = Value
}
// 可以通过 Weekday.Monday 等方式访问枚举值
应用类(Application):应用类是一种特殊的类,它可以直接运行,而无需显式地定义main
方法。
在Scala中,可以通过继承App
特质来定义应用类。应用类的代码会在程序启动时自动执行
object MyApp extends App {
println("Hello, World!")
}
注意:
应用类只能有一个,且必须是顶级对象(即不能是内部类)。应用类的代码会在程序启动时执行,因此通常用于编写简单的脚本或测试代码。
使用type关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名
type Age = Int
val age: Age = 25
type Name = String
val name: Name = "John"
type Person = (Name, Age)
val person: Person = ("John", 25)