Scala的存在类型

简介: Scala的存在类型

Scala的存在类型


存在类型也叫existential type,是对类型做抽象的一种方法。可以在你不知道具体类型的情况下,就断言该类型存在。


存在类型用_来表示,你可以把它看成java中的?。


下面是存在类型的具体例子:


简写 完整形式 描述
Seq[_] Seq[T] forSome {type T} T 可以是Any 的任意子类
Seq[_ <: A] Seq[T] forSome {type T <: A} T 可以是A(在某处已经定义了)的任意子类
Seq[_ >: Z <: A] Seq[T] forSome {type T >: Z <: A} T 可以是A 的子类且是Z 的超类


上面的表格以常用的Seq为例,列举了存在类型的例子。


那么为什么会需要存在类型呢?


如果我们有一个List[A],我们需要两个版本的double函数,一个版本接受List[Int]并返回新的List[Int]*2,另外一个版本接受List[String], 并通过对整数调用toInt,将字符串转换为Int,然后调用第一个版本的double函数。


我们可能会这样写:


object Doubler {
def double(seq: Seq[String]): Seq[Int] = double(seq map (_.toInt))
def double(seq: Seq[Int]): Seq[Int] = seq map (_*2)
}


上面的程序看起来是没问题的,但是编译却失败。


Error:(3, 7) double definition:
def double(seq: Seq[String]): Seq[Int] at line 12 and
def double(seq: Seq[Int]): Seq[Int] at line 13
have same type after erasure: (seq: Seq)Seq
def double(seq: Seq[Int]): Seq[Int] = seq map (_*2)


问题就在于编译过程中的类型擦除,也就是在编译成字节码过后,定义的泛类型将会被删除。那么最后Seq[String]和Seq[Int]都会被编译成Seq,最终导致两个方法拥有同样的参数列表,最终编译报错。


既然有类型擦除的问题,那么我们考虑定义一个double方法,在double方法内部进行类型的判断:


object Doubler {
  def double(seq: Seq[_]): Seq[Int] = seq match {
    case Nil => Nil
    case head +: tail => (toInt(head) * 2) +: double(tail)
  }
  private def toInt(x: Any): Int = x match {
    case i: Int => i
    case s: String => s.toInt
    case x => throw new RuntimeException(s"Unexpected list element $x")
  }
}


为什么我们需要使用Seq[_]呢? 我们看一下Seq类型的定义:


type Seq[+A] = scala.collection.Seq[A]


从定义我们知道,Seq类型一定是需要一个类型参数的,如果我们这样写:


def double(seq: Seq): Seq[Int] = seq match {
    case Nil => Nil
    case head +: tail => (toInt(head) * 2) +: double(tail)
  }


则会编译出错,因为tail是Seq[A]类型的,但是double需要一个Seq类型。


使用Seq[_]表示,Seq[T] forSome {type T}。虽然我不知道Seq里面具体是哪种类型,但是肯定是有类型的。


可以对比一下java.


util.List[_ <: A] 的表达式在结构上与Java 的表达式java.util.List<? extends A>的类似之处。


你会在scala代码中看到很多Seq[_]的代码,存在类型的主要目的是为了兼容java代码。

相关文章
|
6月前
|
IDE Java 编译器
scala的两种变量类型 var 和 val
scala的两种变量类型 var 和 val
146 2
scala的两种变量类型 var 和 val
|
安全 Java 大数据
大数据开发基础的编程语言的Scala的类型系统
Scala是一种强类型的编程语言,它具有一套完善的类型系统。本文将介绍Scala的类型系统,帮助开发者了解这门语言的类型安全性和灵活性。
93 0
|
大数据 Java 编译器
Scala 字符类型|学习笔记
快速学习 Scala 字符类型。
174 0
|
编译器 Scala 容器
Scala的Higher-Kinded类型
Scala的Higher-Kinded类型
|
编译器 Scala
Scala教程之:静态类型(三)
Scala教程之:静态类型(三)
|
存储 Java Scala
Scala教程之:静态类型(二)
Scala教程之:静态类型(二)
|
安全 Java Scala
Scala教程之:静态类型 (一)
Scala教程之:静态类型 (一)
|
安全 Java Scala
Scala入门到精通——第二十一节 类型参数(三)-协变与逆变
作者:摇摆少年梦 视频地址:http://www.xuetuwuyou.com/course/12 本节主要内容 协变 逆变 类型通匹符 1. 协变 协变定义形式如:trait List[+T] {} 。当类型S是类型A的子类型时,则List[S]也可以认为是List[A}的子类型,即List[S]可以泛化为List[A]。也就是被参数化类型的泛化方向与参数类
2664 0
|
Scala
Scala入门到精通——第十七节 类型参数(一)
本节主要内容 类型变量界定(Type Variable Bound) 视图界定(View Bound) 上界(Upper Bound)与下界(Lower Bound) 1. 类型变量界定(Type Variable Bound) 类型变量界定是指在泛型的基础上,对泛型的范围进行进一步的界定,从而缩下泛型的具体范围,例如: //下面的类编译通不过 //因为泛型T
3412 0