Scala笔记整理(四):Scala面向对象—类详解2(继承相关)

[TOC]


单例

希望某个类只存在一个使用的对象,而不管有多少个调用者在使用它,就是单例的概念。

Java中的单例

package cn.xpleaf.single;
/**
 * 单例模式-饿汉式(在属性中先创建好对象,不管是否调用getInstance方法)
 * @author yeyonghao
 *
 */
public class User {
    private static User user = new User();

    private User(){}    //外界调用不到构造函数

    public static User getInstance(){

        return user;
    }
}

/**
 * 单例模式-懒汉式(属性中先定义对象为null,只有第一次调用getInstance方法的时候才创建对象)
 * @author yeyonghao
 *
 */
public class User2 {
    public static User2 user = null;

    private User2(){}   //外界调用不到构造函数

    public static User2 getInstance(){
        if(user == null){
            synchronized(User2.class){
                if(user == null) {
                    user = new User2();
                }
            }
        }
        return user;
    }
}

Scala中的单例

代码及说明如下:

package cn.xpleaf.bigdata.p3

/**
  * scala模拟单例
  */
object _01SingletonOps {
  def main(args: Array[String]): Unit = {
    val s1 = Singleton
    val s2 = Singleton
    s1.incrementAge()
    println("s1's age is: " + s1.getAge)
    println("s2's age is: " + s2.getAge)

    println("=================================")

    println("s1's price is: " + s1.getPhone.getPrice)
    println("s2's price is: " + s2.getPhone.getPrice)
    println("s1 incrementPrice...")
    s1.getPhone.incrementPrice(500.0)
    println("s1's price is: " + s1.getPhone.getPrice)
    println("s2's price is: " + s2.getPhone.getPrice)
  }
}

/**
  * 因为scala中没有静态字段之类,所以我们只能使用static去模拟静态
  * 当被定义为object的类型,其中的所有程序都可以当作java中的静态成员去对待
  * 直接可以通过类名.去调用
  */
object Singleton {
  private var age = 5
  private var phone:Phone = new Phone

  def incrementAge(): Unit = {
    age += 1
  }

  def getAge = age

  def getPhone = phone
}

class Phone {

  var price:Double = 1000.0

  def incrementPrice(addPrice:Double): Unit = {
    this.price += price
  }

  def getPrice = price

}

输出结果如下:

s1's age is: 6
s2's age is: 6
=================================
s1's price is: 1000.0
s2's price is: 1000.0
s1 incrementPrice...
s1's price is: 2000.0
s2's price is: 2000.0

伴生类和伴生对象

直接看下面的代码和说明:

package cn.xpleaf.bigdata.p3

/**
  * scala中的伴生对象和伴生类
  *   因为scala的语法结构中没有像java中的类,既可以拥有静态方法,也可以有非静态方法,
  *   所以就弄出这么一种结构,和该类class同名的object,并且该class与其同名的object必须要放在同一个.scala文件中
  *   那么我们把这个object称之为同名class的伴生对象
  *   把该class称之为同名object的伴生类
  *
  *   其实我们之前学习的数组Array,Map等的创建都用到了其对应的伴生对象的操作方式。
  *     既可以使用类来创建对象,也可以用伴生对象来创建对象
  *     二者唯一差别在于使用伴生类创建对象,必须要有new,而使用伴生对象来创建对象,直接省去new
  *
  *     在伴生类和伴生对象中可以互相访问对方的成员
  *         要想让伴生对象创建的对象也能访问伴生类中的公开成员,必须要在伴生对象中复写一个方法,apply
  *             返回值为该伴生类的对象(细节:在使用伴生对象创建对象的时候,必须要加(),apply方法也要加())
  *
  *     如果一个class有伴生的object对象,那么这个class有多少个构造函数,object就要定义多少个apply函数,
  *     apply参数列表同构造函数的参数列表
  */
object _02CompanionObjOps {

  def main(args: Array[String]): Unit = {
    var arr1 = new Array[Int](3)  // 伴生类方式
    var arr2 = Array[Int](4)      // 伴生对象方式
    /**
      * 所以可以知道,前面使用Array或者Map时,之所以用new行,不用new也行,是因为其使用了伴生类和伴生对象的思想
      */

    val p = new Person

    val p1 = new Person()
    println(p1.getAge)
    // 复杂构造
    val p2 = Person("张三", 14)
    println(p2.getAge)

    val p3 = Person("王五")
    println(p3.getAge)

    Person.show
    // p3.show  // 该方法被定义在object中,不能这样进行调用
  }

}

class Person {

  private var name:String = _
  private var age = 5

  def this(name:String, age:Int) {
    this()
    this.name = name
    this.age = age
  }

  def this(name:String) {
    this()
    this.name = name
  }

  def setAge(age:Int): Unit = {
    this.age = age
  }

  def getAge = age

}

object Person {

  def show() = println("person")  // 相当于模拟java中一个类的静态方法,不同的是,需要使用Person.show来调用,而不可以使用p3.show来调用

  def apply(): Person = new Person()

  def apply(name:String, age:Int): Person = new Person(name, age)

  def apply(name: String): Person = new Person(name)
}

输出结果如下:

5
14
5
person

object的继承和App

代码及说明如下:

package cn.xpleaf.bigdata.p3

/**
  * 对象的扩展
  *   只有方法的定义,没有方法体,该方法被称之为抽象方法
  *   定义了抽象方法的类,必须是抽象类
  *   scala中定义的抽象方法可以省略abstract关键字(实际上测试是不能加abstract的)
  *
  * object的另外一个特性,scala中程序的执行都必须要通过main
  * 当然有时候我们可以不用写这个main,只需要继承一个App即可
  */
object ObjectExtendsOps extends App {
  /*def main(args: Array[String]): Unit = {
    ObjAccountImpl.register("xpleaf")

  }*/
  if(args == null || args.length < 2) { // 继承App后,也是可以直接使用args对象的
    println(
      """参数要写正确哦. Usage: <inputPath string> <outputPath string>
        |inputPath:   输入文件路径
        |outputPath:  输出文件路径
      """.stripMargin)
    System.exit(-1)
  }
  println(args(0))
  println(args(1))
  ObjAccountImpl.register("xpleaf")
}

abstract class Account(name:String) {

  def register(name:String)

  def unRegister(name:String)

}

object ObjAccountImpl extends Account("张三") {

  override def register(name: String): Unit = {
    println(name + "注册账号")
  }

  override def unRegister(name: String): Unit = {
    println(name + "注销账号")
  }

}

/**
  * 如果是类继承的话,则可以加上构造器,但是object去继承时就不行
  */
class ObjAccountImpl1(name:String) extends Account(name) {

  override def register(name: String): Unit = {
    println(name + "注册账号")
  }

  override def unRegister(name: String): Unit = {
    println(name + "注销账号")
  }

}

输出结果如下:

inputPath
outputPath
xpleaf注册账号

object模拟枚举

Java中的枚举

使用枚举来实现一周,同时使用抽象类来模拟实现枚举,完整的案例如下:

package cn.xpleaf.bigdata.p2;

public class EnumOps {
    public static void main(String[] args) {
        /*TrafficLight green = TrafficLight.GREEN;
        TrafficLight red = TrafficLight.RED;
        TrafficLight yellow = TrafficLight.YELLOW;
        System.out.println(green);  // GREEN
        */
        Week mon1 = Week.MON;
        System.out.println(mon1);   // MON
        System.out.println(mon1.next());
        WeekDay mon2 = WeekDay.MON;
        System.out.println(mon2);   // MON
        System.out.println(mon2.next());
    }
}

/**
 * 创建java中的一个枚举
 * java中的枚举就是枚举类的一个在本类中定义的本类实例对象
 */
enum  TrafficLight {
    RED("RED"),
    YELLOW("YELLOW"),
    GREEN("GREEN");
    // private TrafficLight() {}
    String color;
    TrafficLight(String color) {
        this.color = color;
    }
}

/**
 * 枚举实现的一周
 */
enum Week {
    MON("MON") {
        @Override
        public Week next() {
            return Week.TUE;
        }
        @Override
        public Week previous() {
            return Week.SUN;
        }
    },
    TUE("TUE") {
        @Override
        public Week next() {
            return Week.WED;
        }

        @Override
        public Week previous() {
            return Week.MON;
        }
    },
    WED("WED") {
        @Override
        public Week next() {
            return Week.THR;
        }

        @Override
        public Week previous() {
            return Week.TUE;
        }
    },
    THR("THR") {
        @Override
        public Week next() {
            return Week.WED;
        }

        @Override
        public Week previous() {
            return Week.FRI;
        }
    },
    FRI("FRI") {
        @Override
        public Week next() {
            return Week.THR;
        }

        @Override
        public Week previous() {
            return Week.SAT;
        }
    },
    SAT("SAT") {
        @Override
        public Week next() {
            return Week.SUN;
        }

        @Override
        public Week previous() {
            return Week.FRI;
        }
    },
    SUN("SUN") {
        @Override
        public Week next() {
            return Week.MON;
        }

        @Override
        public Week previous() {
            return Week.SAT;
        }
    };
    String weekDay;
    Week(String weekDay) {
        this.weekDay = weekDay;
    }

    // 要想实现下面两个方法的效果,则上面的所有元素都需要重写
    public Week next() {
        return null;
    }

    public Week previous() {
        return null;
    }

}

/**
 * 用抽象类来模拟枚举
 */
abstract class WeekDay {
    public static WeekDay MON = new WeekDay("MON") {
        @Override
        public String toString() {
            return this.weekDay;
        }

        WeekDay next() {
            return TUE;
        }

        WeekDay previous() {
            return SUN;
        }
    };

    public static WeekDay TUE = new WeekDay("TUE") {
        @Override
        public String toString() {
            return this.weekDay;
        }

        WeekDay next() {
            return WED;
        }

        WeekDay previous() {
            return TUE;
        }
    };

    public static WeekDay WED = new WeekDay("WEB") {
        @Override
        public String toString() {
            return this.weekDay;
        }

        WeekDay next() {
            return THU;
        }

        WeekDay previous() {
            return TUE;
        }
    };

    public static WeekDay THU = new WeekDay("THU") {
        @Override
        public String toString() {
            return this.weekDay;
        }

        WeekDay next() {
            return FRI;
        }

        WeekDay previous() {
            return WED;
        }
    };

    public static WeekDay FRI = new WeekDay("FRI") {
        @Override
        public String toString() {
            return this.weekDay;
        }

        WeekDay next() {
            return SAT;
        }

        WeekDay previous() {
            return THU;
        }
    };

    public static WeekDay SAT = new WeekDay("SAT") {
        @Override
        public String toString() {
            return this.weekDay;
        }

        WeekDay next() {
            return SUN;
        }

        WeekDay previous() {
            return FRI;
        }
    };

    public static WeekDay SUN = new WeekDay("SUN") {
        @Override
        public String toString() {
            return this.weekDay;
        }

        WeekDay next() {
            return MON;
        }

        WeekDay previous() {
            return SAT;
        }
    };

    String weekDay;
    private WeekDay(String weekDay) {
        this.weekDay = weekDay;
    }

    @Override
    public String toString() {
        return weekDay;
    }

    abstract WeekDay next();
    abstract WeekDay previous();

}

scala中的枚举

案例及说明如下:

package cn.xpleaf.bigdata.p3

/**
  * 使用object去模拟枚举
  *   枚举,其实说白了就是将一个有限的集合中所有的可能都罗列出来,不用再人为进行输入,就通过
  *   枚举类来完成,规范我们的编程
  *   在java中有专门的枚举这种类型,是和class、interface是并列的,是enum
  *
  *   scala中枚举的类是类名.Value,是因为要继承一个Enumeration的特质,而不像java可以直接定义类的结构enum
  *
  */
object _04EnumOps {
  def main(args: Array[String]): Unit = {
    val red = TrafficLight.RED  // red的类型为:TrafficLight.Value
    println(red)  // RED
    val red1 = TrafficLight.RED1  // 就是本object的引用,如这里,red1的"类型"为:TrafficLight.type,即对TrafficLight的引用
    println(red1) // TrafficLight
  }
}

object TrafficLight extends Enumeration {
//  val RED = TrafficLight;
//  val YELLOW = TrafficLight;
//  val GREEN = TrafficLight;
  val RED, YELLOW, GREEN = Value
  val RED1 = TrafficLight

}

类的继承

直接看代码和注释。

实例1

package cn.xpleaf.bigdata.p3.extendz

/**
  * scala中的继承
  *     描述类与类之间的关系,其中继承是一种,反映的是is a这种关系,一个类是另外一种类型
  *     子类通过关键字extends继承了父类的field和method,同时可以自定义对应的field和method
  *
  *     需要注意的是,如果父类中的成员,包括父类被final修饰了,则无法被继承,覆盖
  *     同样的,如果父类成员被private修饰之后,子类也无法继承或是覆盖
  *
  *     当有继承体系的时候,在用子类构造器创建对象的时候,先调用父类的构造器,
  *
  *     子类调用父类成员,如果子类没有重名的,直接成员名,如果有重名的,需要使用super.  --->有重名的,在父类中定义方法,通过super.方法名调用即可
  *     scala中是无法直接调用父类构造器,是间接调用父类构造器,只能是主构造器调用父类的构造器,辅助构造器是不能直接调用父类的构造器
  *     (实际测试是:无法使用super的方式访问父类的字段,可以这样访问父类的方法,
  另外也没有办法调用父类构造器,不管在哪里,当然在实例中的,在定义子类的时候,那种方式也算是
  子类主构造器对父类构造器的调用,因为在子类构造器中,确实是没有办法使用super的方式对父类构造器进行调用
  *       当然现在学得还十分浅显,后面肯定会深究一下
  *     )
  */
object _01ExtendsOps {
  def main(args: Array[String]): Unit = {
    val stu = new Student("001A")
    stu.show()
  }
}

class Person {

  println("----person primary constructor----")

  var name:String = "张三"
  var age:Int = 0

  def this(name:String, age:Int) {
    this()
    this.name = name
    this.age = age
  }

  /*final*/ def show() = println(s"name is $name, and age is $age")
}

class Student extends Person {

  println("----student primary constructor----")

//  override var name:String = "小红" // 父类中有该字段,不能再重新定义,加上override,编译正常,但运行时也会有异常

  private var cardID:String = _

  def this(cardID:String) {
    this()
    this.cardID = cardID
    println("----student 辅助 constructor----")
  }

  /*
  为啥被final修饰的成员,子类无法覆盖?
  因为final代表的就是常量,代表的就是最终的量,所以不能进行覆盖
  */
  override def show(): Unit = {
    super.show()
    println("name is " + name + s", and age is $age, cardID is $cardID")
  }
}

输出结果如下:

----person primary constructor----
----student primary constructor----
----student 辅助 constructor----
name is 张三, and age is 0
name is 张三, and age is 0, cardID is 001A

实例2

package cn.xpleaf.bigdata.p3.extendz

/**
  * scala继承
  *     scala向父类的构造器传递参数
  * java中多态,
  *   父类引用指向子类对象
  *   Fu zi = new zi();
  * scala中多态的体现
  *   val zi:Fu = new zi()
  *   编译看=的左边,运行要看=的右侧
  */
object _02ExtendsOps {
  def main(args: Array[String]): Unit = {
    val coder:Worker = new Coder("xpleaf", 23, "scala")
    coder.show()
  }
}

class Worker(name:String, age:Int) {

  var language:String = _

  def show(): Unit = {
    println(s"name is $name,, and age is $age")
  }

}

class Coder(name:String, age:Int, language:String) extends Worker(name, age) {

  def this() {
    this("程序员", 18, "Java")
  }

  override def show(): Unit = {
    println(s"name is $name, and age is $age, language is $language")
  }

}

输出结果如下:

name is xpleaf, and age is 23, language is scala

总结

1、一个类有一个主构造器和任意数量的辅助构造器,而每个辅助构造器都必须以对先前定义的辅助构造器或主构造器的调用开始。这样做带来的后果是,辅助构造器永远都不可能直接调用超类的构造器。

2、子类的辅助构造器最终都会调用主构造器,只有主构造器可以调用超类的构造器。主构造器是和类定义交织在一起的,调用超类构造器的方式也同样交织在一起。

3、注意!如果是父类中接收的参数,比如name和age,子类中接收时,就不要用任何val或var来修饰了,否则会认为是子类要覆盖父类的field

class Person (val name:String, val age:Int)
class Employee(name:String, age:Int, val address:String) extends Person(name,age){
    def this(name:String){
        this(name,0,"")
    }

    def this(age:Int){
        this("garry",age,"")
    }
}

类型检查和类型转换

测试代码如下:

package cn.xpleaf.bigdata.p3.extendz

/**
  * 在java中判断对象A是否为B这种类型
  * if(A instanceOf B){
  *
  * }
  * 常见在于equals里面来进行操作
  *   类型检查使用instanceOf,类型转换使用的强制类型转换--->Java
  *   而在scala中:
  *   类型检查:isInstanceOf
  *   类型转换:asInstanceOf
  *
  *   这里书写的这种isInstanceOf也可以使用scala中模式匹配来操作
  */
object _03ExtendsOps {
  def main(args: Array[String]): Unit = {
    // 比较两个工程师是否为同一个人,依据就是姓名是否相等
    val e1 = new Engineer("lisi", 12345)
    val e2 = new Engineer("lisi", 12345)
    println(e1.equals(e2))  // true
    println(e1.getName)     // lisi
    println("======================================")
    // 类型检查的第二种方式,使用模式匹配来进行操作
    val e3:Employee = new Engineer("wangwu", 3457)
    val e4:Employee = new ProductManager

    // 使用模式匹配来确定要执行的方法
    e3 match {
      case e:Engineer => {
        e.code()
      }
      case e:ProductManager => {
        e.pm()
      }
    }
  }
}

class Employee {
  private var name:String = _
  private var salary:Float = _

  def this(name:String, salary:Float) {
    this()
    this.name = name
    this.salary = salary
  }

  def getName = name

  def show: Unit = {
    println(s"$name ======> $salary")
  }
}

class Engineer(name:String, salary:Float) extends Employee {
  private var age:Int = _

  override def getName = name

  def code() = println("Engineer正在写代码...")

  override def equals(obj: scala.Any): Boolean = {
    if(!obj.isInstanceOf[Engineer]) { // 类型检查
      false
    } else {
      // 类型转换
      val o = obj.asInstanceOf[Engineer]
      if(this.getName.equalsIgnoreCase(o.getName)) {
        true
      } else {
        false
      }
    }
  }
}

class ProductManager extends Employee {
  def pm() = println("pm正在赶需求...")
}

输出结果如下:

true
lisi
======================================
Engineer正在写代码...

protected关键字

测试代码如下:

package cn.xpleaf.bigdata.p3.extendz

/**
  * scala继承之protected修饰符
  *     1.被protected修饰的成员,子类可以直接访问,其它子类实例可以访问
  *     2.如果在protected后面加上[this]则表示只能在当前子类中访问,不能在其它子类实例中进行访问,即可能在子类的定义中使用,子类对象无法调用
  *     3.如果protected或者private后面[]中的内容不是this,而是其它包名,则说明该字段或者该方法
  *     只能在对应的包目录或者子包中目录结构中能够进行调用
  */
object _04ExtendsOps {
  def main(args: Array[String]): Unit = {
    val wc = new Dog
    val fg = new Dog
    wc.makeDifference(fg)
  }
}

class Animal {

  private[this] var privateThis:Int = 4
  private[extendz] var privatePackage:Int = 4

  protected var leg:Int = 4

  protected[this] var eyes:Int = 2

  protected[extendz] var others:Int = 2

}

class Dog extends Animal {
  private var color:String = "yellow"

  def getEyes:Int = eyes

  def makeDifference(dog:Dog): Unit = {
    println(s"this dog's leg is $leg, that dog's leg is ${dog.leg}") // 可以直接使用dog.leg,因为protected修饰
    println(s"this dogs's eyes is $eyes, that dog's eyes is ${dog.getEyes}")  // 不可以直接使用dog.eyes,因为protected[this]修饰,只能本类或子类使用
    println(s"this dog's others is $others, that dog's others is ${dog.others}")  // others被protected[extendz]修饰,extendz包下的都可以访问
    // println(privateThis) // 不能访问
    println(privatePackage + "\t" + dog.privatePackage) // 可以访问
    // 可以看到scala中的修饰符跟Java的还是有区别的
  }
}

输出结果如下:

this dog's leg is 4, that dog's leg is 4
this dogs's eyes is 2, that dog's eyes is 2
this dog's others is 2, that dog's others is 2
4   4

匿名内部类

实例

测试代码如下:

package cn.xpleaf.bigdata.p3.extendz

/**
  * scala中的匿名内部类
  *   第一:匿名内部类是一个内部类
  *   第二:这个内部类没有名字
  *       没有定义的过程,只在程序运行时临时定义
  *
  *   往往匿名内部类用的时候,该内部类对应的类一般是一个抽象,同时该类中抽象方法一般不超过3个
  *
  *   对于如果只调用一次的类而言,我们一般就可以使用匿名内部类的方式去调用,没有必要创建一个类的定义
  */
object _05AnonymousInnerOps {
  def main(args: Array[String]): Unit = {
    val cat = new Cat
    cat.fetchMouse()
    //
    val jqCat = new Cat() {

      def code() = println(s"$kind\5$color\t正在写代码...")

    }
    jqCat.fetchMouse()
    jqCat.code()

    //
    val outer:Outer = new Outer {
      override def show1(): Unit = {
        println("impl show1")
      }

      override def show2(): Unit = {
        println("impl show2")
      }
    }
    outer.show1()
    outer.show2()

  }
}

class Cat {
  var color:String = "黑猫"
  val eyes:Int = 2
  var name:String = _
  var kind:String = "波斯"

  def fetchMouse() = println(s"$kind, color's $color")
}

abstract class Outer {

  def show1()

  def show2()

}

输出结果如下:

波斯, color's 黑猫
波斯, color's 黑猫
波斯 黑猫   正在写代码...
impl show1
impl show2

总结

1、在Scala中,匿名子类是非常常见,而且非常强大,因为Spark源码中也大量使用了这种匿名子类。

2、匿名子类,也就是说,可以定义一个类的没有名称的子类,并直接创建其对象,然后将对象的引用赋予一个变量。之后甚至可以将该匿名子类的对象传递给其他函数

3、

class Person(val name:String){

  def greeting = "Glad to see you " + name

}

val person = newPerson ("Garry") {

      override def greeting ="Greet4ings, Earthling! My name is . " + name

}

println(person.greeting)

从技术上讲,这将会创建出一个结构类型的对象。该类型标记为Person{ def greeting:String }

4、也可以用这个类型作为参数类型的定义:

def greetingMeet (p: Person { def greeting:String}) {

    println(p.name + " says: " + p.greeting)

}

抽象

抽象类

1、和Java一样,你可以用abstract关键字来标记不能被实例化的类,通常这是因为它的某个或某几个方法没有被完整定义

2、

abstract classPerson(val name: String) {

  def greeting: String // 没有方法体,这是一个抽象方法

}

在Scala中,不像Java,你不需要对抽象方法使用abstract关键字,你只是省去其方法体。但和Java一样,如果某个类至少存在一个抽象方法,则该类必须声明为abstract

3、在子类中重写超类的抽象方法时,你不需要使用override关键字。

abstract classPerson(val name: String) {

    def greeting: String // 没有方法体,这是一个抽象方法

}

classEmployee(name:String) extends Person(name){

    def greeting:String = {

        println("name=> " + name)

        "Hello => " + name

    }

} 

val employee = newEmployee("Garry")

println(employee.greeting)

抽象字段

1、除了抽象方法外,类还可以拥有抽象字段。抽象字段就是一个没有初始值的字段。

abstract classPerson {

    val personID : Int // 没有初始化,这是一个带有抽象的getter方法的抽象字段

    var personName : String // 另一个抽象字段,带有抽象的getter和setter方法

}

该类为personID和personName字段定义了抽象的getter方法,为name字段定义了抽象的setter方法。

2、生成的Java类并不带字段,具体的子类必须提供具体的字段,例如:

abstract classPerson {

    val personID : Int // 没有初始化,这是一个带有抽象的getter方法的抽象字段

    var personName : String // 另一个抽象字段,带有抽象的getter和setter方法

}

class Employee(val personID: Int) extendsPerson { // 子类有具体的id属性

    println("personID=> " + personID)

    var personName = "Garry" // 和具体的name属性

}

val employee  = new Employee(100)

println(employee.personName)

实例

测试代码如下:

package cn.xpleaf.bigdata.p3.extendz

/**
  * scala中的抽象
  *     抽象的字段,就是只有字段定义,没有字段初始化的字段
  *     当一个类中的字段为抽象字段的话,该类要么abstract,要么实现继承一个含有对应抽象字段的类
  *
  *     多态的应用时:
  *     抽象的字段被定义为val,则只能读,不能写(一旦初始化,不能修改)
  *         子类也只能覆盖为val
  *     抽象字段被定义为var,是普通变量
  *         子类可以覆盖为var,也可以覆盖为val
  *         但是如果覆盖为val的话,只能读不能修改
  */
object _06AbstractOps {
  def main(args: Array[String]): Unit = {
    val p:AbsPerson = new ZiPerson
    println("age: " + p.age)
    println("name: " + p.name)
  }
}

abstract class AbsPerson {
  val age:Int
  var name:String
}

class ZiPerson extends AbsPerson {
  val age: Int = 10
  var name: String = "张三"
}

输出结果如下:

age: 10
name: 张三

特质

测试代码如下:

package cn.xpleaf.bigdata.p3.extendz

/**
  * scala特质说明
  *     scala中同样像java一样不能实现多继承,只能通过多实现来弥补,同时多实现,要比多继承灵活的多
  * 在java中的实现,我们称之为接口interface,使用关键字implement
  * 在scala中,我们称之为特质trait,使用关键字extends
  * 如果在java中实现的是多个接口,接口之间使用","隔开
  * 如果在scala中继承的是多个特质,特质之间使用with隔开
  *
  *     trait是一个比较特殊的结构
  *         既可以有抽象方法,也可以有非抽象方法,如果trait中的方法都是抽象的,我们就可以将其看作java的接口
  *     当我们扩展的多个特质,拥有相同的方法的时候,默认只会调用最右面一个特质的方法
  *
  *     要求:
  *       如果继承的多个特质含有相同方法,我们想执行每一个方法,怎么解决?
  *         则在继承的特质后面追加一个super.方法即可
  *         注意,当前根特质中的方法必须不能为抽象
  *       当扩展的特质是多个的话,执行顺序是从右往左
  */
object _07TraitOps {
  def main(args: Array[String]): Unit = {
    val cLog:Log = new ConsoleLog
    cLog.log("日志信息")
    println("==================")
    val mLog:MainLog = new MainLog
    mLog.log("info")
  }
}

/**
  * 定义了一个特质log,有一个抽象的方法log
  */
trait Log {

  def log(msg:String): Unit = {

  }

}

class ConsoleLog extends Log {
  override def log(msg: String): Unit = {
    println(s"将 ${msg} 输出到Console")
  }
}

trait FileLog extends Log {
  override def log(msg: String): Unit = {
    println(s"将 ${msg} 输出到File")
    super.log(msg)
  }
}

trait FlumeLog extends Log {
  override def log(msg: String): Unit = {
    println(s"将 ${msg} 采集到Flume")
    super.log(msg)
  }
}

class MainLog extends FileLog with FlumeLog {
  override def log(msg: String): Unit = {
    super.log(msg)
  }
}

输出结果如下:

将 日志信息 输出到Console
==================
将 info 采集到Flume
将 info 输出到File

猜你喜欢

转载自blog.51cto.com/xpleaf/2105884