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. 实现自己的 toStringhashCodecopyequals 方法
  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  耳_东  阅读(102)  评论(0)    收藏  举报