Scala高级语法入门 (六)Scala中的异常&隐式转换&泛型

🙆‍♂️🙆‍♂️ 写在前面

​🏠​ 个人主页:csdn春和
📚 推荐专栏:更多专栏尽在主页!
JavaWeb专栏(从入门到实战超详细!!!)
SSM专栏 (更新中…)
​📖​ 本期文章:Scala高级语法入门 (六)Scala中的异常&隐式转换&泛型
本篇文章作为Scala系列的完结篇,感谢各位码友一直以来的支持与厚爱💜



一、Scala异常

Scala异常语法处理上和Java类似,但是又不尽相同。

1.1、Java中的异常

try {
    int a = 10;
    int b = 0;
    int c = a / b;
} catch (ArithmeticException e){
    // catch时,需要将范围小的写到前面
    e.printStackTrace();
} catch (Exception e){
    e.printStackTrace();
} finally {
    System.out.println("finally");
}

java中异常的捕获是从小大大

(1)Java 语言按照 try—catch—finally 的方式来处理异常

(2)不管有没有异常捕获,都会执行 finally,因此通常可以在 finally 代码块中释放资源

(3)可以有多个 catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面, 把范围大的异常类写在后面,否则编译错误。

但是异常并不是我们看上去那么简单

我们来看看下面的代码执行情况是什么?你知道吗?

public class JavaTestException {
    public static void main(String[] args) {

        int j = test();

        System.out.println(j);
    }

    public static int test(){
        int i = 0;
        try {
            return i++;
        }finally {
            return ++i;
        }
    }
}

在这里插入图片描述

❓ 执行结果为2 这是为什么?

一旦源码看不懂 我们就去看字节码

javap -v JavaTestException 查看字节码文件

1、return关键字不会马上返回结果

2、所有的return返回的都是同一个值:临时变量

所以上述代码中return不会马上返回 它要等finally执行完毕后返回,但是finally里面也有return 就相当于对临时变量进行了操作 最终return返回的都是同一个值。

在这里插入图片描述

如果finally里面没有return 那么 临时变量不会被修改 返回 0

1.2、Scala中的

Scala中的异常不区分所谓的编译时异常和运行时异常,也无需显示抛出方法异常,所以Scala中没有throws关键字。

object Scala_exception1 {
  def main(args: Array[String]): Unit = {
    try {
      var n= 10 / 0
    } catch {
      case ex: ArithmeticException=>{
        // 发生算术异常
        println("发生算术异常")
      }
      case ex: Exception=>{
        // 对异常处理
        println("发生了异常1")
      }
    } finally {
      println("finally")
    }
  }
}

Scala中也要求异常范围大的写在后面

1、我们将可疑代码封装在 try 块中。在 try 块之后使用了一个 catch 处理程序来捕获异常。
如果发生任何异常,catch 处理程序将处理它,程序将不会异常终止。
2、Scala 的异常的工作机制和 Java 一样,但是 Scala 没有“checked(编译期)”异常,
即 Scala 没有编译异常这个概念,异常都是在运行的时候捕获处理。
3、异常捕捉的机制与其他语言中一样,如果有异常发生,catch 子句是按次序捕捉的。
因此,在 catch 子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异常写在前,
把具体的异常写在后,在 Scala 中也不会报错,但这样是非常不好的编程风格。
4、finally 子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和 Java 一样。

java 提供了 throws 关键字来声明异常。可以使用方法定义声明异常。它向调用者函数提供了此方法可能引发此异常的信息。它有助于调用函数处理并将该代码包含在 try-catch 块中,以避免程序异常终止。

在 Scala 中,可以使用 throws 注解来声明异常

def main(args: Array[String]): Unit = {
 f11()
}
@throws(classOf[NumberFormatException])
def f11()={
 "abc".toInt
}

二、隐式转换

2.1、什么是隐式转换

在之前的类型学习中,我们已经学习了自动类型转换,精度小的类型可以自动转换为精度大的类型,这个转换过程无需开发人员参与,由编译器自动完成,这个转换操作我们称之为隐式转换。

在其他的场合,隐式转换也起到了非常重要的作用。如Scala在程序编译错误时,可以通过隐式转换中类型转换机制尝试进行二次编译,将本身错误无法编译通过的代码通过类型转换后编译通过。慢慢地,这也形成了一种扩展功能的转换机制。

所谓的隐式转换其实就是类型的转换

2.2、隐式函数

在函数前面加上implicit关键字就是隐式函数了

看看下面的代码:

object Scala_Transform1 {

  def main(args: Array[String]): Unit = {
    
    // 获取第三方的提供的数据
    val age:Int = thirdPart()
    println(age)
  }

  // 第三方 提供的数据 年龄
  def thirdPart(): Int = {
    20
  }

}

打印输出20没问题,那么问题来了。第三方发现年龄有半岁,使用int表示不再合适,所以改为了Double类型

// 第三方 提供的数据 年龄
def thirdPart(): Double = {
  20.30
}

之前我们提到过OCP开发原则,在不改变源码的情况下如何使得编译器不报错

在这里插入图片描述

这里我们就提到了Scala中的隐式转换机制,他会在全局寻找可以使得编译通过的方法,使得二次编译通过

所以这里我们编写一个方法 使得double类型数据转换为int类型数据

// 增加一个将double类型转为int类型的方法 
def transform(num:Double): Int ={
  num.toInt
}

但是这个方法和我们的隐式转换机制怎么联系起来呢?加上implicit关键字

 // 增加一个将double类型转为int类型的方法
implicit def transform(num:Double): Int ={
   num.toInt
 }

在这里插入图片描述

隐式转换可以在不需改任何代码的情况下,扩展某个类的功能。

2.2、隐式参数

普通方法或者函数中的参数可以通过 implicit 关键字声明为隐式参数,调用该方法时, 就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。

(1)同一个作用域中,相同类型的隐式值只能有一个

(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。

(3)隐式参数优先于默认参数

object Scala_Transform2 {

  def main(args: Array[String]): Unit = {
    // TODO 隐式转换 隐式参数

    // 隐式参数
    def reg(implicit passwd:String = "000000"): Unit ={
      println(s"密码为:${passwd}")
    }


    reg()
    reg("123123")

    // 隐式变量
    implicit val passwd = "123456"  // 通过隐式参数可以修改默认值

    // 隐式参数是不用传递的,这个过程是由编译器完成
    reg  // 输出的默认密码就是123456

    // 如果传了参数 那么就不去查找隐式变量了
    reg()  // 输出 000000
  }
}

在这里插入图片描述

在同一个作用域中相同的转换规则会报错

2.3、隐式类

在 Scala2.10 后提供了隐式类,可以使用 implicit 声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用

object Scala_Transform3 {

  def main(args: Array[String]): Unit = {
    // TODO 隐式转换 隐式参数
    
    val user = new User()
    user.insertUser()
    
  }
  
  
  class User{
    def insertUser(): Unit ={
      println("添加用户!")
    }
  }
  
}

加入我们想要扩展一个修改用户的功能该怎么做?

在这里插入图片描述

我们可以再写一个类扩展功能

class UserExtend(){
  def updateUser(): Unit = {
    println("修改用于!")
  }
}

然后在再写一个隐式函数做转换

implicit def transforUser(user: User): UserExtend = {
  new UserExtend
}

在这里插入图片描述

但是这样做显得十分的麻烦

我们可以使用隐式类

implicit class UserExtend(user: User) {
  def updateUser(): Unit = {
    println("修改用于!")
  }
}

这样更容易扩展类的功能

注意事项

1、其所带的构造参数有且只能有一个

2、隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的。

2.4、隐式机制

所谓的隐式机制,就是一旦出现编译错误时,编译器会从哪些地方查找对应的隐式转换规则

1、当前代码作用域

2、当前代码上级作用域

3、当前类所在的包对象

4、当前类(对象)的父类(父类)或特质(父特质)

其实最直接的方式就是直接导入。

三、泛型

3.1、简介

Scala的泛型和Java中的泛型表达的含义都是一样的,对处理的数据类型进行约束,但是Scala提供了更加强大的功能

1、泛型和类型的区别?

​ 类型是约束外部的数据
泛型是约束内部的数据

2、泛型在某些场合中其实就是类型参数,用于向类中传递采纳数

3、泛型只在编译时有效,将这个操作称之为 “泛型擦除”

4、泛型的主要目的是为了约束内部数据

scala中的泛型用[] 表示

3.2、泛型转换

3.2.1、泛型不可变

scala中的泛型也是不可变得

new 的是类型 而不是泛型 泛型只是约束力内部的数据

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

        val test1 : Test[User] = new Test[User] // OK
        val test2 : Test[User] = new Test[Parent] // Error
        val test3 : Test[User] = new Test[SubUser]  // Error

    }
    class Test[T] {
    }
    class Parent {
    }
    class User extends Parent{
    }
    class SubUser extends User {
    }

3.2.2、泛型协变

马丁想着泛型和类型不是同一个层面的东西,所以无法联合使用,不方便,如果能将类型和泛型当成一个整体来使用的话不就方便了?

如果将类型和泛型联合使用,那么类型相同,泛型存在父子关系,那么整体就存在父子关系,这种操作其实就是一种变化,称之为 协变 +T

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

        val test1 : Test[User] = new Test[User] // OK
        val test2 : Test[User] = new Test[Parent] // Error
        val test3 : Test[User] = new Test[SubUser]  // OK

    }
    class Test[+T] {
    }
    class Parent {
    }
    class User extends Parent{
    }
    class SubUser extends User {
    }

3.2.3、泛型逆变

如果将类型和泛型联合使用,那么类型相同,泛型存在父子关系,那么整体就存在子父关系,这种操作其实就是一种变化,称之为逆 变 -T

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

        val test1 : Test[User] = new Test[User] // OK
        val test2 : Test[User] = new Test[Parent] // OK
        val test3 : Test[User] = new Test[SubUser]  // Error

    }
    class Test[-T] {
    }
    class Parent {
    }
    class User extends Parent{
    }
    class SubUser extends User {
    }

3.2.4、泛型的上限

object Scala_geniric4 {

  // 泛型的上限
  def main(args: Array[String]): Unit = {
    val parent: Parent = new Parent()
    val user: User = new User()
    val subuser: SubUser = new SubUser()
//    test[Parent](parent) // Error
    test[User](user) // OK
    test[SubUser](subuser) // OK
  }

  // 上限采用颜文字 
  def test[A <: User](a: A): Unit = {
    println(a)
  }

  class Parent {
  }

  class User extends Parent {
  }

  class SubUser extends User {
  }
}

3.2.5、泛型的下限

def main(args: Array[String]): Unit = {
        val parent : Parent = new Parent()
        val user : User = new User()
        val subuser : SubUser = new SubUser()
        test[Parent](parent) // OK
        test[User](user)   // OK
        test[SubUser](subuser) // Error
    }
    def  test[A>:User]( a : A ): Unit = {
        println(a)
    }
    class Parent {
    }
    class User extends Parent{
    }
    class SubUser extends User {
    }
}

3.2.6、上下文限定

1)语法

def f[A : B](a: A) = println(a) //等同于def f[A](a:A)(implicit arg:B[A])=println(a)

2)说明

上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过 implicitly[Ordering[A]] 获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。

object ScalaGeneric {
    def main(args: Array[String]): Unit = {
        def f[A : Test](a: A) = println(a)
        implicit val test : Test[User] = new Test[User]
        f( new User() )
    }
    class Test[T] {
    }
    class Parent {
    }
    class User extends Parent{
    }
    class SubUser extends User {
    }
}
  • 43
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 59
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 59
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值