从零开始学Scala系列(六)之类(Classes)和对象(Objects)

时间:2023-01-19 23:13:35

1. scala Classes类

scala中的类有以下几种元素(和java中的类有点类似):

类(class),属性(field),方法(method)

来个简单的例子

class Animal {
val name: String = "jack"
var age: Int = 1

def eat: Unit = println("eat...")

override def toString: String = s"[name=$name, age=$age]"
}



object Main {

def main(args: Array[String]): Unit = {
val animal = new Animal
println(animal.name + "::" + animal.age)
println(animal)
animal.eat
}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在上面的例子中 定义了一个Animal类,其中有两个属性 , 分别是val(相当于java中的final)类型的name 和var(普通变量)类型的age。还定义一个eat方法 
当方法体内只有一行代码时,可以省略大括号{}, 重写了toString方法 scala中所有的类都有一个顶层父类scala.Any和java中的java.lang.Object类似。下面贴一下Any的源码

abstract class Any {

def equals(that: Any): Boolean

def hashCode(): Int

def toString(): String

final def getClass(): Class[_] = sys.error("getClass")

final def ==(that: Any): Boolean = this equals that

final def != (that: Any): Boolean = !(this == that)

final def ##(): Int = sys.error("##")

final def isInstanceOf[T0]: Boolean = sys.error("isInstanceOf")

final def asInstanceOf[T0]: T0 = sys.error("asInstanceOf")
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

scala中的类可以带上类参数使得代码更简洁 可以改写一下Animal的代码

class Animal(nameParam: String, ageParam: Int) {
val name: String = nameParam
var age: Int = ageParam

def eat: Unit = println("eat...")

override def toString: String = s"[name=$name, age=$age]"
}

object Main {

def main(args: Array[String]): Unit = {
val animal = new Animal("jack", 2)
//val animal = new Animal(ageParam = 2, nameParam = "jack") 还可以指定参数名称赋值 很灵活
println(animal.name + "::" + animal.age)
println(animal)
animal.eat
}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

当然类参数还可以有默认值 就是当不传该参数的时候使用默认值 如下

class Animal(nameParam: String = "jack", ageParam: Int) {
val name: String = nameParam
var age: Int = ageParam

def eat: Unit = println("eat...")

override def toString: String = s"[name=$name, age=$age]"
}

object Main {

def main(args: Array[String]): Unit = {
val animal = new Animal(ageParam = 2)
//val animal = new Animal("rose", 2)
println(animal.name + "::" + animal.age)
println(animal)
animal.eat
}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

scala中还有一种单例对象,上面的object Main就是一个单例对象, scala中类还可以有一个伴生单例对象 而且它们彼此之间都可以相互访问自己的属性或方法 尽管可能是private的[说明: scala中没加修饰符的属性或方法都是public的], scala中的属性或方法都没有static类型的,要想定义此类型可在伴生对象中定义。如下代码所示:

class Animal(nameParam: String = "jack", ageParam: Int) {
val name: String = nameParam
var age: Int = ageParam

def eat: Unit = println("eat...")

override def toString: String = Animal.show(this)
}

object Animal {
val color: String = "black"
def sleep: Unit = println("sleep")
def show(animal: Animal): String = s"[name=${animal.name}, age=${animal.age}]"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

上面的object Animal就是类Animal伴生的单例对象,其中定义的属性和方法都是static的

2. scala 修饰符(modifiers)

scala中的修饰有private, protected两种,未加修饰符的就是public范围的。

1,private只能在本类中访问和java中private差不多,但是不同的是scala中的外部类不能访问内部类的private修饰的属性或方法, 但是也有手段来实现想java中的private的效果。来看代码

class Outer {
private val nameOuter = "outer"
class Inner {
private val nameInner = "inner"
}
//这里是无法编译通过的 报错
//Error:(27, 43) value nameInner in class Inner cannot be accessed in Outer.this.Inner
def inner(inner: Inner): String = inner.nameInner
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

使用private[Outer]指定修饰符范围:就是在Outer这个类的范围内就可以访问, 看代码:

class Outer {
private val nameOuter = "outer"
class Inner {
private[Outer] val nameInner = "inner"
}
//达到了和java中一样的效果 很灵活
def inner(inner: Inner): String = inner.nameInner
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2, protected 修饰符 
先来对比下scala和java中protected的范围

java中 protected修饰范围: 所有的子类和在同一包下的类 
scala中 protected修饰范围: 所有子类 
看来还是不同的 但是在scala中还是可以实现在java中的效果。

package pkg1 {
class Super {
protected val name = "super"
}
class Other {
//这里是编译不能通过的 因为Other不是Super的子类
def show(su: Super): String = su.name
}
}

package pkg2 {
import pkg1._
class Sub extends Super {
def show(su: Super): String = su.name
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

scala中不仅可以向java那样package在文件的头部, 也可以像上面的代码一样定义package 
比如

package pkg1
package pkg2{
//这里代码都是定义在pkg1.pkg2包里的 都是可以叠加的
}

import pkg1._ scala中import可以在任何地方出现 _下划线相当于java中的*
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

好了 再次回到protected修饰符的问题上 如何在scala中实现protected在java中的效果,看代码

package pkg1 {
class Super {
//可以在包pkg1任何范围内访问
protected[pkg1] val name = "super"
}
class Other {
//这里是编译不能通过的 因为Other不是Super的子类
def show(su: Super): String = su.name
}
}

package pkg2 {
import scala.date20170506.pkg1._
class Sub extends Super {
def show(su: Super): String = su.name
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

现在来详细看看scala中的修饰符的范围定义问题 只能说非常灵活

private[X] val f = "f"  X可以还是包名 类名 单例Object名 还可以是this
   
   
  • 1

包对象 package object 所有包内类 对象均可以访问该对象定义的属性和方法

package object pkg1 {
val name = "pkg1"
def show: Unit = name
}
  • 1
  • 2
  • 3
  • 4