• Scala基础【异常、隐式转换、泛型】


    一 异常

    1 java中的异常

    分为两大类:编译时异常,运行时异常,所有的异常都是在运行时产生的

    在编写代码时标红只是为了程序的健壮性,提示开发者代码会有问题,需要想办法针对不同的问题解决它,如果不处理或者处理不了,需要将其抛出去(throws Exception),谁调用这段程序谁去处理,如果最终没人能够处理,那么java会直接崩溃

    其中的try catch就是自己先尝试解决一下,加入这段代码程序并不会崩溃,会继续向下执行

    异常会按照顺序进行捕捉,所以一般将范围大的异常放到下面

    public static void main(String[] args) {
        try {
            int i = 0;
            int j = 10 / i;
            new FileInputStream("xxxx");
        } catch (ArithmeticException e){
            e.printStackTrace();
            System.out.println("算数异常");
        }
        catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("程序会继续执行");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    执行return语句之前会先判断有没有finally,如果有就执行完finally之后再返回,如果没有直接返回

    需要注意

    • return关键字不会马上返回结果
    • 所有的return返回同一个值:临时变量,1+1+1=3

    以下代码返回结果为2,注意中间变量_temp_

    public static void main(String[] args) {
        int j = test();
        System.out.println(j);
    }
    public static int test(){
        int i = 0;
        try {
            // _temp = i++      _temp = 0, i = 1
            // return _temp     不会马上执行,有finally,向下执行   
            return i++;
        }finally {
            // _temp = ++i      i = 2, _temp = 2
            // return _temp
            return ++i;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    如果去掉return ++i;加上++i,返回结果为0

    int i = 0;
    try {
        // _temp = i++      _temp = 0, i = 1
        // return _temp     不会马上执行,有finally,向下执行
        return i++;
    }finally {
        // _x = ++i      i = 2, _x = 2, _temp = 0
        // return _temp
        //return ++i;
        ++i;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2 scala中的异常

    scala知道人们都不知道异常是什么,所以在scala中异常没有分类,当出现异常时也会添加try catch语句

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

    try{
      val i = 0;
      val j = 10 / i;
      new FileInputStream("xxx")
    }catch {
      case e: ArithmeticException => println("算数异常")
      case e: Exception => println("其他异常")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3 常见问题

    (1)ClassCastException

    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(1);
        list.add("ad");
        List<User> list1 = list;
        for(User user : list1){
            System.out.println(user);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (2)NullPointerException

    当调用一个为空对象的成员方法或者成员属性会出现空指针异常

    因为增强for循环使用了集合的迭代器,所以会出现空指针异常

    public static void main(String[] args) {
        List list = null;
        for (Object obj : list){
            System.out.println(obj);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (3)如果Java程序调用scala代码,如何明确异常

    增加注解 @throws[Exception]

    object Scala02_Exception {
      def main(args: Array[String]): Unit = {
      }
      @throws[Exception]
      def test(): Unit ={
        throw new Exception("aaa")
      }
    }
    public class TestAnn {
        public static void main(String[] args) {
            Scala02_Exception.test();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4 final finalize 和finally

    final修饰的属性为不可变变量,不是一个常量,只是约束了一个变量的值在初始化后不能够改变

    public final String name;
    public String email;
    public TestDept(){
        name = "zhangsan";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    name不赋值的情况下会报错,email在构建对象时系统会进行初始化(null)

    GC在垃圾回收时会执行下面的方法,flinalize是析构方法,主要目的是在回收前问一下此对象还需要做什么,告诉垃圾回收器,因为某种原因,这个对象是不能够被回收的,但是在第二次回收时,不会调用这个方法

    二 隐式转换

    1 介绍

    现想要从第三方库中拿到年龄,因为需求更改,第三方库中的年龄Int变为了Double类型

      def main(args: Array[String]): Unit = {
        //val age : Int = thirdPart()
        val age : Int = thirdPart().toInt
      }
      def thirdPart(): Double = {
        20.0
      }
    //def thirdPart(): Int = {
    // 20
    //}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    以上代码违背了OCP开发原则

    OCP:允许功能扩展,但不能够修改源代码

    如果程序编译出错,编译器会尝试在整个的作用域中查找能够让程序编译通过的方式,如果找到,编译器会尝试二次编译,让之前出现错误的代码经过二次编译后能够通过,这个转换的过程看不到,但是存在,称之为隐式转换,使用关键字implicit声明隐式转换方法,慢慢地,这也形成了一种扩展功能的转换机制

    隐式转换就是类型的转换

    implicit def transform(d : Double): Int ={
      d.toInt
    }
    def main(args: Array[String]): Unit = {
      val age : Int = thirdPart()
      println(age)
    }
    def thirdPart(): Double = {
      20.0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2 隐式函数

    使用implicit声明的函数称为隐式函数

    功能的扩展,以下代码只在以new的方式新建对象时可行,也即特质只在new时可用

      def main(args: Array[String]): Unit = {
        val user = new User with UpdateUser
        user.insertUser()
        user.updateUser()
      }
      trait UpdateUser{
        def updateUser(): Unit ={
          println("update User...")
        }
      }
      class User{
        def insertUser(): Unit ={
          println("insert User...")
        }
      }
      def getUser(): User ={
        new User
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    使用get的方式获取对象,隐式函数的方式扩展功能

    def main(args: Array[String]): Unit = {
      //声明方法
      implicit def transform(user: User) : UserExt ={
        new UserExt()
      }
      val user = getUser()
      user.insertUser()
      user.updateUser()
    }
    //声明类
    class UserExt{
      def updateUser(): Unit ={
        println("update User...")
      }
    }
    trait UpdateUser{
      def updateUser(): Unit ={
        println("update User...")
      }
    }
    class User{
      def insertUser(): Unit ={
        println("insert User...")
      }
    }
    def getUser() ={
      new User()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    3 隐式参数、隐式变量

    隐式参数方便修改,解耦合,OCP开发原则

    隐式参数不用传递,传递的过程由编译器完成

    reg不加括号,找的是隐式变量,加括号是传值

    def main(args: Array[String]): Unit = {
      //隐式参数
      def reg(implicit password : String = "000000"): Unit ={
        println("默认密码:" + password)
      }
      reg()
      reg("123123")
      //隐式变量
      implicit val password : String = "111111"
      reg
      
      val list = List(1,3,2,4)
      list.sortBy(num=>num)
      list.sortBy(num=>num)(Ordering.Int.reverse)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在同一个作用域中,如果有相同的转换规则的多个数据,会发生错误

    4 隐式类

    scala中可以将implicit关键字声明在类的前面称为隐式类,其不能为顶级对象

    在Scala2.10后提供了隐式类,可以使用implicit声明类,隐式类非常强大,同样可以扩展类的功能,在集合的数据处理中,隐式类发挥了重要的作用。其所带的构造参数有且只能有一个

    def main(args: Array[String]): Unit = {
      val user = new User()
      user.insertUser()
      user.updateUser()
    }
    //将User类转换成UserExt
    implicit class UserExt(user: User){
      def updateUser(): Unit ={
        println("update user")
      }
    }
    class User{
      def insertUser(): Unit ={
        println("insert User")
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    5 隐式机制

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

    • 当前代码作用域
    • 当前代码上级作用域
    • 当前类所在的包对象
    • 当前类(对象)的父类(父类)或特质(父特质)或伴生对象
    • 其实最直接的方式就是直接导入

    三 泛型

    1 java中的泛型

    (1)泛型和类型

    所谓的类型其实就是对外部的数据做约束,所谓的泛型其实就是对内部的数据做约束

    泛型和类型的层次不一样,不能作为整体来考虑

    以下代码中T为泛型,Test就是类型

    class Test<T>{
        public T t;
    }
    
    • 1
    • 2
    • 3

    (2)类型参数

    泛型在某些场合中,称为类型参数,用于向类中传递参数

    Test<User> userTest = new Test<User>();
    User t = userTest.t;
    
    Test userTest1 = new Test();
    Object t1 = userTest1.t;
    
    class Test<T>{
        public T t;
    }
    class User{
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    (3)泛型擦除

    泛型只在编译时有效,泛型为了防止类型不一致,对类型做了个约束,将这个操作称为“泛型擦除”

    Test<User> userTest = new Test<User>();
    System.out.println(userTest);
    
    • 1
    • 2

    (4)约束数据类型

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

    泛型不对前两行代码起作用,不能对现有数据做处理,因为如果想对集合内部数据做处理,需要在运行时完成,而泛型又只在编译时才有效,所以泛型没有这个能力

    第四行代码:打印集合,如果集合内部有对象(数据),会调用数据的toString方法,toString方法来自于Object和User和Emp没有关系,所以不会改变类型,不会约束类型,在当前场合下,类型不起任何作用,所以可以运行

    List list = new ArrayList();
    list.add(new Emp());
    List<User> userList = list;
    System.out.println(list);
    
    • 1
    • 2
    • 3
    • 4

    但是如果约定了泛型,对类型做处理时一定会报错,处理的User类型,但集合内的数据是Emp类型

    //ClassCastException
    for(User user : userList){
    
    }
    
    • 1
    • 2
    • 3
    • 4

    (5)父子关系

    泛型和类型不是一个层次,类型之间存在父子关系,而泛型之间没有所谓的父子关系

    以下代码stringList1报错,将泛型换为Object,stringList报错

    public static void main(String[] args) {
        ArrayList<String> stringList = new ArrayList<String>();
        test(stringList);
    
        ArrayList<Object> stringList1 = new ArrayList<Object>();
        test(stringList1);
    }
    public static void test(List<String> list){
        System.out.println(list);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    类型的上下级关系如下

    ArrayList<Object> stringList1 = new ArrayList<Object>();
    test(stringList1);
    ArrayList<Object> stringList2 = new ArrayList<Object>();
    test(stringList2);
    
    • 1
    • 2
    • 3
    • 4

    (6)泛型不可变

    childList2,childList3报错

    不过在使用时,确定好上下限的前提下,可以改变,详情见2泛型边界

    public class Scala03_Generic_Java {
        public static void main(String[] args) {
            ArrayList<Child> childList1 = new ArrayList<Child>();
            //ArrayList childList2 = new ArrayList();
            //ArrayList childList3 = new ArrayList();
        }
    }
    class Parent{
    }
    class Child extends Parent{
    }
    class SubChild extends Child{
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (7) 泛型边界

    工厂模式:创建的对象具有相同的特征,约定类型时,只需要有相同的特征就可以

    在实际环境中,为了使用方便,可以定义泛型的边界,边界就是其上哪里找的问题

    生产数据,强调数据的通用性,类型的下限,传递的类型向上找

    消费数据,强调功能的完整性,类型的上限,传递的类型向下找

    public class Scala04_Generic_Java {
        public static void main(String[] args) {
            //向上找
            Producer<Child> p = new Producer<Child>();
            p.produce(new Message<Child>());
            p.produce(new Message<Parent>());
            //p.produce(new Message());
            
            //向下找
            Consumer<Child> c = new Consumer<Child>();
            Message<? extends Child> message = c.getChlidMessage();
            final Child data = message.data;
        }
    }
    
    class Message<T>{
        public T data;
    }
    //生产数据,下限,向上找
    class Producer<A>{
        public void produce( Message<? super A> message){
    
        }
    }
    //消费数据,上限,向下找
    class Consumer<B>{
        public Message<? extends B> getChlidMessage(){
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    2 scala中的泛型

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

    (1)泛型转换

    泛型不可变

    scala中的泛型使用中括号,同样也是不可变的

    val message1 : Message[Child] = new Message[Child]()
    //val message2 : Message[Child] = new Message[Parent]()
    //val message3 : Message[Child] = new Message[SubChild]()
    
    • 1
    • 2
    • 3

    泛型协变

    泛型和类型不是一个层面上的东西,所以无法联合使用,不方便

    如果能将类型和泛型当成一个整体来使用的话,将会十分方便

    如果将类型和泛型联合使用,类型相同时,如果泛型存在父子类关系,那么联合的类型也就存在父子关系

    这个操作其实就是一种变化,称为“协变”,+T

    def main(args: Array[String]): Unit = {
    
      val message1 : Message[Child] = new Message[Child]()
      //val message2 : Message[Child] = new Message[Parent]()
      val message3 : Message[Child] = new Message[SubChild]()
    }
    
    class Message[+T]{}
    
    class Parent {}
    
    class Child extends Parent {}
    
    class SubChild extends Child {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    泛型逆变

    如果类型相同,泛型之间如果存在父子关系,那么让联合后的类型存在子父关系,颠倒关系

    这个操作其实也是一种变化,称为“逆变”,-T

    出现了以下的变化过程

    Child(父) — SubChild(子)

    MessageChild(子) — MessageSubChild(父)

    class Message[-T]{}
    val message1 : Message[Child] = new Message[Child]()
    val message2 : Message[Child] = new Message[Parent]()
    //val message3 : Message[Child] = new Message[SubChild]()
    
    • 1
    • 2
    • 3
    • 4

    (2)泛型边界

    scala中的泛型也存在上限和下限的概念,上限和下限使用的是颜文字

    def main(args: Array[String]): Unit = {
      val p = new Producer[Child]
      p.produce(new Message[Child])
      p.produce(new Message[Parent])
      //p.produce(new Message[SubChild])
      
      val c = new Consumer[Child]
      val m: Message[_ <: Child] = c.consumer
      val data: Child = m.data
    }
    
    class Message[T] {
      var data : T = _
    }
    
    class Parent {}
    
    class Child extends Parent {}
    
    class SubChild extends Child {}
    
    //生产数据,保证通用性,向上找,泛型下限,范围越来越大
    class Producer[T]{
      def produce(message: Message[ _ >: T]): Unit ={
      }
    }
    //消费数据,不缺失功能,向下找,泛型上限,范围越来越小
    class Consumer[T]{
      def consumer : Message[_ <: T] = {
        null
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    (3)集合中的泛型

    val listNum = List(1,2,3,4)
    listNum.reduce(_+_)
    
    • 1
    • 2

    reduce方法的定义,是一个生产者,向上找

    def reduce[A1 >: A](op: (A1, A1) => A1): A1 = reduceLeft(op)
    
    • 1

    A的含义,就是数据类型,在这里是Int,

    trait TraversableOnce[+A] extends Any with GenTraversableOnce[A] {
      self =>
    
    • 1
    • 2

    Parent,Child,SubChild

    val list : List[Child] = List(
      new Child(), new Child(), new Child()
    )
    // 原类型为Child,需要指定大于Child的类型
    val parent: Parent = list.reduce[Parent](
      (c1, c2) => c1
    )
    // 小于Child的类型会编译出错
    /**
     * Error:(12, 41) type arguments do not conform to method reduce's type parameter bounds
     * [A1 >: com.hike.bigdata.scala.chapter11.Scala03_Generic.Child]
     */
    //    val SubChild: SubChild = list.reduce[SubChild](
    //      (c1, c2) => c1
    //    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    几乎所有集合的所有方法都存在泛型

    fold方法的定义

    def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
    
    • 1
    list.fold[Parent](new Parent())(
      (x,y)=>x
    )
    //出错  A1 >:
    //    list.fold[SubChild](new SubChild())(
    //      (x,y)=>x
    //    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    foldLeft方法的定义,泛型没有约束

    def foldLeft[B](z: B)(@deprecatedName('f) op: (B, A) => B): B = 
    
    • 1

    测试

    list.foldLeft[SubChild](new SubChild){
      (x,y)=>x
    }
    
    • 1
    • 2
    • 3

    (4)上下文限定

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

    // 语法含义:表示在调用f方法时调用一个隐式变量,隐式变量类型为Test,泛型是A
    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 {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    隐式转换错误(…),此时A为SubUser,那么就需要找一个Test类型,泛型为SubUser的隐式变量

    f( new SubUser() )
    
    • 1

    修改可执行成功

    implicit val test : Test[SubUser] = new Test[SubUser]
    f( new SubUser() )
    
    • 1
    • 2

    同理

    implicit val test : Test[Parent] = new Test[Parent]
    f( new Parent() )
    
    • 1
    • 2
  • 相关阅读:
    物理服务器安装CentOS 7操作系统
    RuntimeError: cuDNN error: CUDNN_STATUS_INTERNAL_ERROR
    基于JavaWeb的学生成绩管理系统
    如何快速将Zabbix5.0升级至6.0?
    工程管理系统简介 工程管理系统源码 java工程管理系统 工程管理系统功能设计
    C现代方法(第3、4章)笔记
    python-数据描述与分析(1)
    Uni-APP 安卓环境本地打包
    react项目配置(类组件、函数式组件)
    MySQL备份与恢复
  • 原文地址:https://blog.csdn.net/weixin_43923463/article/details/126128901