scala case class 继承_数字硬件系统设计之一:Scala快速入门(2)

原想简单笼统介绍一下scala,后感觉这么做意思不大,网友看了和没看一样,还是应该稍微详细具体一点,故而把系列编号由(上)(中)(下),改为(上)(2)(3)(4)(5)....,(上)就是(1)吧d0c0a7c130b26c177aa7e039923854c2.png,以下内容部分节选于我们即将出版的书稿:

a1627fd33f5d98c000ed1dd91215b031.png

本次推文介绍scala里的类用法:

在Scala里,类是用关键字“class”来定义,一旦定义完成,就可以通过“new 类名”的方式来构造一个对象。而这个对象的类型,就是这个类。换句话说,一个类就是一个类型,不同的类就是不同的类型。在后续会讲到类的继承关系,以及超类、子类和子类型多态的概念。

在类里可以定义val或var类型的变量,它们被称为“字段”;还可以定义“def”函数,它们被称为“方法”;字段和方法统称“成员”。字段通常用于保存对象的状态或数据,而方法则用于承担对象的计算任务。字段也叫“实例变量”,因为每个被构造出来的对象都有其自己的字段。在运行时,操作系统会为每个对象分配一定的内存空间,用于保存对象的字段。方法则不同,对所有对象来说,方法都是一样的程序段,因此不需要为某个对象单独保存其方法。而且,方法的代码只有在被调用时才会被执行,如果一个对象在生命周期内都没有调用某些方法,那么完全没必要浪费内存为某个对象去保存这些无用的代码。

外部想要访问对象的成员时,可以使用句点符号“ . ”,通过“对象.成员”的形式来访问。此外,用new构造出来的对象可以赋给变量,让变量名成为该对象的一个指代名称。需要注意的是,val类型的变量只能与初始化时的对象绑定,不能再被赋予新的对象。一旦对象与变量绑定了,便可以通过“变量名.成员”的方式来多次访问对象的成员。例如:

    scala> class Students {

             |    var name = "None"

             |    def register(n: String) = name = n

             |  }

    defined class Students

    scala> val stu = new Students

 stu: Students = Students@1a2e563e

    scala> stu.name

    res0: String = None

    scala> stu.register("Bob")

    scala> stu.name

    res2: String = Bob

    scala> stu = new Students

    :13: error: reassignment to val

           stu = new Students

Scala的类成员默认都是公有的,即可以通过“对象.成员”的方式来访问对象的成员,而且没有“public”这个关键字。如果不想某个成员被外部访问,则可以在前面加上关键字“private”来修饰,这样该成员只能被类内部的其他成员或上篇提到的伴生对象来访问,外部只能通过其他公有成员来间接访问。例如:

    scala> class Students {

             |    private var name = "None"

             |    def register(n: String) = name = n

             |    def display() = println(name)

             |  }

    defined class Students

    scala> val stu = new Students

    stu: Students = Students@75063bd0

    scala> stu.register("Bob")

    scala> stu.name

    :13: error: variable name in class Students cannot be accessed in Students

           stu.name

              ^

     scala> stu.display

    Bob

类的构造方法

   1、主构造方法

在C++、Java、Python等面向对象语言里,类通常需要定义一个额外的构造方法(构造函数)。这样,要构造一个类的对象,除了需要关键字new,还需要调用构造方法。事实上,这个过程中有一些代码是完全重复的。Scala则不需要显式定义构造方法 ,而是把类内部的非字段、非方法的代码都当作“主构造方法”。而且,类名后面可以定义若干个参数列表,用于接收参数,这些参数将在构造对象时用于初始化字段并传递给主构造方法使用。Scala的这种独特语法减少了一些代码量。例如:

    scala> class Students(n: String) {

          |    val name = n

          |    println("A student named " + n + " has been registered.")

          |  }

    defined class Students

    scala> val stu = new Students("Tom")

    A student named Tom has been registered.

    stu: Students = Students@5464eb28

在这个例子中,Students类接收一个String参数n,并用n来初始化字段name。这样做,就无需像之前那样把name定义成var类型,而是使用函数式风格的val类型,而且不再需要一个register方法在构造对象时来更新name的数据。

函数println既不是字段,也不是方法定义,"= n"也不是字段,也不是方法定义,所以都被当成是主构造函数的一部分。在构造对象时,主构造函数被执行,因此在解释器里会打印了相关信息。

   2、辅助构造方法

除了主构造方法,scala还可以定义若干个辅助构造方法。辅助构造方法都是以“def this(..)”来开头的,而且第一步行为必须是调用该类的另一个构造方法,即第一条语句必须是“this(...)”——要么是主构造方法,要么是之前的另一个辅助构造方法。这种规则的结果就是任何构造方法最终都会调用该类的主构造方法(而主构造方法其实就是类定义本身),使得主构造方法成为类的单一入口。例如:

    scala> class Students(n: String) {

        |val name = n

        |def this() = this("None") //辅助构造方法是无参数的,其内部调用主构造方法

        |println("A student named " + n + " has been registered.")

        |  }

    defined class Students

    scala> val stu = new Students

    A student named None has been registered.

    stu: Students = Students@64105ca4

在这个例子中,定义了一个辅助构造方法,该方法是无参的,其行为也仅是给主构造方法传递一个字符串“None”。

定义此辅助构造方法的作用:当创建该类的实例对象时,如果缺省了参数,这样与主构造方法的参数列表是不匹配的,但是与辅助构造方法匹配,则系统会使用stu的辅助构造方法构造该实例对象。

Scala只允许主构造方法调用超类的构造方法,不允许辅助构造函数调用超类的构造方法(后续具体讲解)。这种限制源于Scala为了代码简洁性与简单性做出的折衷处理。

   3、无析构函数

因为Scala没有指针,同时使用了Java的垃圾回收器,所以不需要像C++那样定义析构函数。

   4、私有主构造方法

如果在类名与类的参数列表之间加上关键字“private”,那么主构造方法就是私有的,只能被内部定义的辅助构造函数访问,外部代码实列化对象时就不能通过主构造方法进行,而必须使用其他公有的辅助构造方法或工厂方法(专门用于构造对象的方法)。例如:

    scala> class Students private (n: String, m: Int) {

             |    val name = n

             |    val score = m

             |    def this(n: String) = this(n, 100)

             |    println(n + "'s score is " + m)

             |  }

    defined class Students

    scala> val stu = new Students("Bill", 90) //与私有的主构造方法匹配,访问错误

    :12: error: too many arguments (2) for constructor Students: (n: String)Students

           val stu = new Students("Bill", 90)

    scala> val stu = new Students("Bill") //与辅助构造方法匹配

    Bill's score is 100

    stu: Students = Students@7509b8e7

方法重载

如果在类里定义了多个同名的方法,但是每个方法的参数(主要是参数类型)不一样,那么就称这个方法有多个不同的版本,即方法重载,它是面向对象里多态属性的一种表现。这些方法虽然同名,但是它们是不同的,因为函数真正的特征标识是它的参数,而不是函数名或返回类型。注意重载与前面的重写的区别,重载是一个类里有多个不同版本的同名方法,重写是子类覆盖已定义在超类(即父类)的某个方法。

类参数

从前面的例子可以发现,多数时候类的列表参数仅仅是直接赋给某个字段。Scala为了进一步简化代码,允许在类参数前加上val或var来修饰,这样就会在类的内部生成一个与参数同名的公有字段(此字段也是类的成员)。构造对象时,这些参数会直接复制给同名字段。除此之外,还可以加上关键字private、protected或override来表明字段的权限(关于权限修饰见后续章节)。如果参数没有任何val或var关键字,那它就仅仅是“参数”,不是类的成员,只能用来初始化字段或给方法使用,外部不能访问这样的参数,内部也不能修改它,因为它不是类的成员。例如:

scala> class ab(a:Int){ //a不是类的成员,仅仅是个传递参数

     | val b=a+1

     | }

defined class ab

scala> val c=new ab(8)

c: ab = ab@6e2d3f2

scala> c.b

res0: Int = 9

scala> c.a

         ^

       error: value a is not a member of ab

以下在类参数定义时,加上val,则类参数同时变成了类成员:

scala> class ab(val a:Int){

     | val b=a+1

     | }

defined class ab

scala> val c=new ab(8)

c: ab = ab@ccd000e

scala> c.b

res2: Int = 9

scala> c.a

res3: Int = 8

单例对象与伴生对象

在Scala里,除了用new可以构造一个对象,也可以用“object”开头定义一个对象。它类似于类的定义,只不过不能像类那样有参数,也没有构造方法。因此,不能用new来实例化一个object的定义,因为它已经是一个对象了,对比class用new可以实例化无数个对象,而object只能定义一个对象,因此也把object定义的对象叫单列对象,如果对于单列对象object A 存在伴生类class A,则又把object A叫Class A的伴生对象,它们可以相互访问私有成员。概念虽多但是意思很明确d0c0a7c130b26c177aa7e039923854c2.png

工厂对象与工厂方法

如果定义一个方法专门用来构造某一个类的对象,那么这种方法就称为“工厂方法”。包含这些工厂方法集合的单例对象,也就叫“工厂对象” 。通常,工厂方法会定义在伴生对象里。尤其是当一系列类存在继承关系时,可以在基类的伴生对象里定义一系列对应的工厂方法。使用工厂方法的好处是可以不用直接使用new来实例化对象,改用方法调用,而且方法名可以是任意的,这样对外隐藏了类的实现细节。例如:

    class Students(val name: String, var score: Int) {

      def exam(s: Int) = score = s

      override def toString = name + "'s score is " + score + "."

      // 重写超类方法toString,注意关键字override 

    }

    object Students {

   def registerStu(name: String, score: Int) = new Students(name, score)

    }

将文件students.scala编译后,并在解释器里用“import Students._”导入单例对象后,就能这样使用:

    scala> import Students._

    import Students._

    scala> val stu = registerStu("Tim", 100)

    stu: Students = Tim's score is 100.

apply方法

类和伴生对象有一个特殊的方法名:apply,如果定义了这个方法,那么既可以显式调用:“对象.apply(参数)” ,也可以隐式调用:“对象(参数)”。隐式调用时,编译器会自动插入缺失的“.apply”。如果apply是无参方法,应该写出空括号(这和普通方法不同),否则无法隐式调用。无论是类还是单例对象,都能定义这样的apply方法。

通常,在伴生对象里定义名为apply的工厂方法,就能通过“伴生对象名(参数)”来构造一个对象。也常常在类里定义一个与类相关的、具有特定行为的apply方法,让使用者可以隐式调用,进而隐藏相应的实现细节。例如:

class Students(val name: String, var score: Int) {

      def apply(s: Int) = score = s

      def display() = println("Current score is " + score + ".")

      override def toString = name + "'s score is " + score + "."

    }

    object Students {

      def apply(name: String, score: Int) = new Students(name, score)

    }

    scala> val stu = Students("laozhang", 59) 

    stu: Students = laozhang's score is 59.

 //注意没有使用new Students("laozhang", 59),这是传统构造方法

    scala> stu(60)

    scala> stu.display

    Current score is 60.

其中“Students("laozhang", 59)”被翻译成“Students.apply("laozhang", 59)”,也就是调用了伴生对象里的工厂方法,所以构造了一个Students的对象并赋给变量stu。“stu(60)”被翻译成“stu.apply(60)” ,也就是更新了字段score的数据。

主函数

主函数是Scala程序唯一的入口,即程序是从主函数开始运行的。要提供这样的入口,则必须在某个单例对象里定义一个名为“main”的函数,而且该函数只有一个参数,类型为字符串数组Array[String],函数的返回类型是Unit(即空类型,注意不是UInt类型哈)。任何符合条件的单例对象都能成为程序的入口。例如:

//在students.scala文件中定义

    class Students(val name: String, var score: Int) {

      def apply(s: Int) = score = s

      def display() = println("Current score is " + score + ".")

      override def toString = name + "'s score is " + score + "."

    }

    object Students {

      def apply(name: String, score: Int) = new Students(name, score)

    }

//在 main.scala文件中定义

    object Start {

      def main(args: Array[String]) = {

        try {

          val score = args(1).toInt

          val s = Students(args(0), score)

          println(s.toString)

        } catch {

          .....

        } } }

使用命令“scalac students.scala main.scala”将两个文件编译后,就能用命令“scala Start 参数1 参数2”来运行程序。命令里的“Start”就是包含主函数main的单例对象的名字,后面可以输入若干个用空格间隔的参数。这些参数被打包成字符串数组供主函数使用,也就是代码里的args(0)、args(1)。

主函数的另外一种写法是让单例对象继承“App”特质(特质的概念引入是因为scala不支持多重超类的继承,为实现多重超类继承的某些特性而引入了特质概念),这样就只需在单例对象里编写主函数的函数体,无需显式定义main方法。例如:

    object Start extends App {

      try {

        var sum = 0

        for(arg

          sum += arg.toInt

        }

        println("sum = " + sum)

      } catch {

        ......

      }

    }

  将文件编译后使用方法同前

关于class的知识,做为硬件设计语言chisel3/spinalHDL,这些已经够用了。

照例结合spinalHDL给个小提示d0c0a7c130b26c177aa7e039923854c2.png

93cefbb4499cc3aa860df34e07294b58.png

以下视频简单演示伯克利分校的教学用riscv32处理器,其使用基于scala宿主语言的chisel3硬件设计语言编写:

更多信息请长期关注微信公众号:RiscV与IC设计

44b32c638be0efefc06d33491ac6aa38.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/258529.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu下安装FTP服务及使用(VSFTPD详细设置)(二)

vsftpd 作为一个主打安全的FTP服务器,有很多的选项设置。下面介绍了vsftpd的配置文件列表,而所有的配置都是基于vsftpd.conf这个配置文件 的。本文将提供完整的vsftpd.conf的中文说明。学习本文的内容将有助于你初步了解vsftpd的配置文件,但针…

crontab命令

前一天学习了 at 命令是针对仅运行一次的任务,循环运行的例行性计划任务,linux系统则是由 cron (crond) 这个系统服务来控制的。Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的。另外, 由于使用者自己也可以设置计划…

调查显示企业对 Linux 开发人才需求日渐增长

根据2014年Linux就业报告调查显示,随着Linux平台增长,Linux平台开发者就业需求较去年提升了7%,技术公司招聘Linux开发经历的人才需求达到 77%,这是来自Dice和Linux基金会共同对就业市场对Linux人才的全景式调查,调查访…

求圆和椭圆上任意角度的点的坐标

圆上任意角度的点的坐标 如上图,给定圆心(Cx,Cy),半径为R, 求θ\thetaθ对应的点的坐标? 此处θ\thetaθ是相对于水平轴的角度。 显然我们可以使用极坐标转换来求: {pxCxRcos(θ)pyCyRsin(θ)\left\{\begi…

BZOJ ac100题存档

不知不觉AC100题了,放眼望去好像都是水题。在这里就做一个存档吧(特别感谢各位大神尤其是云神http://hi.baidu.com/greencloud和丽洁姐http://wjmzbmr.com/的blog提供的题解) 代码什么的有时间在放。 1000: AB Problem 好吧,这也算…

Android Build.VERSION.SDK_INT兼容介绍

尽管Android向下兼容不好,但是一个程序还是可以在多个平台上跑的。向下兼容不好,接口改变,新的平台上不能用旧的API,旧的平台更不可能用新的API,不等于一个平台需要一个APK。可以在高SDK上开发,并在程序中作…

smtp中mailfrom是必须的吗_你是住在高层建筑中吗?这是你必须了解的

01PARTONE消防电梯和普通客梯的作用及特点1.消防电梯是在建筑物发生火灾时供消防人员进行灭火与救援使用且具有一定功能的电梯。因此,消防电梯具有较高的防火要求,其防火设计十分重要。2.普通电梯均不具备消防功能,发生火灾时禁止人们搭乘电梯…

c++ 函数的值传递,引用传递 和 引用返回的探索

2019独角兽企业重金招聘Python工程师标准>>> 前言 C的函数参数和返回分为按值传递和按引用传递,应用到类上面,会涉及到类的 赋值操作 复制函数 构造函数 析构函数 虽然java开发了两年,但对我而言c我还只是一个初学者.c还有很多陌生的特性需要自己亲自探索.这里用实际…

GCD的部分总结

GCD是基于C语言的底层API,用Block定义任务用起来非常灵活便捷. GCD的基本思想是就将操作放在队列中去执行 (1)操作使用Blocks定义(2)队列负责调度任务执行所在的线程以及具体的执行时间(3)队列的特点是先进先出(FIFO)的,新添加至对列的操作都会排在队尾关于多线程的…

将bgr彩色矩阵归一化到0-255之间 【RGB image normalization】

参考下面文章,可以使用normalize 函数。 https://blog.csdn.net/kuweicai/article/details/78988886 对于彩色rgb图像,也可以直接使用以上函数, 参考 https://devtalk.nvidia.com/default/topic/1042100/rgb-image-normalization/?offset8 image cv2…

多线程编程2-NSOperation

本文目录 前言一、NSInvocationOperation二、NSBlockOperation三、NSOperation的其他用法四、自定义NSOperation回到顶部前言 1.上一讲简单介绍了NSThread的使用,虽然也可以实现多线程编程,但是需要我们去管理线程的生命周期,还要考虑线程同步…

python图片显示英文字符_python如何解析字符串中出现的英文人名?

这里有四个例子,结果来自google scholarstr1 "Jakes, William C., and Donald C. Cox. Microwave mobile communications. Wiley-IEEE Press, 1994."str2 "Schlegel, David J., Douglas P. Finkbeiner, and Marc Davis. \"Maps of dust infra…

闪回表操作语法+使用闪回删除

闪回表操作语法 flashback table 【 schema.】 table_name to {【before drop 【rename to new_table_name】 】|【scn | timestamp 】】 expr 【enable | disable 】 triggers}: 参数说明: schema:用户模式 before drop:表示恢复到删除…

在现有的python环境下创建另一个python版本【亲测有效】

比如,你现在的python环境是3.6,想要使用一个3.5,可以在anaconda环境下使用conda虚拟环境。 第一步: conda create -n py35 【创建Py35虚拟环境】 第二步: 进入环境 activate py35 第三步: 升级或者降…

时间戳

我发现,我在本站公布的文章,url 最后一段都是 134 开头的一串数字。一開始并没在意,仅仅想当然以为是程序随机生成的。 昨天跟程序作者交谈的过程中无意问起,他说这是代表时间的。刚才訪问 GoAgent 项目时发现,其作者 …

欧拉函数 - HDU1286

欧拉函数的作用: 有[1,2.....n]这样一个集合,f(n)这个集合中与n互质的元素的个数。欧拉函数描述了一些列与这个f(n)有关的一些性质,如下: 1、令p为一个素数,n p ^ k,则 f(n) p ^ k - p ^ (k-1) 2、令m&…

其中一个页签慢_渭南提升一个大专学历的有效方法

渭南提升一个大专学历的有效方法,宏德教育,目前已形成以高等学历教育为特色王牌,职称考评、企业内训为辅助的强力优势品牌。渭南提升一个大专学历的有效方法, 获得发明专利或实用新型专利,且已实施取得效益。出版本专业…

《收集苹果》 动态规划入门

问题描写叙述 平面上有N*M个格子,每一个格子中放着一定数量的苹果。你从左上角的格子開始,每一步仅仅能向下走或是向右走,每次走到一个格子上就把格子里的苹果收集起来,这样下去,你最多能收集到多少个苹果。 输入&…

Xamarin XAML语言教程通过ProgressTo方法对进度条设置

2019独角兽企业重金招聘Python工程师标准>>> Xamarin XAML语言教程通过ProgressTo方法对进度条设置 在ProgressBar中定义了一个ProgressTo方法,此方法也可以用来对进度条当前的进行进行设置,ProgressTo与Progress属性的不同之处在于ProgressT…

Radar Installation

题目链接:http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id27586 题意: 在海岸线上摆放雷达并限定雷达覆盖半径d,再以海岸线为轴,给定海上岛屿坐标,求至少需要多少雷达可以覆盖所以岛屿,如…