Scala(四)类型参数

  1. 类型参数:在Scala中类型参数就类似于Java里的泛型。特指一个集合所存储的对象类型。

    1. 泛型类:指的是在类的声明中,定义一些泛型,在类中的字段或者方法都可以使用这些泛型来规范这些字段或者方法的类型。使用方法就是在创建对象的时候将泛型类替换成指定的类型即可。

      package com.lyz.scala.four
      
      object TLearn {
        def main(args: Array[String]): Unit = {
          val student = new Student[Int](1234567) //id is 0phone is 1234567 phone type is class java.lang.Integer
          val teach = new Teach[String]("1234567") //id is phone is 1234567 phone type is class java.lang.String
      
        }
      }
      
      //声明一个有泛型的抽象类
      abstract class Person[T] {
        var id: T
        def getId: T
        def setId(id: T)
      }
      
      //继承Person,指定的泛型是Int,那么他实现Persion里的方法或者是字段他就会自动推断出是Int类型
      class Student[T](phone: T) extends Person[Int] {
        var id: Int = 0
        def getId: Int = id
        println("id is " + id + "phone is " + phone + " phone type is " + phone.getClass)
        def setId(id: Int): Unit = {
          this.id = id
        }
      }
      
      //继承Person,指定的泛型是Int,那么他实现Persion里的方法或者是字段他就会自动推断出是Int类型
      class Teach[T](phone: T) extends Person[String] {
        var id: String = ""
        def getId: String = id
        println("id is " + id + "phone is " + phone + " phone type is " + phone.getClass)
        def setId(id: String): Unit = this.id = id
      }
    2. 函数泛型:泛型类类似,在函数声明的时候可以在函数名后边指定泛型,然后在函数体内,多个变量或者返回值就可以利用该泛型来强制限定类型。

      package com.lyz.scala.four
      
      object FunctionTLearn {
        def main(args: Array[String]): Unit = {
          println(hello[Int](111111)) //传入的类型是Int类型
          println(hello[String]("111111"))//传入的类型是String类型
          println(hello[Double](1.1))//class java.lang.Double
        }
      
        /**
          * 
          * @param phone :利用泛型来接受传入的参数
          * @tparam T :泛型
          * @return
          */
        def hello[T](phone: T): Any = {
          phone match {
            case _: String =>
              "传入的类型是String类型"
            case _: Int =>
              "传入的类型是Int类型"
            case _ =>
              phone.getClass
          }
        }
      }
      
    3. 泛型的上边界Bounds:Scala中在指定泛型时候,我们可以对泛型的范围加以界定,而不可以是任意类型,比如要求泛型是某个类的子类,或者时候某个类的父类。

      package com.lyz.scala.four
      object BoundsLearn {
        def main(args: Array[String]): Unit = {
          val person001 = new Hello[Person001](new Student002("l4"), new Student001("w5"))
          person001.hello() //打印结果为:my name is l4, hi my name is w5
        }
      }
      
      /**
        * 1.如果Person001的主构造函数不带有var或者val,那么这俩个属性就是private[this] val 私有的
        * 2.所以不允许外部对象访问该属性,因为hello的参数Persion是外部传来的属性,所以p.name会报错
        */
      class Person001(val name: String) {
        def hello(p: Person001): Unit = {
          println("my name is " + name + ", hi my name is " + p.name)
        }
      }
      
      class Student001(name: String) extends Person001(name: String){}
      class Student002(name: String) extends Person001(name: String){}
      
      /**
        * 1.Hello[T <: Person001] 这种语法就是对泛型类进行限定,限定为Person001的子类
        * 2.如果不加上该语法,那么 "p1.hello(p2)" 会编译不通过,因为Scala不知道 p1根p2的具体类型
        * 3.泛型是Person001,如果p1和p2是Person001的子类,那么子类是访问不到他们自己的方法的,如p1和p2是访问不到他们的 hi方法得的
        */
      class Hello[T <: Person001](p1: T, p2: T) {
        def hello() {
          p1.hello(p2)
        }
      }
    4. 泛型下边界Bounds:下边界跟上边界正好相反,下边界对泛型的界定为其本身或者其父类类型。

      package com.lyz.scala.four
      
      object BoundsLearn001 {
        def main(args: Array[String]): Unit = {
          hello(new Student0011("l4")) //传进来的是泛型类型的本身类型:class com.lyz.scala.four.Student0011
          hello(new Person0011("l4")) //传进来的是泛型类型的父类型: class com.lyz.scala.four.Person0011
          hello(Array)//传进来不符合下边界规则
        }
      
        /**
          * 1.下边界:语法  def hello[T >: Student0011](p: T): Unit = {}
          * 2.下边界界定的泛型为类型本身或者其父类型
          * 3.这个函数的泛型作用就是限定了传来的参数只能Student001或者Student001的父类
          * 4.为什么p这个引用访问不到本身或者父类的方法呢,因为这个是下边界,scala是不知道传进来的是父类还是子类
          */
        def hello[T >: Student0011](p: T): Unit = {
          p match {
            case p: Student0011 => println("传进来的是泛型类型的本身类型:" + p.getClass)
            case p: Person0011 => println("传进来的是泛型类型的父类型: " + p.getClass)
            case _ => println("传进来不符合下边界规则")
          }
        }
      }
      
      /**
        * 1.如果Person001的主构造函数不带有var或者val,那么这俩个属性就是private[this] val 私有的
        * 2.所以不允许外部对象访问该属性,因为hello的参数Persion是外部传来的属性,所以p.name会报错
        */
      class Person0011(val name: String) {
        def hello(): Unit = {
          println("我是父类")
        }
      }
      
      class Student0011(name: String) extends Person0011(name: String) {
        def hi(): Unit = {
          println("我是子类")
        }
      }
    5. View Bounds:上下边界的高级版,可以将不再上下边界的类型融入到上下边界的泛型中,利用隐式转换的方法。

      package com.lyz.scala.four
      
      object BoundsLearn0012 {
        def main(args: Array[String]): Unit = {
      
          val hello0011 = new Hello0012[Person0012](new Student0012("l4"), new Student0013("w5"))
          hello0011.hello() //打印结果为:my name is l4, hi my name is w5
      
          val hello0012 = new Hello0012[Person0012](new Dog("dog"), new Student0013("w5"))
          hello0012.hello() //打印结果为:my name is dog, hi my name is w5
        }
      
        //Viwe Bounds将不在上下边界的类型加入到上线边界中,需要利用隐式转换,将其他类型转换成我们预期的类型
        implicit def dog2person(obj: Object): Person0012 = {
          obj match {
            case obj: Dog =>
              val dog = obj.asInstanceOf[Dog]
              new Person0012(dog.name)
            case _ => null
          }
        }
      }
      
      /**
        * 1.如果Person001的主构造函数不带有var或者val,那么这俩个属性就是private[this] val 私有的
        * 2.所以不允许外部对象访问该属性,因为hello的参数Persion是外部传来的属性,所以p.name会报错
        */
      class Person0012(val name: String) {
        def hello(p: Person0012): Unit = {
          println("my name is " + name + ", hi my name is " + p.name)
        }
      }
      
      class Student0012(name: String) extends Person0012(name: String) {
        def hi(): Unit = {
          println("我是子类")
        }
      }
      
      class Student0013(name: String) extends Person0012(name: String) {
        def hi(): Unit = {
          println("我是子类")
        }
      }
      
      //Dog类不在上下边界的范围之内,如果我们需要将它假如到上线边界就需要利用到了隐式转换
      class Dog(val name: String) {
        def hi(): Unit = {
          println("wang wang ,my name is " + name)
        }
      }
      
      /**
        * 1.Hello[T <% Person001] 这种语法就是View Bounds 
        * 2.如果不加上该语法,那么我传入不再边界之内的类型,编译器会报错
        * 3.(implicit dog: T => Person0012) 这是View Bounds新语法
        */
      class Hello0012[T](p1: T, p2: T)(implicit dog: T => Person0012) {
        def hello() {
          p1.hello(p2)
        }
      }
    6. Manifest Bounds:用于初始化数组类型的泛型类型

          ```
          package com.lyz.scala.four
      
          object ManifestContextBounds {
            def main(args: Array[String]): Unit = {
              val initArrInt = initAarry001(1, 2, 3, 4, 5, "aaaa")
              val initArrObj = initAarry001(new Person0014("l4"), new Animole0014("w5"))
            }
      
            /**
              * [T: Manifest] :用于初始化Array数组的泛型类型。
              *
              */
            def initAarry001[T: Manifest](array: T*): Array[T] = {
              val arr = new Array[T](array.length)
      
              //这里一定用 until 否则会下标越界,因为如果长度为10,array(10)是不存在的
              for (i <- 0 until array.length) {
                arr(i) = array(i)
              }
              arr
            }
          }
          class Person0014(name: String)
          class Animole0014(name: String)
          ```
      
      
       2. 协变:在Scala的泛型中有这样一种泛型类型为 `C[+T]` 这种语法的意思就是如果`A`是`B`的子类,那么`C[A]`也就成了`C[B]`的子类。
      
      package com.lyz.scala.four
      
      object CovariationLearn {
        def main(args: Array[String]): Unit = {
      
          //教的课程中的数学课
      
          val teacher = new Course[Teach0101]("数学")
          val professore = new Course[Professor0101]("数学")
      
          val teaching = new Teaching
      
          //能够编译通过,因为 teaching方法里的参数泛型的类型就是Teach0101
          teaching.teaching(teacher) //打印结果为:普通教学,教授和老师都可以授课
          //编译通过,因为teaching方法里的参数泛型的类型是Teach0101,而Teach0101又是Professor0101的父类,所以Course[Teach0101]也是Course[Professor0101]的父类
          teaching.teaching(professore) //打印结果为:普通教学,教授和老师都可以授课
      
          //编译通过:因为teachingDifficult方法的参数泛型的类型就是Professor0101
          teaching.teachingDifficult(professore) //打印结果为:高级教学只能教授授课,普通老师不能授课
      
          /**
            * 编译不通过:
            *       1.因为teachingDifficult方法里参数泛型的类型是Professor0101,Professor0101又是Teach0101的子类
            *       2.所以Course[Professor0101]也是Course[Teach0101]的子类,子类引用指向了父类,所以编译不通过
            *
            */
          teaching.teachingDifficult(teacher) //编译报错直接飘红
        }
      }
      
      /**
        * 教学课程 协变泛型,来指定老师跟教授能够教的课程的不同
        *
        * 1.协变:语法为 [+T]
        * 2.
        *
        */
      class Course[+T](arg: T) {
      
        /**
          * 编译不通过,异常为:Covariant type T occurs in contravariant position in type T of value arg1
          * 异常解释:协变类型T出现在逆变的位置
          * 原因:当T被定义成协变之后,就不能应用于成员方法中了,
          * 解决方法:也为方法泛型化,因为是协变的,输入的类型必须是T的父类
          * 为什么需要传入的T的父类,因为这个函数的泛型变成了逆变,又因为逆变跟协变是相反的,如果我们传入的是T的子类
          * 那么根据逆变的原理,我们反而传入了T的父类,这子类引用指向了父类,编译器会报错。如果我们传入的是父类,那么根据逆变的原理
          * 反而就会成了T的子类,父类引用指向了子类,没毛病。
          */
        def initTeaching[U >:T](arg1:U): U = {
             new U
        }
      }
      
      class Teaching {
        //普通教学,教授和老师都可以授课
        def teaching(p: Course[Teach0101]): Unit = {
          println("普通教学,教授和老师都可以授课")
        }
      
        //高级教学只能教授授课,普通老师不能授课
        def teachingDifficult(p: Course[Professor0101]): Unit = {
          println("高级教学只能教授授课,普通老师不能授课")
        }
      }
      
      //教授
      class Teach0101
      
      //老师
      class Professor0101 extends Teach0101
      
      
    7. 逆变:跟协变正好是相反的

猜你喜欢

转载自blog.csdn.net/suubyy/article/details/80688312
今日推荐