鲁春利的工作笔记好记性不如烂笔头



1、扩展类    

    extends是Scala中实现继承的保留字;

class week extends month{......}

    week类继承了month类所有非私有成员;

    week类是month类的子类,month类是week类的超类;

    子类能重写超类的成员(字段、方法);

class week(val num : Int) extends month(val num : Int) {......}

    单例对象同样能从类中继承,与类的继承语法相同:object day extends week {......}

重写

    Scala中使用override保留字进行方法、字段重写

class week extends month {
    override def firstday () {......}
}

    override保留字声明其后的字段或方法是对超类的重写,也可以写在类定义参数中。

class week (override val lastday : String) extends month(val lastday : String)

    重新定义的字段和方法不可重写(override),方法不同(参数类型或个数)不可重写。

scala> class month {
     |     def secondary(m : String) {
     |         println("secondary is " + m);
     |     }
     | }
defined class month
 
scala>
 
scala> class week extends month {
     |     override def secondary(m : String) {    // 重写该方法
     |         println("secondary is " + m);
     |     }
     | }
defined class week

2、重写规则

   重写def

    用val:利用val能重写超类没有参数的方法;

    用def:子类方法与超类成员重名;

    用var:同时重写getter、setter方法,只重写getter方法报错。

   重写val:

    用val:子类的一个私有字段与超类的字段重名,getter方法重写超类的getter方法

   重写var:

    用var:当超类的var是抽象的才能被重写,否则超类的var都会被继承。

// 新建文件month.scala,内容为:
abstract class month {
    val zero : Int;
    val one = 25;         // 可在子类中用val重写
    var two = 15;         // 不可在子类中用var重写,因为不是抽象的
    var three : Int;
    def firstday ;        // 可在子类中用val重写
    def now ;             // 可在子类中用var重写
    def now_ ;        
    def lastday(m : Char) = {}    // 可在子类中用def重写
}

// 通过scalac命令编译该文件
D:\LuclAppServ\scala-SDK\source>scalac month.scala

D:\LuclAppServ\scala-SDK\source>javap.exe -private month.class
// 通过javap命令查看生成的文件
Compiled from "month.scala"
public abstract class month {
  // val变量且被初始化了
  private final int one;
  // var变量且被初始化了
  private int two;
  // val变量但为抽象的,直接生成了getter方法
  public abstract int zero();
  // 只有getter方法
  public int one();
  // 同时具有getter和setter方法(=被转义为$eq)
  public int two();
  public void two_$eq(int);
  // var变量但为抽象的,直接生成了getter和setter方法
  public abstract int three();
  public abstract void three_$eq(int);
  // 其他抽象的方法
  public abstract void firstday();
  public abstract void now();
  public abstract void now_();
  // 具有方法体,非抽象方法
  public void lastday(char);
  // 构造函数
  public month();
}

    通过IDE工具生成的week.scala代码如下

/**
 * @author lucl
 */
class week extends month {
  override val zero : Int = 10;

  override var three : Int = 3;

  override def firstday : Unit = {
    println("method of  firstday.");
  }

  override def now : Unit = {
    println("method of now.");
  }

  override def now_ : Unit = {
    println("method of now_.");
  }
  
}

object week {
  def main (args : Array[String]) {
    var w = new week();
    println(w.zero + "\t" + w.now);
  }
}

    查看生成的week子类代码

D:\LuclAppServ\workspaces\scala\scalaproj\bin>javap -private week.class
Compiled from "week.scala"
public class week extends month {
  private final int zero;
  private int three;
  
  public int zero();
  
  public int three();
  public void three_$eq(int);
  
  public void firstday();
  
  public void now();
  public void now_();
  
  public week();
  
  public static void main(java.lang.String[]);
}

D:\LuclAppServ\workspaces\scala\scalaproj\bin>javap -private week$.class
Compiled from "week.scala"
public final class week$ {
  public static final week$ MODULE$;
  public static {};
  public void main(java.lang.String[]);
  private week$();
}

说明:

    子类构造器运行在超类构造器之后,在超类的构造器调用的子类被重写后,返回值可能不正确。

/**
 * @author lucl
 */
class A {
  val num = 31;
  val days = new Array[Int](num);
  println("When invoke Class A the length of days is " + days.length + ".");
  def dayLength = {
    println("Class A : the length of days is " + days.length + ".")
  }
}

/**
 * @author lucl
 */
object B extends A {
  override val num = 7;
  
  def main (args : Array[String]) {
    dayLength;
    println("The finally value of num is " + num);
  }
}

    运行结果:

Scala2.11.7学习笔记(六)类和对象_第1张图片

    构造B对象前先执行A的构造器,num被初始化为31,days被初始化为Array数组;

    Array数组初始化时需要调用num,但num被子类重写了,但此时B的构造器还未被调用,num被初始化为0,days被初始化为长度为0的数组;

    A的构造器执行完毕,执行B的构造器,num被初始化为7,但此时A中days已初始化过不会再更新其初始化信息,days的数组长度为0。

    解决方法:

        将超类的val声明为final(不可再被子类重写);

        将超类的val声明为lazy;

        在子类中使用提前定义语法。

    a. final

        当A类中的num声明为final val num : Int = 7,则子类中不可再重写该字段;

    b. lazy

/**
 * @author lucl
 */
class A {
  lazy val num = 31;   // 通过lazy标注
  val days = new Array[Int](num);
  println("When invoke Class A the length of days is " + days.length + ".");
  def dayLength = {
    println("Class A : the length of days is " + days.length + ".")
  }
}

/**
 * @author lucl
 */
object B extends A {
  override lazy val num = 7;
  
  def main (args : Array[String]) {
    dayLength;
    println("The finally value of num is " + num);
  }
}

    运行结果

Scala2.11.7学习笔记(六)类和对象_第2张图片   

    c. 提前定义

    把需要提前定义的语句块放在extends与超类之间,并后接with保留字。
    class B extends {override val num = 7; } with A {......}

/**
 * @author lucl
 */
object B extends {override val num = 7; } with A {
  def main (args : Array[String]) {
    dayLength;
    println("The finally value of num is " + num);
  }
}

    执行结果:

    

3、抽象类

    不能被实例的类叫做抽象类,用保留字abstract标记;

    抽象类的某个或某几个成员没有被完整定义,这些没有被完整定义的成员为抽象字段或方法。

/**
 * @author lucl
 */
abstract class year {
  val name : Array[String];  // 抽象的val,带有一个抽象的getter方法
  var num : Int;             // 抽象的var,带有抽象的getter/setter方法
  def sign;                  // 没有方法体/函数体,是一个抽象方法
}

    只要类中有任意一个抽象成员,必须使用abstract标记;

    重写抽象方法、抽象字段不需要使用override保留字。

保护

    当一个类不希望被集成、拓展时,可在类声明前加上final保留字

final class year {......}

    当一个类的某些成员不希望被重写时,可在成员声明前加上final保留字

class year {final def sign {......}}


    当超类中的某些成员需要被子类继承,又不想对子类以外成员可见时,在成员声明前加上protected保留字;

    protected[this],将访问限定于当前对象(子类也不可访问),类似于private[this];

/**
 * @author lucl
 * 只要类中有一个成员是抽象的,则类就需要声明为抽象类
 */
abstract class Human {
  var name : String;      // 抽象字段
  def sleep() : String;   // 抽象方法
  def info (address : String);
}

/**
 * 
 */
abstract class Teacher (tname : String, age : Int) extends Human {
  println(tname + "\t" + age);
  
  override var name : String = tname;
  
  // 若将类声明为def sleep = "8 hours",在下面调用super.sleep()的位置会提示返回的为Unit
  override def sleep() : String = "8 hours.";
  
  def info (address : String);
}

/**
 * Worker继承Teacher时有两个参数name和age需要从Worker中传递参数
 * Worker的的参数名字需要与Teacher中一致,否则IDE会提示错误
 */
class Worker(tname : String, age : Int, salary : Int) extends Teacher (tname, age) {
  override def info (address : String) {
    println(tname + "' home is in " + address);
    println(tname + "'s age is " + age + ", earn ¥" + salary + ".");
  }
  
  override def toString = {
    tname + " is a worker, sleep " + super.sleep;
  }
}

object AbstractClassOps {
  def main(args : Array[String]) {
    val w = new Worker ("zhangsan", 25, 3000);
    w.info("BeiJing");
    println(w);
  }
}

/**
  zhangsan  25
  zhangsan' home is in BeiJing
  zhangsan's age is 25, earn ¥3000.
  zhangsan is a worker, sleep 8 hours.
*/


4、类的private属性

/**
 * @author lucl
 */
// 默认是public类型的
class Person {
  // age必须赋值,否则该类必须为abstract的
  private var age : Int = 0;  
  
  // 没有private修饰默认是public的
  // 无参的方法可以省略(),调用时可以省略();
  def increment() = age += 1;
  // 若声明的方法不带(),则调用时不可带()
  def current = age;
  
  // 类可以直接访问伴生对象的私有属性
  def speak = Person.sayHello;
}

class Student {
  /**
   * 声明为private类型的参数,只能通过当前类的函数来访问
   */
  private var privateAge : Int = 0;
  // 仅限于类的当前实例对象访问,其他传入的对象(如下面的other)将不可访问private[this]修饰的变量
  private [this] val name : String = "";
  
  // 自定义的getter/setter方法 ,用来操作私有字段 
  def age = privateAge;
  
  def age_ (newAge : Int) {
    privateAge = newAge;
  }
  
  /**
   * this对象的使用,表示调用该方法的当前对象
   */
  def sameStudent (other : Student) = {  
    // 上面的等号表示有返回结果,否则最后的true会提示:
    // a pure expression does nothing in statement position; 
    // you may be omitting necessary parentheses
    if (this.privateAge != other.privateAge) {
      false;
    }
    /**
     *  此时通过other将无法访问该name对象
     *  value name is not a member of Student
     */
    /*if (this.name != other.name) {
      false;
    }*/
    true;
  }
  
  // 在student中则不可通过Person类直接访问sayHello方法,提示:
  // method sayHello in object Person cannot be accessed in object Person
  // def speak = Person.sayHello;
  // 但可以通过如下方式访问:
  var p = new Person;
  p.speak;  // 这里会直接执行
}

/**
 * 对象为类的伴生对象,类为对象的伴生类
 */
object Person {
  def main (args : Array[String]) {
    var p = new Person();
    println("age is : " + p.age);    // 可以访问到私有属性
    p.increment;
    println("age is : " + p.age);
    println("current is : " + p.current);
    
    // 带有()则提示“Int does not take parameters”
    // p.current();
    
    val s = new Student();
    // variable privateAge in class Student cannot be accessed in Student
    // s.privateAge;
    println(s.age);
    s.age_(20);
    println(s.age);
  }
  private def sayHello () {
    println("Singleton object Person to say.");
  }
}

/**
 // 输出结果
  age is : 0
  age is : 1
  current is : 1
  Singleton object Person to say.
  0
  20
*/


5、嵌套类

    Scala允许任何语法结构中嵌套任何语法结构,因此能在类中定义类,类似于Java中的内部类。

    内部类中可以访问外部类的成员,利用外部类.this或指针实现。

scala> class HelloWorld {pointto =>
    val value2 = "HelloWorld";
    class HI {
        var value3 = HelloWorld.this.value2;
        var value4 = pointto.value2;
    }
} 

scala> var one = new HelloWorld;
one: HelloWorld = HelloWorld@4b134f33

scala> one.value2;
res52: String = HelloWorld

scala> class Family (val hname : String, val wname : String) {
    class Husband (var name : String) {
        println("Husband is : " + name);
    }
    
    class Wife (var name : String) {
        println("Wife is " + name);
    }
    
    def info () {
        var husband = new Husband(hname);
        var wife = new Wife(wname);
        println("This family holds husband " + husband.name + ", wife " + wife.name);
    }
}

scala> val f = new Family("Tom", "Jerry");
f: Family = Family@62de96e8

scala> f.info();
Husband is : Tom
Wife is Jerry
This family holds husband Tom, wife Jerry


Java内部类:内部类是属于外部类的;

/**
 * 
 * @author lucl
 * Java内部类示例
 * 说明:Java内部类是从属于外部类的
 */
public class JavaOuter {
    private String name;
    public JavaOuter (String name) {
        this.name = name;
    }
    /**
     * 内部类
     */
    // 一旦用static修饰内部类,它就变成静态内部类了。
    class Inner {
        private String name;
        public Inner (String name) {
            this.name = name;
        }
        public void foo (Inner inner) {
            System.out.println("\t" + JavaOuter.this.name + "\t" + inner.name);
        }
    }
    
    public void foo () {
        System.out.println("Outer : " + this.name);
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        JavaOuter outer1 = new JavaOuter("Spark");
        JavaOuter outer2 = new JavaOuter("Hadoop");
        
        JavaOuter.Inner inner1 = outer1.new Inner("scala");
        JavaOuter.Inner inner2 = outer2.new Inner("java");
        
        outer1.foo();
        inner1.foo(inner1);

        outer2.foo();
        inner2.foo(inner2);
        
        outer1.foo();
        inner1.foo(inner2);    // 在这里inner1可以调用inner2
    }
}

/**
     // 输出结果
    Outer : Spark
            Spark    scala
    Outer : Hadoop
            Hadoop    java
    Outer : Spark
            Spark    java
*/

Scala内部类:内部类是属于外部类的对象的;

/**
 * @author lucl
 * Scala内部类示例
 * 说明:Scala内部类是从属于外部类的对象的
 */
class ScalaOuter(val name : String) {outer =>
  /**
   * 内部类
   */
  class Inner (val name : String) {
    def foo (inner : Inner) {
      println("\t" + outer.name + "\t" + inner.name + ".");
    }
  }
  
  def foo () {
    println("Outer : " + outer.name);
  }
}

object OOPInScala {
    /**
   * main方法
   */
  def main (args : Array[String]) {
    val outer1 = new ScalaOuter ("Spark");
    val outer2 = new ScalaOuter ("Hadoop");
   
    val inner1 = new outer1.Inner("scala");
    val inner2 = new outer2.Inner("java");
    outer1.foo;
    inner1.foo(inner1);
    
    outer2.foo;
    inner2.foo(inner2);
    
    // 对于scala来说,这inner1调用foo方法传递参数时,是不可以将inner2作为参数传递的
    // IDE提示:type mismatch; found : outer2.Inner required: outer1.Inner
    // inner1.foo(inner2);
  }
}

/**
    // 输出结果
    Outer : Spark
          Spark scala.
    Outer : Hadoop
          Hadoop  java.
*/