每天半小时掌握Scala(day 04)

Posted 爱上终身学习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每天半小时掌握Scala(day 04)相关的知识,希望对你有一定的参考价值。

===(class)和对象(object)===

 

(class)和构造器

类的定义形式如下

        class MyClass(a: Int, b: Int) {

            println(a.toString)

         }

scala类也可以带有类参数类参数可以直接在类的主体中使用没必要定义字段然后把构造器的参数赋值到字段里但需要注意的是类参数仅仅是个参数而已不是字段如果你需要在别的地方使用就必须定义字段不过还有一种称为参数化字段的定义形式可以简化字段的定义如下

class MyClass(val a: Int, val b: Int) {

    println(a.toString)

}

以上代码中多了val声明作用是在定义类参数的同时定义类字段不过它们使用相同的名字罢了类参数同样可以使用var作前缀还可以使用private、protected、override修饰等等。scala编译器会收集类参数并创造出带同样的参数的类的主构造器并将类内部任何既不是字段也不是方法定义的代码编译至主构造器中除了主构造器,scala也可以有辅助构造器辅助构造器的定义形式为def this(…)。每个辅助构造器都以“this(…)”的形式开头以调用本类中的其他构造器被调用的构造器可以是主构造器也可以是源文件中早于调用构造器定义的其他辅助构造器其结果是对scala构造器的调用终将导致对主构造器的调用因此主构造器是类的唯一入口点scala只有主构造器可以调用超类的构造器

你可以在类参数列表之前加上private关键字使类的主构造器私有私有的主构造器只能被类本身以及伴生对象访问

可以使用require方法来为构造器的参数加上先决条件如果不满足要求的话,require会抛出异常阻止对象的创建

如果类的主体为空那么可以省略花括号

 

访问级别控制

公有scala的默认访问级别因此如果你想使成员公有就不要指定任何访问修饰符公有的成员可以在任何地方被访问

私有类似于java,即在之前加上private不同的是scala中外部类不可以访问内部类的私有成员

保护类似于java,即在之前加上protected不同的是scala中同一个包中的其他类不能访问被保护的成员

scala里的访问修饰符可以通过使用限定词强调格式为private[X]protected[X]的修饰符表示直到X”的私有或保护这里X指代某个所属的包类或单例对象

scala还有一种比private更严格的访问修饰符private[this]private[this]标记的定义仅能在包含了定义的同一个对象中被访问这种限制被称为对象私有这可以保证成员不被同一个类中的其他对象访问

对于私有或者保护访问来说,scala的访问规则给予了伴生对象和类一些特权伴生对象可以访问所有它的伴生类的私有成员保护成员反过来也成立

 

成员(类型字段和方法):

    scala中也可以定义类型成员类型成员以关键字type声明通过使用类型成员你可以为类型定义别名

    scala里字段和方法属于相同的命名空间,scala禁止在同一个类里用同样的名称定义字段和方法尽管java允许这样做

 

gettersetter:

    在scala类的每个非私有的var成员变量都隐含定义了gettersetter方法但是它们的命名并没有沿袭java的约定,var变量xgetter方法命名为x”,它的setter方法命名为x_=”。你也可以在需要的时候自行定义相应的gettersetter方法此时你还可以不定义关联的字段自行定义setter的好处之一就是你可以进行赋值的合法性检查

    如果你将scala字段标注为@BeanProperty,scala编译器会自动额外添加符合JavaBeans规范的形如getXxx/setXxxgettersetter方法这样的话就方便了javascala的互操作

 

样本类

    带有case修饰符的类称为样本类(case class),这种修饰符可以让scala编译器自动为你的类添加一些句法上的便捷设定以便用于模式匹配,scala编译器自动添加的句法如下

帮你实现一个该类的伴生对象并在伴生对象中提供apply方法让你不用new关键字就能构造出相应的对象

在伴生对象中提供unapply方法让模式匹配可以工作

样本类参数列表中的所有参数隐式地获得了val前缀因此它们被当作字段维护

添加toString、hashCode、equals、copy自然实现

 

封闭类

    带有sealed修饰符的类称为封闭类(sealed class),封闭类除了类定义所在的文件之外不能再添加任何新的子类这对于模式匹配来说是非常有用的因为这意味着你仅需要关心你已经知道的子类即可这还意味你可以获得更好的编译器帮助

 

单例对象(singleton object):

scala没有静态方法不过它有类似的特性叫做单例对象object关键字定义:main函数也应该在object中定义任何拥有合适签名的main方法的单例对象都可以用来作为程序的入口点)。定义单例对象并不代表定义了类因此你不可以使用它来new对象当单例对象与某个类共享同一个名称时它就被称为这个类的伴生对象(companion object)类和它的伴生对象必须定义在同一个源文件里类被称为这个单例对象的伴生类类和它的伴生对象可以互相访问其私有成员不与伴生类共享名称的单例对象被称为独立对象(standalone object)

applyupdate:

scala通常使用类似函数调用的语法当使用小括号传递变量给对象时,scala都将其转换为apply方法的调用当然前提是这个类型实际定义过apply方法比如s是一个字符串那么s(i)就相当于c++中的s[i]以及java中的s.charAt(i),实际上 s(i)  s.apply(i) 的简写形式类似地,BigInt(“123”) 就是 BigInt.apply(“123”) 的简写形式这个语句使用伴生对象BigIntapply方法产生一个新的BigInt对象不需要使用new。与此相似的是当对带有括号并包含一到若干参数的变量赋值时编译器将使用对象的update方法对括号里的参数索引值和等号右边的对象执行调用arr(0) = “hello”将转换为arr.update(0, “hello”)。

类和单例对象之间的差别单例对象不带参数而类可以因为单例对象不是用new关键字实例化的所以没机会传递给它实例化参数单例对象在第一次被访问的时候才会被初始化当你实例化一个对象时如果使用了new则是用类实例化对象new则是用伴生对象生成新对象同时要注意的是我们可以在类或(单例)对象中嵌套定义其他的类和(单例)对象

 

对象相等性

java不同的是scala,“==!=可以直接用来比较对象的相等性,“==”“!=”方法会去调用equals方法因此一般情况下你需要覆盖equals方法如果要判断引用是否相等可以使用eqne

在使用具有哈希结构的容器类库时我们需要同时覆盖hashCodeequals方法但是实现一个正确的hashCodeequals方法是比较困难的一件事情你需要考虑的问题和细节很多可以参见java总结中的相应部分另外正如样本类部分所讲的那样一旦一个类被声明为样本类那么scala编译器就会自动添加正确的符合要求的hashCodeequals方法

 

===抽象类和抽象成员===

 

    与java相似,scalaabstract声明的类是抽象类抽象类不可以被实例化

    在scala抽象类和特质中的方法字段和类型都可以是抽象的示例如下

trait MyAbstract {

    type T // 抽象类型

    def transform(x: T): T // 抽象方法

    val initial: T // 抽象val

    var current: T // 抽象var

}

抽象方法

抽象方法不需要也不允许abstract修饰符一个方法只要是没有实现没有等号或方法体),它就是抽象的

抽象类型

scala中的类型成员也可以是抽象的抽象类型并不是说某个类或特质是抽象的(特质本身就是抽象的),抽象类型永远都是某个类或特质的成员

抽象字段

没有初始化的valvar成员是抽象的此时你需要指定其类型抽象字段有时会扮演类似于超类的参数这样的角色这对于特质来说尤其重要因为特质缺少能够用来传递参数的构造器因此参数化特质的方式就是通过在子类中实现抽象字段完成如对于以下特质

trait MyAbstract {

    val test: Int

    println(test)

    def show() {

        println(test)

    }

}

    你可以使用如下匿名类语法创建继承自该特质的匿名类的实例如下

new MyAbstract {

    val test = 1

}.show()

你可以通过以上方式参数化特质但是你会发现这和“new 类名(参数列表)”参数化一个类实例还是有区别的因为你看到了对于test变量的两次println(第一次在特质主体中第二次是由于调用了方法show),输出了两个不同的值(第一次是0,第二次是1)。这主要是由于超类会在子类之前进行初始化而超类抽象成员在子类中的具体实现的初始化是在子类中进行的为了解决这个问题你可以使用预初始化字段懒值

 

预初始化字段

    预初始化字段可以让你在初始化超类之前初始化子类的字段预初始化字段用于对象或有名称的子类时形式如下

class B extends {

    val a = 1

} with A

    预初始化字段用于匿名类时形式如下

new {

    val a = 1

} with A

    需要注意的是由于预初始化的字段在超类构造器调用之前被初始化因此它们的初始化器不能引用正在被构造的对象

 

懒值

加上lazy修饰符的val变量称为懒值懒值右侧的表达式将直到该懒值第一次被使用的时候才计算如果懒值的初始化不会产生副作用那么懒值定义的顺序就不用多加考虑因为初始化是按需的



每天向你推送最实用的干货,欢迎关注~

点击这里查看往期精彩内容:


以上是关于每天半小时掌握Scala(day 04)的主要内容,如果未能解决你的问题,请参考以下文章

每天半小时掌握Scala(day 01)

每天半小时掌握Scala(day 03)

5个能够改变一生的自学网站,每天半小时效果看的见!

linux学习之路每天半小时

每天半小时下班的公交车上,把html5学完了...

自动化快速上手--Python--元组--每天半小时