Scala OOP 基础二

2017/08/06 Scala

Scala OOP 基础知识二

引用和值类型

Java 语法为 JVM 实现数据的方式提供了模型。首先,它提供了一组原生类型:short 、int 、long 、float 、double 、boolean 、char、byte 和关键字 void,在scala中对应于Short 、Int 、Long 、Float 、Double 、Boolean 、Char 、Byte 和 Unit 类型,这些称为值类型 。值类型的“实例”总是用字面量 来创建,如 1 ,3.14 ,true 。 它们被存储在堆栈中,或为了获得更好的性能,被存储于 CPU 寄存器。其他的类型被称为引用类型,因为它们的所有实例都分配在堆中,引用这些实例的变量实际上指向了堆中的相应位置。

Scala 必须符合 JVM 的规则,但 Scala 做了改进,使得原生类型和引用类型的区别更明显。所有引用类型都是 AnyRef 的子类型。所有值类型均为 AnyVal 的子类 型。AnyRef 是 Any 的子类型,而 Any 是 Scala 类型层次的根类型,Any 仅有这两个直接的子类型。Java 的根类型 Object 实际上更接近 Scala 的 AnyRef 。引用类型用 new 关键字来创建,如果构造函数没有参数(在有的语言中称为“默认构造函数”),可以去掉后面的括号。

scala1

在Scala中用带 apply 方法的对象创建引用类型的实例是很常见的做法,apply 方法起到工厂的作用(这种方法必须在内部调用 new 或对应的字面量语 法)。标记有 case 类的类型会自动生成伴随对象及其 apply 方法,所有可以不使用new 关键字就可以生成实例。

scala最大限度的减少了封装过的引用类型。在scala中可以声明值类型的参数化集合,如 List[Int] 。在Java 中必须使用包装过的类型,如 List<Integer> ,使得代码不能更加高效的重用。

Unit 的行为与包含零个元素的元组很像,而元素个数为零的元组写为 () 。Unit 这个名字来自数学里的乘法,任意值与单位值相乘, 总是返回其原始值。这个单位值对于数字来说,就是 1。对于加法来说,0 为其单位值。

Value class

scala 2.10 引入value class 和universal trait 机制,能够避免将类型分配到堆上,从而使值类型的封装具有同值类型一样的良好性能。有效的value class 有如下的要求:

  1. 有且仅有一个参数
  2. 参数不能是value class本身。
  3. value class 参数化的时候,不能使用@specialized标记。
  4. 没有定义默认构造函数以外的其它构造函数。
  5. 只定方法,没有其他的val和var 变量。
  6. 不能重载equals和hashCode方法。
  7. 没有嵌套的特征,类或者是对象。
  8. 不可以被继承。
  9. 只能继承自通用特征。
  10. value class 必须是对象可引用的一个顶级类型或对象的一个成员。(scala中可以定义不能被引用的类型,后文会细讲)。
    value class 如果不适合要求编译器是不会让代码通过的。

universal trait 特征如下:

  1. 只能从Any派生。
  2. 只定义方法。
  3. 没有对自身初始化。

value class 提供了一个低开销的技术,用来定义扩展方法,定义有意义的领域名称。 下面给出一个示例。

class Cash(val value:Float) extends AnyVal {
  override def toString = "$%.2f".format(value)
}

构造函数

构造函数名称是this ,举例如下:

Object Address(street:String,city:String,zip:String){
	def this(zip:String)=this("[Unknown]",Address.zipToCity(zip),zip)
}

Object Address{
  def zipToCity(zip:String)="Any"
  def zipToState(zip:String)="CN"
}

case class Persion(name :String,age :Option[Int],address:Option[Address]){ //default constructor
	def this(name:String) = this(name,None,None)
}

构造函数调用是有序的,引用的其它构造函数必须是先定义的。编译器不会会case class默认构造函数外的函数自动生成apply,所以在使用自己定义的类型的时候,需要使用new关键字,好可以重载apply手动重载方法,可以不使用new 关键字。

case class Person3(
  name:String,
  age :Option[Int]=None,
  address :Option[Address]=None)
}

Object Person3{
  def apply(name :String):Person3 = new Person3(name)
  
  def apply(name:String,age:Int):Person3 = new Person3(name,Some(age))
  
  def apply(name:String,age:Int,address:Address):Person3=new Person3(name,Some(age),Some(address))
}

val p=Person3("Ethan") //call Object Person3.apply("Ethan")

类的字段

一个类的默认构造函数上面加上var,val,则这个字段成为类的实例的字段。对于case 修饰的类,val 是默认的。(此处scala自动生成了setter和getter字段)。

class A(var a:String)
//equals
class A(a:String){
  private var _val:String=s 
  def val:String=_val
  def val_=(newValue:String):Unit=_val=newValue//
}

val_这个方法名是一般规范,会允许使用的时候去掉下划线,使用中缀表达式。即a.val是对val函数的调用。

一元方法

这个很简单,举例就能说明:

case class Complex(real:Double,img:Double){
  def unary_- :Complex = Complex(-real,img)
  def -(other: Complex) = Complex(real-other.real,img-other.img)
}

然后我们有了减号和负号。

Show Disqus Comments

Search

    Table of Contents