zoukankan      html  css  js  c++  java
  • scala 学习笔记(04) OOP(上)主从构造器/私有属性/伴生对象(单例静态类)/apply方法/嵌套类

    一、主从构造器

    java中构造函数没有主、从之分,只有构造器重载,但在scala中,每个类都有一个主构造器,在定义class时,如果啥也没写,默认有一个xxx()的主构造器

    class Person {
    
      var name: String = _
    
      /**
       * 从构造器
       * @param name
       */
      def this(name: String) = {
        this //注意:从构造器,必须先调用主构造器
        this.name = name;
      }
    
      override def toString = {
        "name:" + name
      }
    
    }
    

    上面的这段代码,如果将生成的class反编译成java来看的话,可能更容易理解:

    public class Person
    {
      private String name;
    
      public String name()
      {
        return this.name; } 
      public void name_$eq(String x$1) { this.name = x$1; }
    
    
      public String toString()
      {
        return new StringBuilder().append("name:").append(name()).toString();
      }
    
      public Person()
      {
      }
    
      public Person(String name)
      {
        this();
        name_$eq(name);
      }
    }
    

    从反编译结果看,生成了二个构造函数,一个是默认的Person(),另一个是Person(String name)。

    Scala是一个崇尚简约之美的语言,在定义Class时,可以将属性声明、带参构造器,一并全解决了,所以刚才这段代码,"等效"于下面这样:

    class Person(var name: String) {
    
      override def toString = {
        "name:" + name
      }
    
    }
    

     注意第一行,此时的主构造器不再是默认的无参构造器,而是Person(var name:String),它有二层意思:一是定义了一个带参数的构造器,二是由于name前加了var,说明name:String不仅仅是构造器的参数,还是类Person的一个属性成员,不过这个版本与第一个版本还是有些差别的,如果用JD-GUI反编译查看的话,会发现默认的无参构造器消失了

    public class Person
    {
      private String name;
    
      public String name()
      {
        return this.name; } 
      public void name_$eq(String x$1) { this.name = x$1; }
    
      public String toString() {
        return new StringBuilder().append("name:").append(name()).toString();
      }
    
      public Person(String name)
      {
      }
    }
    

     Person的使用示例如下:

    object App {
    
      def main(args: Array[String]) {
        val p: Person = new Person("jimmy")
        println(p.toString)
        p.name = "jimmy.yang"
        println(p.toString)
      }
    
    }
    

    主构造器上,还可以增加限定词private来修饰,比如下面这样:

    class Person private(var name: String) {
    
      var age: Int = 0;
    
      def this(age: Int, name: String) = {
        this(name)
        this.age = age;
      }
    
      override def toString = {
        "name:" + name + ",age:" + age
      }
    
    }
    

    这样Person类的使用方,就只能使用从属构造器this(age:Int,name:String)了。

    二、私有属性(private property)

    将前面的Person改一下,将年龄Age设置成私有成员

    package yjmyzz
    
    
    class Person private(var name: String) {
    
      println("这是主构造器的可执行语句,我是" + name) //这一行在new Person时会执行
    
      /**
       * 定义一个私有成员
       */
      private var _age: Int = 0;
    
      def age = _age
    
    
      def this(age: Int, name: String) = {
        this(name)
        this._age = age;
      }
    
      def isOlder(another: Person) = {
        this._age > another._age //注意:这里可直接访问另一个Person的私有成员_age
      }
    
      override def toString = {
        "name:" + name + ",age:" + age
      }
    
    }
    

     注意:isOlder方法,该方法用于比较二个Person谁更年长,跟java不同的是,在Class定义范围内,可以直接访问另一个类实例的私有成员!这在java、c#中是绝对不允许的。

    另外,还有一个值得注意的地方,Class的定义里,除了def定义的方法(或许称为函数更适合)外,任何可执行语句都会被执行,比如第6行的println语句。下面是调用示例:

        val jimmy: Person = new Person(18, "jimmy")
        val mike: Person = new Person(30, "mike")
        if (mike.isOlder(jimmy))
          println(mike.name + " is older than " + jimmy.name)
    

    执行结果:

    这是主构造器的可执行语句,我是jimmy
    这是主构造器的可执行语句,我是mike
    mike is older than jimmy

    如果不希望private成员在Class定义中直接被其它实例所访问,可以改成private[this],即:

      private[this] var _age: Int = 0;
    
      def age = _age
    
      def isOlder(another: Person) = {
        this._age > another.age
      }
    

    这样的话,isOlder中的another,只能通过函数age来访问私有成员_age了。

    三、static成员/伴生对象Object/apply方法

    scala里并没有static关键字,要达到类似的效果,可以借助object对象,object天然是singleton模式,比如下面的代码:

    object Singleton {
    
      var count = 0;
    
      def increment: Unit = {
        count += 1
      }
    
    }
    

    定义成object类型的对象,没有办法直接new, object中的所有方法都是静态方法,这一点类似c#中的static静态类,使用时直接按静态方法调用即可:

        var obj1 = Singleton.count
        println(obj1)
        Singleton.increment
        var obj2 = Singleton.count
        println(obj2)
    

    object不仅仅用于单例模式的实现,更多时候,我们可以定义一个与class同名的object,然后把class的所有静态方法放到object里,比如:

    class People(var name: String) {
    
      println("main constructor in Class People")
    
    }
    
    
    object People {
    
      def whoami: Unit = {
        println("I am a people ")
      }
    
      def apply(name: String) = {
        println("apply in Object People")
        new People(name)
      }
    
    }
    

    后面的object People称为class People的伴生对象,可以理解为class People的静态成员/方法集合,注意里面的apply方法,这个方法会被自动调用,通常用于创建对象实例,有点工厂模式的意味,看下面的调用代码:

        var p:People = People("jimmy")
        println(p.name)
        People.whoami
    

    这里我们没有用new关键字来创建对象,而是"隐式"调用了伴生对象的静态方式apply,以下是输出结果:
    apply in Object People
    main constructor in Class People
    jimmy
    I am a people

    伴生对象+apply方法,这是scala中经常使用的一个技巧,即简化了代码,又起了工厂模式的作用,我们甚至还可以在apply方法中加入对象控制的额外业务逻辑,这比直接new对象更灵活。

    从object的使用上,还可以看出静态方法的调用上scala与java的不同,java中静态方法即可以用"类名.静态方法()",也可以用"对象实例.静态方法()"来调用,说实话,有点不太讲究,而Scala"纠正"了这一错误,静态方法只能在object(即:静态类)上调用,非静态方法只能在对象实例上调用,这与c#的理念是一致的(见:java学习:OOP入门 第7点)

    apply方法不仅可以存在于object中,class中也可以有apply方法,我们把People的Class改一下:

    class People(var name: String) {
    
      println("main constructor in Class People")
    
      def apply(): Unit ={
        println("apply in Class People")
      }
    
    }
    

    然后这么调用:

        var p:People = People("jimmy")
        p()
    

    注意第2行,就是这么简单!输出结果:
    apply in Object People
    main constructor in Class People
    apply in Class People

    四、内部类(也称嵌套类)

    class内部还可以再定义类,即嵌套类,与java不同的是,scala的嵌套类是属于实例的,而不属于定义它的外部类。这句话听着很绕,还是直接看代码吧,先把前面的People类改造一下:

    class People(var name: String) {
    
      /**
       * 定义嵌套类(注:必须写在最开始,好象也只能定义一个?)
       */
      master =>
    
      //这个master变量即指People本身this,名字可以随便取
    
      class Pet(var name: String) {
        def hi() = println("我叫" + this.name + " , 我是" + master.name + "的宠物!")
      }
    
    
      /**
       * 增加了一个宠物属性
       */
      var pet: Pet = _
    
    }
    
    
    object People {
    
      def apply(name: String, petName: String) = {
        println("apply in Object People")
        val people = new People(name)
        people.pet = new people.Pet(petName)
        people
      }
    
    }
    

     然后使用:

        val jimmy = new People("jimmy")
        val dog = new jimmy.Pet("wang wang") //注:这是调用的"实例"上的Pet,而不是new People.Pet()
        dog.hi()
    
        println("------------")
    
        val mike = People("Mike","miao miao")
        mike.pet.hi()
        println("------------")
    
    
        //println("jimmy与mike交换宠物:")
        //jimmy.pet = mike.pet //直接报错,因为mike的宠物是只属于mike的,它与jimmy的宠物类型不兼容
    
        //jimmy又养了一只猫
        var cat = new jimmy.Pet("miao")
        //然后把狗狗扔了
        jimmy.pet = cat;
        jimmy.pet.hi()
    

    注意第2行及第13行,第2行是直接用 new 实例.内部类()的方式创建的,而非 new 外部类.内部类()这种方式,说明内部类是从属于外部类的实例,第13行再次证明了这一点,虽然都是内部类Pet的实例,但当试图将mike的Pet实例赋值给jimmy的Pet实例时,编译器直接报错,说明内部类的实例一旦创建,则"生是X家人,死是X家鬼",绝对的忠贞不二。

  • 相关阅读:
    卡特兰数
    hdu 1023 Train Problem II
    hdu 1022 Train Problem
    hdu 1021 Fibonacci Again 找规律
    java大数模板
    gcd
    object dection资源
    Rich feature hierarchies for accurate object detection and semantic segmentation(RCNN)
    softmax sigmoid
    凸优化
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/4715339.html
Copyright © 2011-2022 走看看