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学习笔记15----子节点和兄弟节点的操作

    1.元素.firstChild : 只读属性,第一个子节点 标准下:会包含文本类型的子节点. 非标准下:只包含元素类型子节点. 元素.firstElementChild : 只读属性,第一个元素子节点 ...

  2. 纯CSS实现圆角、可拖动的一个DIV模块层

    <style>body{ margin:0px; padding:0px; font-size:14px;}#t { position:absolute; float:left; left ...

  3. jqueryEasyui常用代码

    //查询: function doSearch(form){ var fields =$('#queryForm').serializeArray(); var $fm = $(form); var ...

  4. ajax回调函数中使用$(this)取不到对象的解决方法

    如果在ajax的回调函数内使用$(this)的话,实践证明,是取不到任何对象的,需要的朋友可以参考下 $(".derek").each(function(){ $(this).cl ...

  5. Windows消息队列一

    系统消息--ID范围 -0x03FF 由系统定义好的小哦啊拍下哦,可以在程序中直接使用. 用户自定义消息--ID范围0x0400-0x7FFF 由用户自己定义,满足用户自己的需求.由用户自己发出消息, ...

  6. Windoows窗口程序七

    WM_QUIT--用于结束消息循环处理 wParam - PostQuitMessage函数传递的参数 lParam - 不使用 当GetMessage收到这个消息后,会返回false,结束while ...

  7. Qt 事件过滤器

    Qt创建了QEvent事件对象之后,会调用QObject的event()函数做事件的分发.有时候,你可能需要在调用event()函数之前做一些另外的操作,比如,对话框上某些组件可能并不需要响应回车按下 ...

  8. Spark1.0.0 学习路线指导

    转自:http://www.aboutyun.com/thread-8421-1-1.html 问题导读1.什么是spark?2.spark编程模型是什么?3.spark运维需要具有什么知识?4.sp ...

  9. 关于Cocos2d-x有些头文件无法引入或者类显示无法打开

    选中工程右键“属性”->"配置属性“->"c/c++"->"常规”->"附加包含目录"中添加“”$(EngineRo ...

  10. linux -- ubuntu桌面版安装xampp

    首先,请从www.xampp.org下载最新版XAMPP. 安装 如果是xampp压缩文件 将xampp压缩文件复制到/opt下并解压.如果你计算机没有/opt目录,用 “sudo mkdir/opt ...