abstract class Stack[A] {
  def push(x: A): Stack[A] = new NonEmptyStack[A](x, this)
    def isEmpty: Boolean
    def top: A
    def pop: Stack[A]
    val contents: T = _ //初始值:_ ,表示一个默认值,数字类型是0 ,boolean是false ,Unit是() (无参数无返回),其他是null
}
class EmptyStack[A] extends Stack[A] {
  def isEmpty = true
  def top = error("EmptyStack.top")
  def pop = error("EmptyStack.pop")
}
class NonEmptyStack[A](elem: A, rest: Stack[A]) extends Stack[A] {
  def isEmpty = false
  def top = elem
  def pop = rest
} val x = new EmptyStack[Int]
val y = x.push(1).push(2)
println(y.pop.top) def isPrefix[A](p: Stack[A], s: Stack[A]): Boolean = {
  p.isEmpty ||
  p.top == s.top && isPrefix[A](p.pop, s.pop)
} val s1 = new EmptyStack[String].push("abc")
val s2 = new EmptyStack[String].push("abx").push(s1.top)
println(isPrefix[String](s1, s2))

类型参数边界

  在用类型参数定义了一个抽象类Set[A]后,在实现中要用到比较(<>),但是不能确定A的具体类型,因此不能直接使用。一个解决办法就是对合法类型进行限制,对只含有方法<>的类型放行。在标准库里有一个特质Ordered[A],用来表示可比较的类型。现在可以强制要求这个类型为Ordered的子类型。可以通过给出一个上界(upper bound)的方式来解决这个问题:

abstract class Set[A] {
  def incl(x: A): Set[A]
  def contains(x: A): Boolean
}
// 传入的A类型参数必须是Ordered[A]的子类型
class EmptySet[A <: Ordered[A]] extends Set[A] {
def contains(x: A): Boolean = false
def incl(x: A): Set[A] = new NonEmptySet(x, new EmptySet[A], new EmptySet[A])
// 在new NonEmptySet(...)时,没有写入类型参数。因为可以从返回值类型中推断出来。
}
class NonEmptySet[A <: Ordered[A]](elem: A, left: Set[A], right: Set[A]) extends Set[A] {
def contains(x: A): Boolean =
if (x < elem) left contains x
else if (x > elem) right contains x
else true
def incl(x: A): Set[A] =
if (x < elem) new NonEmptySet(elem, left incl x, right)
else if (x > elem) new NonEmptySet(elem, left, right incl x)
else this
}
// 先创建一个Ordered的子类
case class Num(value: Double) extends Ordered[Num] {
def compare(that: Num): Int =
if (this.value < that.value) -1
else if (this.value > that.value) 1
else 0
} val s = new EmptySet[Num].incl(Num(1.0)).incl(Num(2.0))
s.contains(Num(1.5))

变化型注解(variance annotation)

“+”表示协变,“-”表示逆变。
C[+T]:如果A是B的子类,那么C[A]是C[B]的子类。
C[-T]:如果A是B的子类,那么C[B]是C[A]的子类。
C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系。

协变类型应该出现在协变位置,这些位置包括:类里值的参数类型;方法的返回值类型;以及其他协变类型中的参数。放在其他地方会被拒绝

class Array[+A] {
def apply(index: Int): A
def update(index: Int, elem: A) // 可以通过下界(lower bounds)来解决这个问题
^ covariant type parameter A
appears in contravariant position.
}
// 下界(Lower Bounds)
// B是A的父类
class Stack[+A] {
def push[B >: A](x: B): Stack[B] = new NonEmptyStack(x, this)
}

Scala 泛型类型和方法的更多相关文章

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

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 ...

  2. scala的apply方法

    package com.test.scala.test /** * apply 方法 */ object ApplyTest { def main(args: Array[String]): Unit ...

  3. “T”必须是具有公共的无参数构造函数的非抽象类型,才能用作泛型类型或方法

    最近在项目中,使用EF编程时,在使用泛型类型的过程中,写了一上午,结果生成时,编译不通过,报出如下错误: “T”必须是具有公共的无参数构造函数的非抽象类型,才能用作泛型类型或方法.如图: 找了好久,终 ...

  4. spark-shell的Scala的一些方法详解

    Tom,DataBase,80 Tom,Algorithm,50 Tom,DataStructure,60 Jim,DataBase,90 Jim,Algorithm,60 Jim,DataStruc ...

  5. scala 基本类型和操作

    Scala基本类型 Scala中的基本数据类型如下图:  (来源:Programming in scala) 从上表中可以看出,Scala的基本数据类型与Java中的基本数据类型是一一对应的,不同的是 ...

  6. 第1节 Scala基础语法:scala中的方法源码分析

    val list=List(1,2,3,4) list.reduce((x:Int,y:Int)=>x+y)--->list.reduceLeft((x:Int,y:Int)=>x+ ...

  7. scala函数和方法的差别

    想知道什么差别,把代码复制下来运行看结果 // 1 1 2 3 5 8 13 def f(x:Int) :Int= { ) ) else f(x-)+f(x-) } println(f()) // v ...

  8. 大数据学习——scala函数与方法

    package com /** * Created by Administrator on 2019/4/8. */ object TestMap { def ttt(f: Int => Int ...

  9. java 声明多个泛型类型和通配符

    若一个类中多个字段需要不同的泛型声明,则在声明类的时候指定多个泛型类型即可: 格式: public interface IDAO<PK, T> { PK add(T t); void re ...

随机推荐

  1. js学习笔记19----getElementsByClassName函数封装

    js里面的getElementsByClassName()方法可通过某个class名获取到元素,在标准浏览器下可使用,在非标准浏览器下不可用.为了能够让这个方法兼容所有的浏览器,可以封装成如下函数: ...

  2. 验证url 地址是否是图片

    由于正则不是很熟悉 所以面对这样的目前只能采取两步走 一 判断url地址是否是正确的http 二判断后缀是否是图片 格式 /驗證URL function validUrl(strUrl){ strUr ...

  3. r语言 技巧总结

    1.table函数返回众数,再转为dataframe as.data.frame(table(x)) 2.使用which 返回数组下标 which(rs.list=="rs1008507&q ...

  4. LVS学习笔记及总结(思维导图版)

    转自: http://www.07net01.com/2015/10/944377.html 下图是我在跟随马哥的脚步学习LVS过程中的学习笔记,以此为蓝本总结的,若有不足之处请谅解!

  5. Hibernate一级缓存、二级缓存以及查询缓存的关系

    转载自http://blog.csdn.net/maoyeqiu/article/details/50209893 前两天总结了一下二级缓存和查询缓存的关系,但是又有一个新的问题,就是查询缓存缓存到二 ...

  6. 第二百九十六节,python操作redis缓存-Hash哈希类型,可以理解为字典类型

    第二百九十六节,python操作redis缓存-Hash哈希类型,可以理解为字典类型 Hash操作,redis中Hash在内存中的存储格式如下图: hset(name, key, value)name ...

  7. javax.naming.NoInitialContextException错误的解决方案

    今天,学习用了一下nutz框架,写了一个HelloWorld的小程序,在用jndi配置数据源时,写了一个测试类,并在main方法中调用了jndi获得数据库连接,但是报错了,错误信息如下: javax. ...

  8. C# 将RichTextBox中内容的文档以二进制形式存

    private void button1_Click(object sender, EventArgs e)        { System.IO.MemoryStream mstream = new ...

  9. TinyOS节点间通信相关接口和组件介绍

    一.基本通信接口:   Packet:提供了对message_t抽象数据类型的基本访问.这个接口的命令有:清空消息内容,获得消息的有效载荷区长度,获得消息有效载荷区的指针. //tos/interfa ...

  10. CentOS系统命令

    系统命令 yum命令 yum makecache yum 生成缓存 yum list installed mysql* 查看有没有安装过*包 rpm -qa | grep mysql* 查看有没有安装 ...