十六、Scala的泛型


1. 泛型

泛型指泛指某种具体的数据类型,在Scala中泛型用[数据类型]表示。实际开发中,泛型一般是结合数组或集合来使用的,此外,泛型的常见用法还有以下三种:

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

1.1 泛型方法

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

格式:

def 方法名[泛型名称](...) = {
	//...
}

示例:
在这里插入图片描述

object ClassDemo {
	//方式一:不采用泛型的普通方法
	def getMiddleElement(arr:Array[Int]) = arr(arr.length / 2)
	//方式二:采用自定义的泛型
	//T:type单词的缩写,类型(默认)
	def getMiddleElement[T](arr:Array[T]) = arr(arr.length / 2)

	def main(args:Array[String]):Unit = {
		//测试方式一
		println(getMiddleElement(1,2,3,4,5))//3
		println(getMiddleElement("a","b","c"))//报错!可不可以把Int向上提升为Any呢?可以但消耗资源

		//测试方式二
		println(getMiddleElement(1,2,3,4,5))//3
		println(getMiddleElement("a","b","c"))//b
	}
}

1.2 泛型类

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

格式:

class[T](val 变量名T)

示例:
在这里插入图片描述

object ClassDemo {
	//1.定义一个Pair泛型类,该类包含两个字段,且两个字段的类型不固定
	class Pair[T](var a:T, var b:T)

	def main(args:Array[String]):Unit = {
		//2.创建不同类型的Pair泛型类对象,并打印
		//val p1 = new Pair[Int](10, "abc") //报错
		val p1 = new Pair[Int](10, 20)
		println(p1.a, p1.b) //(10, 20)

		val p2 = new Pair[String]("a", "b")
		println(p2.a, p2.b) //(a, b)
	}
}

1.3 泛型特质

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

格式:

trait 特质A[T] {
	//特质中的成员
}

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

示例:
在这里插入图片描述

object ClassDemo {
	//1.定义泛型特质Logger,该类包含一个变量a和show()方法,它们都是用Logger特质的泛型
	trait Logger[T] {
		val a:T
		def show(b:T)
	}

	//2.定义单例对象ConsoleLogger,继承Logger特质
	object ConsoleLogger extends Logger[String]{
		override val a:String = "lee"
		override def show(b:String):Unit = println(b)
	}

	def main(args:Array[String]):Unit = {
		//3.打印单例对象ConsoleLogger中的成员
		println(ConsoleLogger.a)
		COnsoleLogger.show("abc")
	}
}
//lee
//abc

2. 上下界

在使用泛型(方法、类、特质)时,如果要限定该泛型必须从哪个类继承、或者必须是哪个类的父类,此时必须要使用泛型的上下界。

2.1 上界

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

格式:

[T <: 类型]

比如:[T <: Person]意思是泛型T的数据类型必须是Person类型或Person子类型

示例:
在这里插入图片描述

object ClassDemo {
	//1.定义一个Person类
	class Person

	//2.定义一个Student类,继承Person类
	class Student extends Person

	//3.定义一个泛型方法demo(),该方法接收一个Array参数
	//def demo[T](arr:Array[T]) = println(arr)
	//4.限定demo方法的Array元素类型只能是Person或Person子类
	def demo[T <: Person](arr:Array[T]) = println(arr)

	def main(args:Array[String]):Unit = {
		//5.调用demo()方法,传入不同元素类型的Array
		demo(Array(new Person(), new Person()))
		demo(Array(new Student(), new Student()))
		//demo(Array("a", "b", "c")) //这行会报错
	}
}
//lee

2.2 下界

使用T >: 类型名 表示给类型添加一个下界,表示泛型参数必须要是该类本身或该类型的父类型。

格式:

[T >: 类型]

比如:[T >: Person]意思是泛型T的数据类型必须是Person类型或Person父类型

注意:
如果泛型既有上界又有下界,下界写在前面,上界写在后面,即:[T >: 类型1 <: 类型2]

示例:
在这里插入图片描述

object ClassDemo {
	//1.定义一个Person类
	class Person

	//2.定义一个Policeman类,继承Person类
	class Policeman extends Person

	//3.定义一个Superman类,继承Policeman类
	class Superman extends Policeman

	//3.定义一个泛型方法demo(),该方法接收一个Array参数
	//def demo[T](arr:Array[T]) = println(arr)
	//4.限定demo方法的Array元素类型只能是Person或Policeman
	def demo[T >: Policeman](arr:Array[T]) = println(arr)

	def main(args:Array[String]):Unit = {
		//6.调用demo()方法,传入不同元素类型的Array
		demo(Array(new Person()))
		demo(Array(new Policeman()))
		//demo(Array(new Superman())) //报错
		//demo(Array("a", "b", "c")) //报错
		
	}
}

3. 协变、逆变、非变

Spark的源代码中大量使用到协变、逆变、非变。
在这里插入图片描述

3.1 非变

格式:

class Pair[T]{}
  • 默认泛型类是非变的,即:类型B是A的子类型,Pair[A]和Pair[B]没有任何从属关系

3.2 协变

格式:

class Pair[+T]{}
  • 类型B是A的子类型,Pair[B]是Pair[A]的子类型
  • 参数化类型的方向和类型的方向是一致的

3.3 逆变

格式:

class Pair[-T]{}
  • 类型B是A的子类型,Pair[A]是Pair[B]的子类型
  • 参数化类型的方向和类型的方向是相反的

示例:
在这里插入图片描述

object ClassDemo {
	//1.定义一个Super类和一个Sub子类
	class Super
	class Sub extends Super

	//2.使用协变、逆变、非变分别定义三个泛型类
	class Temp1[T]
	class Temp2[+T]
	class Temp3[-T]

	def main(args:Array[String]):Unit = {
		//3.分别创建泛型类对象来演示协变、逆变、非变
		//测试非变
		val t1:Temp1[Sub] = new Temp1[Sub]
		//val t2:Temp1[Super] = t1 //编译报错

		//测试协变
		val t3:Temp2[Sub] = new Temp2[Sub]
		val t4:Temp2[Super] = t3 

		//测试逆变
		//val t5:Temp3[Sub] = new Temp3[Sub]
		//val t6:Temp3[Super] = t5 //编译报错
		val t7:Temp3[Super] = new Temp3[Super]
		val t8:Temp3[Sub] = t7
		
	}
}

4. 案例:列表去重排序

需求:
在这里插入图片描述

import scala.io.Source

object ClassDemo {
	def main(args:Array[String]):Unit = {
		//1.定义数据源对象,关联数据源文件
		val source = Source.fromFile("./data/1.txt")
		//2.从指定的文件中读取所有的数据
		val list1 = source.mkString.split("\\s+").toList
		println(list1) //List(11,6,5,3,22,9,3,11,5,1,2)
		//3.把List[String] -> List[Int]
		val list2:List[Int] = list1.map(_.toInt)
		//4.把List[Int] -> Set[Int],对列表元素去重
		val set:Set[Int] = list2.toSet
		//5.Set[Int] -> List[Int],然后升序排列
		val list3:List[Int] = set.toList.sorted
		println(list3) //List(1,2,3,5,6,9,11,22)
		//6.把所有的数据写入到指定的目的地文件中
		//6.1创建字符缓冲流,用来写入数据到指定的目的地文件中
		val bw = new BufferedWriter(new FileWriter("./data/2.txt"))
		//6.2遍历list3,获取每一个数字
		for(i <- list3) {
			//6.3将获取到的数字转成字符串后,再写入
			bw.write(i.toString)
			//6.4换行
			bw.newLine()
		}
		//7.释放资源
		bw.close()
		source.close()
		
		
	}
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值