博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Scala学习(六)对象
阅读量:7153 次
发布时间:2019-06-29

本文共 3377 字,大约阅读时间需要 11 分钟。

hot3.png

Scala中对象本质上可以拥有类的所有特质,甚至可以扩展其他类或特质。但有一个例外:你不能提供构造器参数

1.单例对象

在Scala中没有静态方法和静态字段,但是我们可以用object语法来达到相同的目的。对象定义了某个类的单个实例:

object Util{    private val str = "Hello World";    def out(){        println(str);     }}

在解释器中运行:

image2018-10-6_14-37-10.png?version=1&modificationDate=1538807830000&api=v2

可以看到,其效果与Java中的静态方法一样。

对象的构造器在该对象第一次被使用时调用。如果一个对象从未被使用,器构造器也不会执行。

注:Scala的编译器可以编译txt格式的scala代码。

那么,为什么object可以有这样类似于Java中的静态的效果呢?我们来看下Util对象编译后的class文件:

image2018-10-6_14-48-31.png?version=1&modificationDate=1538808513000&api=v2

我们将Util对象的代码进行编译,之后生成了两个class文件Util.class 和 Util$.class ,我们分别查看他们都是什么:

首先Util.class中我们看到有一个静态方法out

image2018-10-6_14-50-21.png?version=1&modificationDate=1538808622000&api=v2

Util$.class是一个单例模式的类,里面有我们Scala的Util对象的str成员,由于str为val,所以Util$.class这里只有str的读方法str()

image2018-10-6_14-51-24.png?version=1&modificationDate=1538808685000&api=v2

我们来反编译Util.class与Util$.class,这样可以更加直观

image2018-10-6_14-57-20.png?version=1&modificationDate=1538809041000&api=v2

这里Util.class的out()方法调用Util$.class的out()方法

image2018-10-6_14-55-59.png?version=1&modificationDate=1538808960000&api=v2

注:Scala的对象编译完成后,会生成对应的Java class。其中方法都是静态方法,非静态成员对应到生成的单例类中。

说明:因为Scala对象是单例的这一特性,因此在程序中任何需要使用单例的地方,你都可以用Scala对象实现。

通过上面一系列的分析,我们清楚了Scala如何通过对象,来实现Java中静态变量的效果,因为他的底层就是通过java的静态方法实现的。

2.伴生对象

在Java或C++中,你通常会用到既有静态放大又有普通方法的类。在Scala中,你可以通过类和与类同名的“伴生对象”来达到相同的目的。

伴生对象要求类名和object名称相同,并且在同一个Scala文件中定义。

我们来定义一个伴生对象:

class Car{    def stop(){        println("stop...");    }}object Car{    def run(){        println("run...")    }}

接下来我们分别执行类的方法和伴生对象的方法,对比差异。

image2018-10-6_15-16-32.png?version=1&modificationDate=1538810193000&api=v2

我们看到伴生对象中的run()方法可以不用实例化直接运行(那当然,他是静态的方法),二类中的stop()方法不能直接运行,需要先实例化才能运行。

接下来我们看下上面的代码编译后是什么结构。

image2018-10-6_15-21-42.png?version=1&modificationDate=1538810504000&api=v2

同样生成了Car.class与Car$.class,我们分别用javap命令看下这两个class的结构

image2018-10-6_15-20-35.png?version=1&modificationDate=1538810436000&api=v2

在Car.class中出现了两个方法,一个是静态方法run(),也就是我们在伴生对象中定义的方法。另一个是普通方法stop(),使我们在类中定义的方法

image2018-10-6_15-21-6.png?version=1&modificationDate=1538810467000&api=v2

Car$.class还是一个单例,由于我们的代码中没有定义普通成员,因此这里很干净。

反编译class文件:

image2018-10-6_15-30-8.png?version=1&modificationDate=1538811009000&api=v2

image2018-10-6_15-30-20.png?version=1&modificationDate=1538811021000&api=v2

说明:伴生对象可以被访问,但并不在作用于当中。举例来说,类中必须通过 对象名.方法 的方式去访问,而不是直接调用对象名。

3.扩展类或特质的对象

给我的感觉就是Java中的继承。下面来用代码演示对象扩展类(对象一样):

image2018-10-6_20-13-14.png?version=1&modificationDate=1538827994648&api=v2

我们定义了一个抽象的Animal类 和一个 继承了Animal类的对象Dog,Dog重写了Animal类中的未定义方法。

上面的演示我们得知:

  1. Scala中抽象类的定义和Java一样用abstract关键字,继承和Java一样用extends关键字
  2. 和Java一样抽象类不能被实例化

4.apply方法

我们通常会在对象中定义一个apply方法。当余姚如下表达式时,apply方法就会被调用:

Object(参数1...参数N)

通常,这样一个Apply方法返回的是一个伴生对象。

举例来说,Array数组对象定义了apply方法,让我们可以以这样的表达式返回一个数组对象

Array(1,2,3,4,5)

为什么不用关键字呢?因为对于嵌套表达式而言省去new关键字会方便很多,例如:

Array(Array(1,2),Array(2,3))

注意:Array(100)和new Array(100)很容易搞混。前一个表达式时调用了apply方法,返回了一个单元素(整数100)的Array[Int];而第二个表达式调用的是构造器this(100),结果是Array[Nothing],包含100个null元素

image2018-10-6_20-29-45.png?version=1&modificationDate=1538828984849&api=v2

apply方法需要定义在对象中,如果类需要定义apply方法,则需要定义在它的伴生对象中。

5.应用程序对象

每个Scala程序都必须从一个对象的main方法开始,这个方法的类型为Array[String] => Unit:

object Hello{    def main(args: Array[String])  {        println("Hello World")    }}

由于Java中main是静态的方法,因此Scala中的main方法必须定义在对象中或伴生对象中。

将上面的代码编译后执行:

image2018-10-6_20-58-15.png?version=1&modificationDate=1538830695377&api=v2

或者直接运行scala文件

image2018-10-6_21-0-19.png?version=1&modificationDate=1538830819639&api=v2

Scala中除了使用main方法外,还有另一种方式实现相同的功能,通过扩展App特质,将代码写入构造方法体内:

object Hello extends App{    println("Hello World")}

再次编译效果相同

image2018-10-6_21-7-42.png?version=1&modificationDate=1538831262207&api=v2

6.枚举

和Java或C++不同,Scala中并没有枚举类型。不过标准类库提供了一个Enumeration助手类,可以用于产出枚举。

我们可以通过下面3种方式构造枚举:

//统一构建object MyEnum extends Enumeration{    val Red,Yellow,Green = Value}//与上面一样,只不过分开写object MyEnum extends Enumeration{    val Red = Value;    val Yellow = Value;    val Green = Value;}//Value方法是一个重载方法,可以通过它定义枚举值得id,对应值等object MyEnum extends Enumeration{    val Red = Value(0,"red");//设置枚举的id和对应值    val Yellow = Value(10);//设置枚举的id,值默认与成员同名    val Green = Value("gre");//设置枚举对应值,id默认前一个枚举的id+1}

定义完成后,我们就可以用MyEnum.Red、MyEnum.Yellow来引用枚举值了。

注:枚举的类型是MyEnum.Value而不是MyEnum,后者是握有这些值的对象。

有人推荐引入一个类型别名,不过这样需要与import一起用才会有意义:

object MyEnum extends Enumeration{    type MyEnum = Value    val Red,Yellow,Green = Value}import MyEnum._def doWhat(color:MyEnum) = {    if(color == Red) "stop"    else "go"}

枚举值得ID可以通过方法id返回,名称通过toString方法返回。

通过values方法返回枚举值的集合:

for(c <- MyEnum.values) println(c.toString + " " + c.id)

image2018-10-6_21-39-25.png?version=1&modificationDate=1538833165391&api=v2

最后你可以通过ID或者名称来进行查找定位枚举值对象:

MyEnum(0)MyEnum.withName("Red")

image2018-10-6_21-41-21.png?version=1&modificationDate=1538833280910&api=v2

转载于:https://my.oschina.net/u/3687664/blog/2223240

你可能感兴趣的文章
我的J2EE学习历程
查看>>
在表格中显示数据2
查看>>
memory_get_usage函数
查看>>
MongoDB 关系-1对多的嵌入式
查看>>
SpringBoot中Mail的使用
查看>>
python中使用pycharm2018工具安装Autopep8
查看>>
file-diff.py——文件对比,输出HTML文档
查看>>
关于access开发的小感
查看>>
PPP认证详解
查看>>
iCloud4_Implementing the Detail View Controller
查看>>
5分钟深入 Hadoop 容错
查看>>
memcached的key太长引起报错
查看>>
oracle监听
查看>>
rsync+inotify实时同步
查看>>
CentOS系统管理_用户和用户组
查看>>
Win7开启ACHI模式蓝屏的解决办法
查看>>
secure crt常用快捷键
查看>>
【转】应用 printf 语句格式化输出字符
查看>>
C#中yield return用法分析
查看>>
GreenPlum数据的装载与卸载之copy命令的使用
查看>>