Scala泛型

简单回顾泛型

java中可使用泛型进行编程,一个简单的泛型例子如下:

List<String> strList = new ArrayList<String>();
strList.add("one");
strList.add("two");
strList.add("three");

String one = strList.get(0); // 泛型拿数据不必进行类型转换,不使用泛型的话需要对类型进行转换

scala的泛型

scala中的泛型称为类型参数化(type parameterlization)。语法跟java不一样,使用”[]”表示类型。

一个使用类型参数化的函数:

def position[A](xs: List[A], value: A): Int = {
    xs.indexOf(value)
}

position(List(1,2,3), 1) // 0
position(List("one", "two", "three"), "two") // 1

稍微复杂点的类型参数化,实现一个map函数,需要一个List和一个函数作为参数:

普通的map方法:

List(1,2,3) map { _ * 2 }  // List[Int] = List(2,4,6)
List(1,2,3) map { _ + "2" }  // List[String] = List(12, 22, 32)

使用泛型实现的map方法:

def map[A,B](list:List[A], func: A => B) = list.map(func)

map(List(1,2,3), { num: Int => num + "2" }) // List[String] = List(12, 22, 32)
map(List(1,2,3), { num: Int => num * 2 }) // List[Int] = List(2, 4, 6)

上限和下限

scala跟java一样,也提供了上限(upper bounds)和下限(lower bounds)功能。

上限(upper bounds)

java中上限的使用如下:

<T extends Object>

通配符形式
<? extends Object>

scala写法:

[T <: AnyRef]

通配符形式
[_ <: AnyRef]

上限的一些例子:

public void upperBound(List<? extends Number> list) {
    Object obj = list.get(0); // Number是Object的子类,使用Object可以代替Number。
    Number num = list.get(0);
    Integet i = list.get(0); // compile error
    list.add(new Integer(1)); // compile error
}

上限做参数,set的话不能确定具体的类型,所以会报编译错误。get的话得到的结果的类型的下限为参数的上限。相当于使用了上限参数的话,该参数就变成了只读参数,类似生产者,只提供数据。

scala版本:

def upperBound[A <: Animal](list: ListBuffer[A]): Unit = { 
    list += new Animal("123") // compile error
    val obj: AnyRef = list(0) // ok
    val a: Animal = list(0) // ok
    val a: Cat = list(0) // compile error
}

这里使用ListBuffer作为集合,ListBuffer的+=方法会在列表内部添加数据,不会产生一个新的List。如果使用List的话,:+操作符会在新生成的List中自动得到符合所有元素的类型。

List[Animal](new Cat()) :+ 1 // List[Any] = List([email protected], 1)

生成新的List会自动根据上下文得到新的泛型类型List[AnyRef]。

下限(lower bounds)

<T super MyClass>

通配符形式
<? super MyClass>

scala写法:

[T >: MyClass]

通配符形式
[_ >: MyClass]

下限的一些例子:

public static void lowerBound(List<? super Number> l) {
    l.add(new Integer(1));
    l.add(new Float(2));
    Object obj = l.get(0);
    Number num = l.get(0); // compile error
}

下限做参数,get方法只能用最宽泛的类型来获取数据,相当于get只提供了数据最小级别的访问权限。类似消费者,主要用来消费数据。

scala版本:

def lowerBound[A >: Animal](list: ListBuffer[A]): Unit = { 
    list += new Animal() // ok
    list += new Cat() // ok
    val obj: Any = list(0) // ok
    val obj: Animal = list(0) // compile error
}

协变和逆变

协变(covariance):对于一个带类型参数的类型,比如List[T],如果对A及其子类型B,满足List[B]也符合List[A]的子类型,那么就称为协变,用加号表示。比如 MyType[+A]

逆变(contravariance):如果List[A]是List[B]的子类型,用减号表示。比如MyType[+B]

如果一个类型支持协变或逆变,则称这个类型为可变的(variance)。否则称为不可变的(invariant)。

在java里,泛型类型都是不可变的,比如List<String>并不是List<Object>的子类。

trait A[T]
class C[T] extends A[T]
class Parent; class Child extends Parent
val c:C[Parent] = new C[Parent] // ok
val c:C[Parent] = new C[Child]; // Child <: Parent, but class C is invariant in type T.

协变

上面的例子提示已经很明确了。类C是不可变的,改成协变即可。

trait A[+T]
class C[+T] extends A[T]
class Parent; class Child extends Parent
val c: C[Parent] = new C[Parent] // ok
val c: C[Parent] = new C[Child]  // ok

scala中List就是一个协变类。

val list:List[Parent] = List[Child](new Child())

逆变

逆变概念与协变相反。

trait A[-T]
class C[-T] extends A[T]
class Parent; class Child extends Parent
val c: C[Parent] = new C[Parent] // ok
val c: C[Child] = new C[Parent]  // ok

协变逆变注意点

逆变协变并不会被继承,父类声明为逆变或协变,子类如果想要保持,任需要声明:

trait A[+T]
class C[T] extends A[T] // C是不可变的,因为它不是逆变或协变。
class D[+T] extends A[T] // D是可变的,是协变
class E[-T] extends A[T] // E是可变的,是逆变
时间: 2024-08-14 02:00:03

Scala泛型的相关文章

[Scala基础系列 10]Scala泛型、类型边界

1.泛型和类型边界 1.1.上限(upper bounds) 我们先看一个简单的实例,用于判断两个变量中较大的值,其中两个变量的类型均为Int型 package com.tv189.advanced /** * Created by molyeo on 2015/8/12. */ class PairInt(val first: Int, val second: Int) { def bigger = { if (first.compareTo(second) > 0) first else s

17. Scala泛型、上下界、视图界定、上下文界定

17.1 泛型的基本介绍 17.1.1 基本介绍 1) 如果我们要求函数的参数可以接受任意类型,可以使用泛型,这个类型可以代表任意的数据类型 2) 例如List,在创建List时,可以传入整型.字符串.浮点数等等任意类型.那是因为List在类定义时引用了泛型.比如在Java中:public interface List<E> extends Collection<E> 17.1.2 泛型的应用案例1 -要求 1) 编写一个Message类 2) 可以构建Int类型的Message,

Scala入门到精通——第十六节 泛型与注解

本节主要内容 泛型(Generic Type)简介 注解(Annotation)简介 注解常用场景 1. 泛型(Generic Type)简介 泛型用于指定方法或类可以接受任意类型参数,参数在实际使用时才被确定,泛型可以有效地增强程序的适用性,使用泛型可以使得类或方法具有更强的通用性.泛型的典型应用场景是集合及集中中的方法参数,可以说同java一样,scala中泛型无处不在,具体可以查看scala的api //单个泛型参数的使用情况 class Person[T](var name:T) cla

Scala 深入浅出实战经典 第42讲:scala 泛型类,泛型函数,泛型在spark中的广泛应用

王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 腾讯微云:http://url.cn/TnGbdC 360云盘:http://yunpan.cn/cQ4c2UALDjSKy 访问密码 45e2 技术爱好者尤其是大数据爱好者 可以加DT大数据梦工厂的qq群 DT大数据梦工厂① :462923555 DT大数据梦工厂②:437123764 DT大数据梦工厂③

scala中List的泛型分析以及::类和Nil对象

学习了scala中List的泛型分析以及::类和Nil对象,List是对scala的类型系统的一个非常好的使用,进一步巩固List的知识对于理解scala的类型系统是非常有价值的.本讲主要分析了List的泛型.::类和Nil对象. List有两个非常重要的子类,一个是::,一个是Nil,这两个子类都是case class.Nil表示一个空的列表,而::表达的是一个非空的列表. 例子如下: case object Nil extends List[Nothing]{ override def is

第42讲:Scala中泛型类、泛型函数、泛型在Spark中的广泛应用

今天来了解下scala的泛型 先让我们看下这段代码 class Triple[F,S,T](val first:F,val second: S,val third: T) val triple = new Triple("Spark",3,3.1415)        val bigData = new Triple[String,String,Char]("Spark","Hadoop",'R')        def getData[T](l

Scala的泛型

我们在这文章将要学习Scala的最后一个特性是泛型.Java程序员们可能最近才知道这个东西,因为这个特性是在Java1.5中才被加入的.泛型是一种可以让你使用类型参数的设施.例如当一个程序员正在实现一个链表时,将不得不面对诸如如何决定链表中节点保存数据的类型之类的问题.正由 于这是一个链表,所以往往会在不同的环境中使用,因此,我们不能草率的决定节点数据类型,比如说Int.这种决定是相当的草率且局限性的.以前Java程序员们通常使用Object,所有类型的超类,来解决问题.但是这种方法远远算不上是

Scala笔记整理(八):类型参数(泛型)与隐士转换

[TOC] 概述 类型参数是什么?类型参数其实就是Java中的泛型.大家对Java中的泛型应该有所了解,比如我们有List list = new ArrayList(),接着list.add(1),没问题,list.add("2"),然后我们list.get(1) == 2,对不对?肯定不对了,list.get(1)获取的其实是个String--"2",String--"2"怎么可能与一个Integer类型的2相等呢? 所以Java中提出了泛型的

好程序员大数据学习路线分享Scala系列之泛型

好程序员大数据学习路线分享Scala系列之泛型,带有一个或多个类型参数的类是泛型的. 泛型类的定义: //带有类型参数A的类定义class Stack[A] {private var elements: List[A] = Nil//泛型方法def push(x: A) { elements = x :: elements }def peek: A = elements.headdef pop(): A = {val currentTop = peekelements = elements.ta