Fork me on GitHub

Scala Day03


模式匹配

Scala 有一个十分强大的模式匹配机制,可以应用到很多场合:如 switch 语句、类型检查等。
并且 Scala 提供了样例类,对模式匹配进行了优化,可以快速进行匹配

匹配字符串

package com.scala.day03

import java.util.Random

/**
  * 刚才所做的所有的操作都是为了  使用和体验  这个scala的语法
  */
object MatchTest {

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

        val list = List("a", "b", "c", "d")
        var r = new Random()
        // index值就是list的下标:可能为  0, 1, 2, 3
        val index = r.nextInt(list.length)
        // value就是从list中随机取到的一个值
        val value = list(index)
        println(value)

        var result = value match {
            // 如果value的值为"a", 执行{}中的代码
            case "a" => {
                println("a")
                "a"
            }
            case "b" => {}

            case "c" => {
                println("匹配到字符串C")
                1
            }
            // "_" 表示匹配任意的单个值;
            case _ => {
                println("else")
            }
        }
        println(result)
    }
}

匹配类型

package com.scala.day03

import java.util.Random

class Student

object MatchTest2 {

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


        val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 2f, 3D, true, "a", new Student())
        var r = new Random()
        // index值就是list的下标:可能为  0, 1, 2, 3
        val index = r.nextInt(list.length)
        // value就是从list中随机取到的一个值
        val value = list(index)
        println("value: \t" + value)


        // 根据类型在匹配
        // if x > 8  :  守卫条件
        val result = value match {
            // 表示匹配 大于8 的整数
            case x: Int if x > 8 => println(" > 8", x)
            case x: Int => println(" <= 8", x)
            case x: Float => println(2, x)
            case x: Double => println(3, x)
            case x: Boolean => println(true, x)
            case x: String => println("s", x)
            // 类似java中 switch case中的default
            case _ => println("else")
        }

    }
}

注意:case y: Double if(y >= 0) => ...
模式匹配的时候还可以添加守卫条件。如不符合守卫条件,将掉入 case _中

匹配数组、元组、集合

package com.scala.day03

import scala.collection.immutable.HashSet

object MatchTest3 {
    def main(args: Array[String]): Unit = {
        val list = List(1, 2, 3, 4, 5, 6, "abcd")
        //    val set = HashSet(1,2,3,4,5,6,"abcd")
        val value = list
        value match {
            case 2 :: 3 :: Nil => {
                println("List(2,3)")
            }
            case List(1, 2, 3, _, _, _, x) => println(x)
            case 1 :: 2 :: x => {
                println(x)
            }
            case _ => println("else")
        }
    }
}

注意:在 Scala 中列表要么为空(Nil 表示空列表)要么是一个 head 元素加上一个 tail 列表。
9 :: List(5, 2) :: 操作符是将给定的头和尾创建一个新的列表
注意::: 操作符是右结合的,如 9 :: 5 :: 2 :: Nil 相当于 9 :: (5 :: (2 :: Nil))

样例类

样例类 是一种特殊的类,可用于模式匹配
case class 是多例的,后面要更构造参数;case object是单例的

当一个类被声名为 case class 的时候,scala 会帮助我们做下面几件事情:

  1. 构造器中的参数如果不被声明为 var 的话,它默认是 val 类型的,但一般不推荐将构造器中的参数声明为 var;
  2. 自动创建伴生对象,同时在里面给我们实现子 apply 方法,使得我们在使用的时候可以不直接显示地 new 对象
  3. 伴生对象中同样会帮我们实现 unapply 方法,从而可以将 case class 应用于模式匹配apply方法接受参数返回对象,unapply 方法接收对象返回参数
  4. 实现自己的 toString、hashCode、copy、equals 方法
  5. case class 主构造函数里面没有修饰符,默认的是 val

除此之此,case class 与其它普通的 scala 类没有区别

创建样例类

case class Student(id:Int,name:String)

创建的对象,默认就是val类型,并且是private修饰的
因此成员变量就需要用var修饰

创建样例对象

case object Student

样例对象是不能带有参数的

示例


```scala
package com.scala.day03

import java.util.Random


object MatchTest4 {

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


    val list: List[Any] = List(
      Student4444(1, "a"),
      Student5555(2, "b"),
      Student6666
    )

    var r = new Random()
    // index值就是list的下标:可能为  0, 1, 2, 3
    val index = r.nextInt(list.length)
    // value就是从list中随机取到的一个值
    val value = list(index)
    println("value: \t" + value)


    value match {

      case Student4444(id, name) => {
        println("student4444")
        println(id, name)
      }

      case Student5555(id, name) => {
        println("student55555")
        println(id, name)
      }

      case Student6666 => {
        println("case object")
      }
    }

  }
}


/**
  * 这个case class 就是样例类, 作用,就是用来做模式匹配的
  *
  * 这个 case object的作用也类似
  */
case class Student4444(id: Int, name: String)

case class Student5555(id: Int, name: String)

case object Student6666

Option类型

在 Scala 中 Option 类型样例类用来表示可能存在或也可能不存在的值(Option 的子类有 Some和 None)。Some 包装了某个值,None 表示没有值

package com.scala.day03

object MapOptionTest {

  val map = Map(("a", 1), ("b", 2), ("c", 3))

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


    var key: String = "d"

    //    println(map(key))
    //    println(map.get(key))
    //    println(map.getOrElse(key, "default"))

//    println(find(map, key))
//    println(getOrElse(map, key))


    println(getOrElse2(key))

  }

  def find(map: Map[String, Int], key: String): Int = {

    // Some(x)   None
    map.get(key)

    return map.getOrElse(key, 0)

  }


  // 定义的一个非常普通的方法
  def getOrElse(map: Map[String, Int], key: String): Int = {
    // Some(x)   Option中代表值存在的一个 子类
    // None      Option中代表值不存在的一个 子类
    val result: Option[Int] = map.get(key)
    val lastResult = result match {
      case Some(x) => x
      case None => 0
    }
    lastResult
  }

  // 利用模式匹配实现的一个方法
  def getOrElse1(key: String) = {
    map.get(key) match {
      case Some(x) => x
      case None => 0
    }
  }

  // 模式匹配: 偏函数
  // String, Int
  //  String: 输入的参数类型
  //  Int : 输出的类型

  // 没有match关键字的一堆 case分支 组成的一个代码块就是一个偏函数
  def getOrElse2: PartialFunction[String, Int] = {
    case "a" => 1
    case "b" => 2
    case "c" => 3
    case _ => 0
  }


  // 偏函数:   就是为了做模式匹配
}

偏函数

被包在花括号内没有 match 的一组 case 语句是一个偏函数,它是 PartialFunction[A, B]的一个实例,A 代表参数类型,B 代表返回值类型,常用作输入模式匹配

package day2.oop

object PartialFuncDemo {
    //偏函数
    def func1: PartialFunction[String, Int] = {
        case "one" => 1
        case "two" => 2
        case _ => -1
    }
    //普通函数的模式匹配实现
    def func2(num: String) : Int = num match {
        case "one" => 1
        case "two" => 2
        case _ => -1
    }
    def main(args: Array[String]) {
        println(func1("one"))
        println(func2("one"))
    }
}

高阶函数 和 闭包

因为方法和函数虽然定义不同,但是作用一样,也切也可以相互转化
所有以后统统都叫函数;

定义函数

有名函数

scala> val add =(x:Int,y:Int) =>{x+y}
add: (Int, Int) => Int = <function2>

scala> val array = List(1,2,3,4,4,5,6,7)
array: List[Int] = List(1, 2, 3, 4, 4, 5, 6, 7)

scala> array.reduce(add)
res6: Int = 32

匿名函数

scala> val array = List(1,2,3,4,4,5,6,7)
array: List[Int] = List(1, 2, 3, 4, 4, 5, 6, 7)

scala> array.reduce((x:Int,y:Int)=>{x+y})
res7: Int = 32

其他用法

scala> val array = List(1,2,3,4,4,5,6,7)
array: List[Int] = List(1, 2, 3, 4, 4, 5, 6, 7)

scala> array.map((x:Int)=>x+2)
res8: List[Int] = List(3, 4, 5, 6, 6, 7, 8, 9)

scala> array.map(x=>x+2)
res9: List[Int] = List(3, 4, 5, 6, 6, 7, 8, 9)

scala> array.map(_+2)
res10: List[Int] = List(3, 4, 5, 6, 6, 7, 8, 9)

scala> array.reduce(_+_)
res11: Int = 32

高阶函数

高阶函数:一个函数可以作为另外一个函数的参数或者返回值

  1. 函数作为参数
  2. 函数作为返回值

函数的返回值是函数

scala> def add(x:Int) = {(y:Int)=>x+y}
add: (x: Int)Int => Int

scala> val add2 = add(2)
add2: Int => Int = <function1>

scala> add2(4)
res12: Int = 6

scala>

函数的参数为另外一个函数

scala> def opt(f:(Int,Int)=>Int) = f(3,4)
opt: (f: (Int, Int) => Int)Int

scala> val add = (x:Int,y:Int) => x+y
add: (Int, Int) => Int = <function2>

scala> val max =(x:Int,y:Int)=>if(x>y)x else y
max: (Int, Int) => Int = <function2>

scala> opt(add)
res13: Int = 7

scala> opt(max)
res14: Int = 4

柯里化 Curry

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果是一个新函数的技术。

柯里化函数与 高阶函数中返回值是函数的类型很像;
但是它必须同时将所有参数传递完;

package com.scala.day3

object CurryTest {
    def multiply(x:Int)(y:Int) = x * y
    //柯里化就是把参数可以分开来,把部分函数参数可以用下划线来代替
    def multiply2 = multiply(2)_
    // 一个普通的方法,接受两个 Int 类型参数做乘积
    def multiply3(x:Int, y:Int) = x * y
    def multiply4(x:Int, y:Int=10) = x * y
    def multiply5(x:Int)(y:Int=10) = x * y
    def main(args: Array[String]):Unit = {
        println(multiply(2)(4))
        println(multiply2(4))
        // 跟柯里化的函数在结果上没有区别,那到底有什么区别呢?
        println(multiply3(2,4))
        println(multiply4(4))
        println(multiply5(4)())
    }
}

可以使用_ 代替第二个参数;从而变成高阶函数的样式

科里化的默认参数,可以隐式参数,也可以是默认参数;

// 默认参数
scala> def add1(x:Int)(y:Int=10) = x*y
add1: (x: Int)(y: Int)Int

scala> val result = add1(5)()
result: Int = 50

// 隐式参数
scala> def add1(x:Int)(implicit y:Int=10) = x*y
add1: (x: Int)(implicit y: Int)Int

scala> val result = add1(5)
result: Int = 50

默认参数

也就是在定义函数的时候,直接针对参数进行赋值;
但是柯里化函数的默认参数,必须是隐式的;

隐式参数

三个要点:

  1. 在一个代码运行环境中,相同类型的隐式变量只能存在一个
  2. 如果存在多个,那么请定义在一个自定义的object单例对象;必须是单例对象;
  3. 使用的时候,需要使用哪一个;就使用import命令添加即可

工作机制:

  1. 默认,在方法的定义上,是有一个隐式参数默认值的
  2. 当环境中,引入了其他的相同类型的隐式变量的话,那么,首先从全局中,从系统中,寻找相同类型的隐式变量值,如果没有找到,则使用方法定义的默认的参数值;

隐式变量的工作,是按照类型匹配,并且一个代码运行环境汇总,不能出现二义性(不能同时存在多个相同类型的隐式变量)

_的应用场景

  1. 手动转换方法为函数
  2. 元组中,用来指定下标访问对应位置上的值
  3. 匹配模式中,表示其他任意情况 case _
  4. 变量的初始值:var a:Int = _;var s:String=_
  5. map函数和reduce函数等系统函数中;可以使用_代表某个元素,但是这个元素在具体的逻辑中只能够出现一次;

闭包

本质是可以访问一个函数里面局部变量的另外一个函数

package com.aura.scala.day03

object BibaoTest {

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

    // 闭包就是用来解决这个问题: 在一个函数的外部要访问这个函数内部的一个局部变量

    def f1(x:Int):Int = {
      var abc = 100
      var sum:Int = 100
      sum += x
      abc += sum
      sum
    }
    println(f1(100))
  }
}


隐式转换

需要注意的是:

  1. 隐式转换只能定义在object中;
  2. 注意隐式转换的二义性

忠告:尽量不要使用隐式转换

应用场景

  1. 当调用某个对象不存在的方法的时候,会触发这个对象进行隐式转换

    package com.scala.day03
    
    import java.io.File
    
    import scala.io.Source
    
    object FileUtil {
    
        def main(args: Array[String]): Unit = {
            // 例如 Int类型 没有 to方法一样
            // scala在处理的时候是将 Int转换为 RichInt类型了
            // 可以通过 :implicits -v 查看
            1.to(10)
    
    
            // 获取一个File对象
            val file = new File("c:/words.txt")
    
            // 读取这个File对象代表的那个文件的全部内容
    
            // 如果想让这个方法执行成功,那么必须利用scala的隐式转换的语法,让file对象转换成另外一个具备
            // readAllContentXXXXXXXXXXXXXX这个方法的一个实例对象,即可!
    
            // 二义性: 此处不能导入MyImplicit123下的所有隐式转换;只能导入某一个隐式转换;
    
            import MyImplicit123.xxxxxxxx1
            val allContent:String = file.readAllContentXXXXXXXXXXXXXX()
    
            println(allContent)
    
        }
    }
    
    
    // 这个隐式转换的定义,就是为了让 File 类型的对象,可以被转换为 RichFile 类型的对象
    object MyImplicit123{
      implicit def xxxxxxxx1(file:File):RichFile1 = new RichFile1(file)
      implicit def xxxxxxxx2(file:File):RichFile2 = new RichFile2(file)
    }
    
    // 这个组件 RichFile的作用,就是定义一个具备readAllContentXXXXXXXXXXXXXX
    // 这个方法的类
    class RichFile1(val file:File){
      def readAllContentXXXXXXXXXXXXXX(): String ={
        Source.fromFile(file).mkString
      }
    }
    
    class RichFile2(val file:File){
      def readAllContentXXXXXXXXXXXXXX(): String ={
        Source.fromFile(file).mkString
      }
    }
    
    
    
  2. 当调用某个方法时,传入的参数类型不匹配的时候,会触发参数变量进行隐式转换

    
    // 可以当做 类型转换器来使用;
    package com.scala.day03
    
    /**
      - 人  和  人
      *
      - 人   和  狗
      */
    object MakeFriend {
        def main(args: Array[String]): Unit = {
            val huangbo = new Person("huangbo")
            val xuzheng = new Person("xuzheng")
            val wangcai = new Dog("wangcai")
            huangbo.makeFriend(xuzheng)
            import MyImplicit333.dog2Person
            huangbo.makeFriend(wangcai)
    
            /**
            *
            * 隐式转换有将Int转换为Double的,但是没有将Double转换为Int的;
            * 如果需要,则需要手动定义;
            */
            val add:(Int,Int)=>Int = (x:Int,y:Int)=>x+y
            // 传入的参数是double类型的;此时就需要将double转化为Int类型的
            add(5.0,4.0)
    
        }
    }
    object MyImplicit333 {
        implicit def dog2Person(dog: Dog): Person = {
            new Person(dog.name)
        }
    }
    // huangbo 和  xuzheng 交朋友
    // val huangbo = new Person("huangbo")
    // val xuzheng = new Person("xuzheng")
    // huangbo.makeFriend(xuzheng)
    class Person(val name: String) {
        def makeFriend(p: Person): Unit = {
            println(this.name, p.name)
        }
    }
    class Dog(val name: String) {
    }
    
    
  3. 在存在视图边界的时候

    泛型

scala默认提供的隐式转换


scala> :implicits -v
/* 69 implicit members imported from scala.Predef */
  /* 7 inherited from scala */
  final implicit class ArrayCharSequence extends CharSequence
  final implicit class ArrowAssoc[A] extends AnyVal
  final implicit class Ensuring[A] extends AnyVal
  final implicit class RichException extends AnyVal
  final implicit class SeqCharSequence extends CharSequence
  final implicit class StringFormat[A] extends AnyVal
  final implicit class any2stringadd[A] extends AnyVal

  /* 40 inherited from scala.Predef */
  implicit def ArrowAssoc[A](self: A): ArrowAssoc[A]
  implicit def Ensuring[A](self: A): Ensuring[A]
  implicit def StringFormat[A](self: A): StringFormat[A]
  implicit def any2stringadd[A](self: A): any2stringadd[A]

  implicit def booleanArrayOps(xs: Array[Boolean]): mutable.ArrayOps[Boolean]
  implicit def byteArrayOps(xs: Array[Byte]): mutable.ArrayOps[Byte]
  implicit def charArrayOps(xs: Array[Char]): mutable.ArrayOps[Char]
  implicit def doubleArrayOps(xs: Array[Double]): mutable.ArrayOps[Double]
  implicit def floatArrayOps(xs: Array[Float]): mutable.ArrayOps[Float]
  implicit def genericArrayOps[T](xs: Array[T]): mutable.ArrayOps[T]
  implicit def intArrayOps(xs: Array[Int]): mutable.ArrayOps[Int]
  implicit def longArrayOps(xs: Array[Long]): mutable.ArrayOps[Long]
  implicit def refArrayOps[T <: AnyRef](xs: Array[T]): mutable.ArrayOps[T]
  implicit def shortArrayOps(xs: Array[Short]): mutable.ArrayOps[Short]
  implicit def unitArrayOps(xs: Array[Unit]): mutable.ArrayOps[Unit]

  implicit def $conforms[A]: <:<[A,A]
  implicit def ArrayCharSequence(__arrayOfChars: Array[Char]): ArrayCharSequence
  implicit def Boolean2boolean(x: Boolean): Boolean
  implicit def Byte2byte(x: Byte): Byte
  implicit def Character2char(x: Character): Char
  implicit def Double2double(x: Double): Double
  implicit def Float2float(x: Float): Float
  implicit def Integer2int(x: Integer): Int
  implicit def Long2long(x: Long): Long
  implicit def RichException(self: Throwable): RichException
  implicit def SeqCharSequence(__sequenceOfChars: IndexedSeq[Char]): SeqCharSequence
  implicit def Short2short(x: Short): Short
  implicit val StringCanBuildFrom: generic.CanBuildFrom[String,Char,String]
  implicit def augmentString(x: String): immutable.StringOps
  implicit def boolean2Boolean(x: Boolean): Boolean
  implicit def byte2Byte(x: Byte): Byte
  implicit def char2Character(x: Char): Character
  implicit def double2Double(x: Double): Double
  implicit def float2Float(x: Float): Float
  implicit def int2Integer(x: Int): Integer
  implicit def long2Long(x: Long): Long
  implicit def short2Short(x: Short): Short
  implicit def tuple2ToZippedOps[T1, T2](x: (T1, T2)): runtime.Tuple2Zipped.Ops[T1,T2]
  implicit def tuple3ToZippedOps[T1, T2, T3](x: (T1, T2, T3)): runtime.Tuple3Zipped.Ops[T1,T2,T3]
  implicit def unaugmentString(x: immutable.StringOps): String

  /* 22 inherited from scala.LowPriorityImplicits */
  implicit def genericWrapArray[T](xs: Array[T]): mutable.WrappedArray[T]

  implicit def wrapBooleanArray(xs: Array[Boolean]): mutable.WrappedArray[Boolean]
  implicit def wrapByteArray(xs: Array[Byte]): mutable.WrappedArray[Byte]

  implicit def wrapCharArray(xs: Array[Char]): mutable.WrappedArray[Char]

  implicit def wrapDoubleArray(xs: Array[Double]): mutable.WrappedArray[Double]
  implicit def wrapFloatArray(xs: Array[Float]): mutable.WrappedArray[Float]
  implicit def wrapIntArray(xs: Array[Int]): mutable.WrappedArray[Int]
  implicit def wrapLongArray(xs: Array[Long]): mutable.WrappedArray[Long]

  implicit def wrapRefArray[T <: AnyRef](xs: Array[T]): mutable.WrappedArray[T]
  implicit def wrapShortArray(xs: Array[Short]): mutable.WrappedArray[Short]
  implicit def wrapUnitArray(xs: Array[Unit]): mutable.WrappedArray[Unit]


  implicit def booleanWrapper(x: Boolean): runtime.RichBoolean
  implicit def byteWrapper(x: Byte): runtime.RichByte
  implicit def charWrapper(c: Char): runtime.RichChar
  implicit def doubleWrapper(x: Double): runtime.RichDouble
  implicit def fallbackStringCanBuildFrom[T]: generic.CanBuildFrom[String,T,immutable.IndexedSeq[T]]
  implicit def floatWrapper(x: Float): runtime.RichFloat
  implicit def intWrapper(x: Int): runtime.RichInt
  implicit def longWrapper(x: Long): runtime.RichLong
  implicit def shortWrapper(x: Short): runtime.RichShort
  implicit def unwrapString(ws: immutable.WrappedString): String
  implicit def wrapString(s: String): immutable.WrappedString

异常处理

与java一样,写法不同


try{
    add(5.0,4.0)
}case e:IndexOutOfBoundsException{

}case e:ArrayIndexOutOfBoundsException{

}

泛型

泛型的作用:在编译期进行参数检查;防止在运行时出错;

泛型类:指定类可以接受任意类型参数。
泛型方法:指定方法可以接受任意类型参数。

scala类型变量界定

下界: T >: S 类似于java的 extends
上界: T <: S super
类型变量界定就是在泛型的基础上,对泛型的范围进一步的界定;从而缩小泛型的具体范围

package com.scala.day03

object XiaJieTest {

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

//    ceshi[Son](new Son())
    ceshi[Father](new Father())
  }

  def ceshi[T >: Me](person: T): Unit ={

    println("aaaaaa")
  }
}


class GrandFather
class Father extends GrandFather
class Me extends Father
class Son extends Me
class Tongzhuo

scala视图界定

如果希望跨越类继承层次结构时,可以使用视图界定来实现的,其后面的原理是通过隐式转换来实现

视图界定利用<%符号来实现


package com.mazh.scala.day3
case class Student11[T,S <: Comparable[S]](var name:T,var height:S)
/**
 * 作者: 马中华:http://blog.csdn.net/zhongqi2513
 */
object GenericTypeTest3{
 def main(args: Array[String]): Unit = {
 // 这是合法的语句
 val s= Student11("john","170")
 //下面这条语句不合法,这是因为,Int 类型没有实现 Comparable 接口
 val s2= Student11("john",170)
 }
}

修改以后的代码

package com.mazh.scala.day3
case class Student11[T,S <% Comparable[S]](var name:T,var height:S)
/**
 * 作者: 马中华:http://blog.csdn.net/zhongqi2513
 */
object GenericTypeTest3{
 def main(args: Array[String]): Unit = {
 // 这是合法的语句
 val s= Student11("john","170")
 // Int 类型的变量经过隐式转换成了 RichInt 类型,RichInt 类型是实现了 Comparable 接口的
 val s2= Student11("john",170)
 }
}

分析:
利用<%符号对泛型 S 进行限定,它的意思是 S 可以是 Comparable 类继承层次结构中实现了Comparable 接口的类,也可以是能够经过隐式转换得到的实现了 Comparable 接口的类。

上面改动的语句在视图界定中是合法的,因为 Int 类型此时会隐式转换为 RichInt 类,而RichInt 类属于 Comparable 继承层次结构。Int 类会隐式转换成 RichInt 类,RichInt 并不是直
接实现 Comparable 口,而是通过 ScalaNumberProxy 类将 Comparable 中的方法继承过来。

scala的协变和逆变

协变

当类型 B 是类型 A 的子类型时,则 List[B]也可以认为是 List[A]的子类型,即 List[B]可以泛化为 List[A]

package com.aura.scala.day03

object XieBianTest {

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

    var o1 = new SSS[String]("aa")
    var o2 = new SSS[Int](1)

//    o2 = o1



    var o3 = new SSS[AA](new AA())
    var o4 = new SSS[BB](new BB())


    var a = new AA()
    var b = new BB()

    a = b
   // b = a

    // StudentService ss = new StudentServiceImpl()
    // StudentService ss = new StudentServiceImpl1()
    // StudentService ss = new StudentServiceImpl2()


   o3 = o4
//    o4 = o3


    // o3 的类型 一定要   >=   o4 的类型


  }
}

/**
  *   +  协变
  *   -  逆变
  */
class SSS[+T](val id:T){}


class AA
class BB extends AA

逆变

当类型 B 是类型 A 的子类型时,则 List[A]也可以认为是 List[B]的子类型,即 List[A]可以泛化为 List[B]

posted @ 2020-01-24 10:13  耳_东  阅读(108)  评论(0)    收藏  举报