Scala教程-3 Scala中的类

3 篇文章 0 订阅
2 篇文章 0 订阅

3. Scala中的类

Any: 抽象类,是所有类型的父类;

Nothing: 是所有类型的子类,Nothing没有对象,但是可以用来定义类型 ,当方法抛出异常或者返回一个类型犹豫不决的时候,可以用Nothing;

AnyRef: 是所有引用类型的基类。除了值类型,所有类型都继承自AnyRef 。

AnyVal: 是所有值类型的基类,它描述的是值,而不是代表一个对象。它包括9个AnyVal子类型:

scala.Double
scala.Float
scala.Long
scala.Int
scala.Char
scala.Short
scala.byte
scala.Unit (非数值类型)
scala.Boolean (非数值类型)

注意: Scala中,一个源文件可以有很多类,并且都是public级别的。

使用了private[this]定义后的内容就无法外部使用了,这起到了非常好的保护作用

3.1 Scala的构造函数

3.1.1 主构造器

class Person(name:String,val age:Int){
    println("This is the primary constructor")
}

特点:

  1. 主构造器(默认构造器)直接在类名称后面,主构造器中的参数会被编译成类的字段;
  2. 主构造器执行,会执行类中的所有不包含在方法体中的语句(此时类中所有没放在方法和代码块中的代码,都是构造器中的代码);
  3. 如果在主构造器函数的参数没有用val或者var去声明变量,那此时变量是private[this]级别的,只能够被类内部访问,且默认是val;

注意: 无参构造器可以省略后面的括号

3.1.2 附属构造器

class Person(var name:String,val age:Int){
    println("This is the primary constructor!")
    var gender:String = _
	def this(name:String,age:Int,gender:String){
        this(name,age)
        this.gender = gender
	}    
}

特征:

  1. 附属构造器是用this来声明的

  2. 附属构造器必须调用主构造器或者其它附属构造器

3.2 class继承

class Person(val name:String,var age:Int){
    println("This is the primary constructor of person")
    
    val school = "BEIJING"
    def sleep = "8 hours"
    override def toString="I am a person"
}

class worker(name:String,age:Int,val salary:Long) extends Person(name,age){
	println("This is the subClass of Person,Primary constructor of Worker")
  	override val school = "Spark"
  	override def toString = "I am a Worker!"+super.sleep
}

注意: 子类在继承父类时,必须继承父类的主构造器;

子类构造函数中的参数必须同时也初始化父类主构造器中的参数;

子类重写抽象父类的抽象字段和抽象方法时,可以写override,也可以不写;但是覆盖非抽象字段和放法时,必须写override;

3.3 多重继承和Trait

3.3.1 基础用法

1) Scala中,Trait是一种特殊概念。首先,Trait可以被作为接口来使用,此时Trait与Java接口非常类似。也可以作为抽象类使用,在Trait中定义抽象方法,其与抽象类中的抽象方法一样,不给出方法的具体实现,trait还可以定义具体方法,也有的说法是Trait的功能混入了类。

2) Scala中,Trait可以定义具体字段,继承Trait的类就自动获取了Trait中定义的字段。 Trait中,可以定义抽象字段,而Trait中的具体方法可以基于抽象字段来编写,但继承Trait的类,必须覆盖抽象字段,提供具体的值。
注意: 这里与继承Class不同,如果继承Class获取的字段,实际定义在父类中,而继承Trait获取的字段,就直接添加到了类中。

3) 所有的Java接口都可以作为Scala的特质来使用,与Java一样Scala只能有一个超类,但可以拥有任意数量的特质。

4) 在解读时,extends后面的作为一个整体,然后类extends这个整体,如果一个类只扩展了特质,那么第一个关键字为extends(with关键字不能单独使用,必须和extends关键字一起使用)

注意: 在Scala中,无论继承类还是继承Trait都是用extends关键字,interface在Scala中不是关键字;Java使用extends继承类使用implement实现接口;

5) Scala中,类继承Trait后,必须实现其中的抽象方法,实现时不需要使用override关键字,而具体方法被重写需要加override,同时Scala同Java一样,不支持类多继承,但支持多重继承Trait,使用with关键字即可。

6) Trait本身不能构造,只能构造trait的实现类,具体的实现类构造时,trait也会被构造,构造顺序从左到右

class Human{
  println("human")
}

trait Teacher extends Human{
  println("Teacher")
  // 定义抽象方法
  def teach
}

trait PianoPlayer extends Human{
  println("PianoPlayer")
  def palyPiano = {
    println("I'm playing piano")
  }
}

class PianoTeacher extends Human with Teacher with PianoPlayer{
  override def teach = {
    println("I'm training students")
  }
}

/
trait Action{
  def doAction
}

trait TBeforeAfter extends Action{
  abstract override def doAction{
    println("Initialization")
    super.doAction
    println("Destory")
  }
}

class Work extends Action{
  override def doAction = println("Working...")
}


class TraitUse {
  
}

object TraitUse extends App{
  val t1 = new PianoTeacher
  t1.palyPiano
  t1.teach
  
  println("*********************")
  val t2 = new Human with Teacher with PianoPlayer{
    def teach = {
      println("I'm training students")
    }
  }
  t2.palyPiano
  t2.teach
  
  println("*********************")
  val work = new Work with TBeforeAfter
  work.doAction
}
运行结果:
运行结果为:
human
Teacher
PianoPlayer
I'm playing piano
I'm training students
*********************
human
Teacher
PianoPlayer
I'm playing piano
I'm training students
*********************
Initialization
Working...
Destory

3.3.2 中级用法

1) 为实例混入Trait:在创建某个类对象时,可以指定该对象混入某个Trait,这样只有这个对象混入了该Trait,其他实例对象则没有

2) Trait调用链:在Scala中,支持让类继承多个Trait,依次调用多个Trait中的同一个方法,首先会从最右边的Trait的方法开始执行,然后依次向左执行,形成一个调用条。这个相当于设计模式中的责任链模式的一种具体实现依赖, 最后都执行super方法。

3) Trait中,可以覆盖父Trait的抽象方法,但是覆盖时,如果使用了super方法的代码是无法通过编译的。如果要通过编译,就得给子Trait的方法加上abstract override修饰。

trait Logger {
  def log(message: String)
}

trait MyLogger extends Logger {
  abstract override def log(message: String) {
    super.log(message)
  }
}

3.3.3 高级用法

1) 混合使用Trait具体方法和抽象:可以让具体方法依赖于抽象方法,抽象方法放到继承Trait的类中实现

trait Valid {
    def getName:String
    def valid:Boolean = {
        getName = "lubin"
    }
}

class Person(val name:String) extends Valid{
    println(valid)
    def getName = name
}

scala>val person = new Person("lubin)
true
scala>val james = new Person("james)
false

2) Trait构造机制:在Scala中,Trait是有构造代码的,就是Trait中不包含在任何方法中的代码,而继承了Trait的构造机制如下:
a)父类的构造函数
b)Trait的构造代码执行,多个Trait从左向右依次执行
c)构造Trait时会先构造父Trait,如果多个Trait继承同一个父Trait,则父Trait只会构造一次
d)所有trait构造结束之后,子类的构造函数执行

class Person{
    println("This is the Person constructor!")
}

trait Logger{
    println("This is the Logger constructor!")
}

trait MyLogger extends Logger{
    println("This is the MyLogger constructor!")
}

trait TimeLogger extends Logger{
	println("This is the TimeLogger constructor!")
}

class Student extends Person with MyLogger with TimeLogger{
    println("This is the Student constructor!")
}

scala> val stu = new Student
This is the Person constructor!
This is the Logger constructor!
This is the MyLogger constructor!
This is the TimeLogger constructor!
This is the Student constructor!

3) Trait 字段的初始化:在Scala中,Trait是没有接收字段的构造函数,这是Trait与Class的唯一区别,如果要求trait对字段进行初始化,只能使用Scala中的一种高级特性,即提前定义或使用Lazy Value。

4) Trait还继承class:在Scala中,Trait可以继承class,这个class就会成为所有继承该trait的类的父类

5) 没有具体方法的Trait会被编译成接口

trait Trait1{
}
trait Trait2{
  def m1:Int
  def m2(arg:Int):Int
}
// 编译后
public interface Trait1
{
}

public interface Trait2
{
    public abstract int m1();
    public abstract int m2(int i);
}

6) 有具体方法的trait会被编译成两个class

trait Trait3{
  def m3(arg:Int):Int = 1
}
//编译之后得到两个class文件:Trait3.class和Trait3$class.class,查看编译后的文件可得Trait3.class是一个接口
public interface Trait3
{
    public abstract int m3(int i);
}
// 方法实现在Trait$class.class文件中
public abstract class Trait3$class
{

    public static int m3(Trait3 $this, int arg)
    {
        return 1;
    }

    public static void $init$(Trait3 trait3)
    {
    }
}

注意: trait会被编译为等价的接口;

如果trait有具体方法,则这些方法会被复制到相应的$class中,并且有下面两处变化

​ ①类为abstract,方法为static

​ ②trait的实例被插入到参数列表的最前端

7) 在构造对象时,可以混入该对象所具有的特质的子类,那么在调用该对象所含特质的方法时,将会执行子类的方法。

trait Logged {
  def log(msg: String) { }
}
 
class SavingsAccount extends Account with Logged {
  def withdraw(amount: Double) {
    if (amount > balance) log("Insufficient funds")
    else ...
  }
  ...
}
 
trait ConsoleLogger extends Logged {
  override def log(msg: String) { println(msg) }
}
 
val acct = new SavingsAccount with ConsoleLogger
// 当调用acct的log方法时,执行的是ConsoleLogger特质的log方法

3.4 伴生对象和apply方法

3.4.1 伴生对象object

1) object对象中的成员变量和成员方法都是静态变量和静态方法。

2) 只有在第一次使用object时,其中的方法才会被执行(java中静态成员和方法在类加载的时候就会被执行)

3) 它拥有一个隐形的构造器,无参。

4) 如果有一个与object同名的class类则称object为伴生对象,class为伴生类。一个伴生类可以访问其伴生对象的所有成员(包括私有成员),伴生对象通常可作为相对应类的静态成员封装区域(可以看做一个集合)

package lubin

/**
 * @author lenovo
 */
class ApplyTest {
  def apply()={
    println("I love Spark so much!")
  }
  
  def haveATry{
    println(" Have a try on apply!")
  }
}

object ApplyTest{
  def apply()={
    println("I love Scala so much!")
    new ApplyTest    //创建ApplyTest类对象
  }
}

object Application{
  def main(args:Array[String]){
    val arr = Array(1,2,3,4)    //通过Array类伴生对象中的apply方法来创建集合
    val a = ApplyTest()    //通过类ApplyTest的伴生对象中的apply方法来创建ApplyTest对象。
a.	haveATry
println("***********************")
    val b = new ApplyTest
    b.haveATry
    println(b())    //调用类的apply方法,b后面必须加括号否则就是打印地址了
  }
}
程序运行结果为:
I love Scala so much!
 Have a try on apply!
***********************
 Have a try on apply!
I love Spark so much!

注意: 用类名再加上括号,则可以调用object中的apply方法,对象加括号则调用的是class的apply方法

3.4.2 apply方法

class UsageOfApply {
    
}

class ApplyTest {
    def test{
        println("test")
    }
}

object ApplyTest{
    def apply()=new ApplyTest
    def static{
		println("I am a static method!")
	}
}

object UsageOfApply extends App {
    val a = ApplyTest()
    a.test()
}
结果:test

注意: 当使用”val a=ApplyTest()”时会导致伴生类对象中的apply方法的调用并返回该方法调用的值,也就是ApplyTest的实例化对象。

在apply方法中还可以实现单例的控制。

3.5 包的定义和使用

3.5.1 包的定义

使用关键字 package,后面是包名,包名可以是链式结构(嵌套结构) :包名1.包名2。

package lubin

package spark.navigation {
    class Navigation
    package tests {
      //在spark.navigation.tests包里的类
      //IDE会自动给类添加一个包名,如上面的包lubin。
      class NavigatorSuite
    }
}

package hadoop {
  package navigation {
    class Navigator
  }
  
  package launch {
class Booster {
  //包中类的实例化。
      val nav = new navigation.Navigator
    }
  }
}

3.5.2 包对象

//定义了一个object类型的package,此包为people
package object people {
  val defaultName = "Scala"
}

//在people下的所有类都可以用上面的成员。
package people {
  class people {
    var name = defaultName
  }
}

3.5.3 Scala中隐式引用的包

//Scala中自动引入,下划线相当于java中的*,表示所有内容。
import java.lang._
import scala._
import Predef._

//如果不想引入包下面的所有的成员,可以用大括号的方式。
import java.awt.{Color,Font}
//如果java中存在某个类与scala中某个类冲突,可以对java中
//的类使用别名,如HashMap的别名为JavaHashMap
import java.util.{HashMap => JavaHashMap}
//如果不想使用某个类,则使用占位符,代替。
//java中也有个StringBuilder,如果此时你使用StringBuilder
//则此时,使用的是java中的StringBuilder。
import scala.{StringBuilder => _} // 表示隐藏掉scala.StringBuilder类

3.5.4 包、类中的访问权限

// Navigation包下的类不可访问com.spark包下的;com.spark包下的不可访问com包下的
package com.spark {
  package navigation  {
    //private[spark]表示此类的访问权限为spark包下的全体成员。
    private[spark] class Navigator {
      protected[navigation] def useStarChart(){}
      class legOfJourney {
        private[Navigator] val distance = 100
      }
      //限制此变量使用权限为对象。
      private[this] var speed = 200
    }
  }
  
  package launch {
    import navigation._
    object Vehicle {
      private[launch] val guide = new Navigator 
    }
  }
}

//伴生类可以访问伴生对象,伴生对象可以访问伴生类,(包括私有的)
class PackageOps_Advanced {
  import PackageOps_Advanced.power
  
  private def canMakeItTrue = power > 10001
}

object PackageOps_Advanced{
  private def power = 10000
  
  def makeItTrue(p:PackageOps_Advanced):Boolean={
    val result = p.canMakeItTrue
    result
  }
}

注意: 在Scala对象中,定义的字段为private,则会自动生成私有的getXXX,setXXX方法。如果没有说明为private,则该字段会自动变为private,但是会生成public的getXXX,setXXX方法,外部能够访问该字段(实质是通过public类型的getXXX,setXXX方法)。

Scala中的方法默认是public的,加上private就为私有的。

3.5.5 类中getXXX和setXXX方法

class Person{
	private var myName = “Flink”
	// 自定义get方法
	def name  = this.myName
	// 自定义set方法
	def name_ =(newName:String){
    	myName = newName
   	 println(“Hello: ”+myName)
	}
}

注意: 如果val john = new Person 则john.name调用的是john.name方法;john.name=”Spark”调用的是john.name_方法。

Scala类中的属性的set方法为属性名后面加下划线;

Scala类中var定义的变量会生成getter和setter方法,val定义的变量只会生成getter方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值