Scala 泛型

一、泛型

Scala 和 Java 一样,类、特质、方法都支持泛型。泛型就是定义类、方法时不用指定具体数据类型,在使用时才传入具体适用类型,这样的好处就是支持多类型的复用。泛型的常见用法有以下几种:

  • 泛型方法
  • 泛型类
  • 泛型特质

1.1 泛型方法

泛型方法指把泛型定义到方法声明上,即该方法的参数类型由泛型来决定,在调用该方法时再传入明确的数据类型。

语法格式:

Scala 复制代码
def 方法名[泛型名称](..) = {
    //...
}

例如:

Scala 复制代码
object Test {
  def getMiddleElement[T](array:Array[T]) = array(array.length / 2)
 
  def main(args: Array[String]): Unit ={
    println(getMiddleElement(Array(1, 2, 3, 4, 5))) //3
    println(getMiddleElement(Array(1, 2, 3, 4, 5,6))) //4
    println(getMiddleElement(Array("a", "b", "c", "d", "e"))) //c
  }
}

1.2 泛型类

语法格式:

Scala 复制代码
class 类[T](val 变量名: T)

例如:

  • 实现一个 Pari 泛型样例类;
  • Pari 类包含两个字段,而且两个字段的类型不固定;
  • 创建不同类型泛型类对象;
Scala 复制代码
case class Pair[T](var a:T, var b:T)
 
def main(args: Array[String]): Unit = {
    val pairList = List(
        Pair("Hadoop", "Storm"), 
        Pair("Hadoop", 2008),
        Pair(1.0, 2.0),
        Pair("Hadoop", Some(1.9))
    )
 
    println(pairList)
}
//List(Pair(Hadoop,Storm), Pair(Hadoop,2008), Pair(1.0,2.0), Pair(Hadoop,Some(1.9)))

1.3 泛型特质

泛型特质指把泛型定义到特质的声明上,即该特质中的成员的参数类型是由泛型来决定的。在定义泛型特质的子类或子单例对象时,明确具体的数据类型。

语法格式:

Scala 复制代码
trait 特质A[T] {
	//特质中的成员
}

class 类B extends 特质A[指定具体的数据类型] {
	//类中的成员
}

例如:

  • 定义一个泛型特质 Logger,该特质有一个变量 a 和 show() 方法;
  • 定义单例对象,继承 Logger 特质;
  • 打印单例对象中的成员;
Scala 复制代码
object Test {
	
	trait Logger[T] {
		val a:T
		def show(b:T)
	}

	
	object ConsoleLogger extends Logger[String]{
		override val a:String = "lee"
		override def show(b:String):Unit = println(b)
	}

	def main(args:Array[String]):Unit = {
		println(ConsoleLogger.a)
		COnsoleLogger.show("abc")
	}
}
//lee
//abc

二、泛型的上下界

在定义方法 / 类的泛型时,限定必须从哪个类继承、或者必须是哪个类的父类。此时就需要使用到上下界。

2.1 上界

使用 <: 类型名表示给类型添加一个上界,表示泛型参数必须要从该类(或本身)继承。

Scala 复制代码
[T <: 类型]

例子:

  • 定义一个 Person 类;
  • 定义一个 Student 类,继承 Person 类;
  • 定义一个 demo 泛型方法,该方法接收一个 Array 参数;
  • 限定 demo 方法的 Array 元素类型只能是 Person 或者 Person 的子类;
  • 测试调用 demo,传入不同元素类型的 Array;
Scala 复制代码
class Person
class Student extends Person
 
def demo[T <: Person](a:Array[T]) = println(a)
 
def main(args: Array[String]): Unit = {
    demo(Array(new Person))
    demo(Array(new Student))
    // 编译出错,必须是Person的子类
    // demo(Array("hadoop"))
}

2.2 下界

上界是要求必须是某个类的子类,而下界则是必须是某个类的父类(或本身)。

Scala 复制代码
[T >: 类型]

注意:如果类既有上界又有下界。下界写在前面,上界写在后面,如下:

Scala 复制代码
def demo[T >: Policeman <: Person ](array:Array[T]) = println(array)

例如:

  • 定义一个 Person 类;
  • 定义一个Policeman类,继承Person类
  • 定义一个Superman类,继承Policeman类
  • 定义一个demo泛型方法,该方法接收一个Array参数,
  • 限定demo方法的Array元素类型只能是Person、Policeman
  • 测试调用demo,传入不同元素类型的Array
Scala 复制代码
package com.robot.scalademo
 
class Person
class Policeman extends Person
class Superman extends Policeman
 
object Demo {
 
  def demo[T >: Policeman](array:Array[T]) = println(array)
 
  def main(args: Array[String]): Unit = {
    demo(Array(new Person))
    demo(Array(new Policeman))
    // 编译出错:Superman是Policeman的子类
    // demo(Array(new Superman))
  }
}
/**
    demo(Array(new Person))
    demo(Array(new Policeman))
    // 编译出错:Superman是Policeman的
  * */
 
---------------------------------------------------------------------
//泛型既有上界也有下界
package com.robot.scalademo
class Human
class Person  extends  Human
class Policeman extends Person
class Superman extends Policeman
 
object Demo {
 
  def demo[T >: Policeman <: Person ](array:Array[T]) = println(array)
 
  def main(args: Array[String]): Unit = {
    demo(Array(new Person))
    demo(Array(new Policeman))
    // 编译出错:Superman是Policeman的子类
    // demo(Array(new Superman))
    // 编译出错:Human是Person的父类
    //demo(Array(new Human))
  }
}
/**
    demo(Array(new Person))
    demo(Array(new Policeman))
    // 编译出错:Superman是Policeman的
  * */

三、非变、协变、逆变

  • 非变:A 类和 B 类是父子类关系,但是 Pair[A] 和 Pair[B] 之间没有任何关系;
  • 协变:A 类和 B 类是父子类关系,Pair[A] 和 Pair[B] 之间也有父子类关系;
  • 逆变:A 类和 B 类是父子类关系,但是 Pair[A] 和 Pair[B] 之间是子父类关系;

如下图:

3.1 非变

语法格式:

Scala 复制代码
class Pair[T]{}
  • 默认泛型类都是非变的;
  • 类型 B 是 A 的子类型,Pair[A] 和 Pair[B] 没有任何从属关系;
  • Java 中是一样的;

3.2 协变

语法格式:

Scala 复制代码
class Pair[+T]
  • 类型 B 是 A 的子类型,Pair[B] 可以认为是 Pair[A] 的子类型;
  • 参数化类型的方向和类型的方向是一致的;

3.3 逆变

语法格式:

Scala 复制代码
class Pair[-T]
  • 类型 B 是 A 的子类型,Pair[A] 反过来可以认为是 Pair[B] 的子类型;
  • 参数化类型的方向和类型的方向是相反的;

3.4 代码示例

  • 定义一个 Super 类、以及一个 Sub 类继承自 Super 类;
  • 使用协变、逆变、非变分别定义三个泛型类;
  • 分别创建泛型类来演示协变、逆变、非变;
Scala 复制代码
object Demo {
  def main(args: Array[String]): Unit = {
    //非变案例演示
    val A:Temp1[Sub] = new Temp1[Sub]
    //val B:Temp1[Super] = A      //非变, 编译报错,不能赋值。
    //尽管类型A是B的子类型,Pair[A]和Pair[B]没有任何从属关系,参数化类型之间没有关系,不管原类型之间的关系
 
    // 协变案例演示
    val C: Temp2[Sub] = new Temp2[Sub]
    val D: Temp2[Super] = C
    //类型C是D的子类型,Pair[C]可以认为是Pair[D]的子类型,参数化类型的方向和类型的方向是一致的。
 
    // 逆变案例演示
    val E: Temp3[Super] = new Temp3[Super]
    val F: Temp3[Sub] = E
   //类型F是E的子类型,Pair[E]反过来可以认为是Pair[F]的子类型。参数化类型的方向和类型的方向是相反的
  }
}
相关推荐
开心猴爷1 小时前
HTTPS Everywhere 时代的抓包挑战,从加密流量解析到底层数据流捕获的全流程方案
后端
卓码软件测评1 小时前
【第三方CNAS软件测试机构:Gatling中的资源监控_实时收集服务器CPU、内存、磁盘I/O和网络指标】
后端·测试工具·测试用例·scala·压力测试
DengRan1 小时前
别再手写 Java 二进制解析了 — 用 FastProto 从繁琐到优雅
后端
岁岁种桃花儿1 小时前
Java应用篇如何基于Redis共享Session实现短信登录
java·开发语言
不是笨小孩1351 小时前
多元算力融合实践:openEuler在中等配置硬件环境下的性能验证
后端
q_19132846951 小时前
基于SpringBoot2+Vue2+uniapp的考研社区论坛网站及小程序
java·vue.js·spring boot·后端·小程序·uni-app·毕业设计
csbysj20201 小时前
Python3 MySQL (PyMySQL) 使用指南
开发语言
derive_magic1 小时前
wwwwwwjava
开发语言·c#
稚辉君.MCA_P8_Java1 小时前
Gemini永久会员 深度解析jvm内存结构
jvm·后端·架构