Scala面向对象编程之 类

-- 定义一个简单的类

 scala的类和java基本差不多,先定义各一个简单类演示:

//定义类,包含field以及方法
  class HelloWorld{
    private var name="leo"
    def sayHello(){println("Hello, "+name)}
    def getName= name
  }
  // 创建类的对象,并调用方法
  val helloWorld= new HelloWorld
  helloWorld.sayHello() 
  println(helloWorld.getName) // 也可以不加括号,如果定义方法时不带括号,则调用方法时不能带括号

代码展示:


以上就是定义了一个简单的类 ,在外部怎么使用类,怎么调用方法。

 以下将详解类

-- 自定义getter与setter

//如果只是希望拥有简单的getter和setter方法,那么就按照scala提供的语法规则,根据需要为field选择合适的修饰符就好:var、var、private、private[this] 
    // 但是如果希望能够自己对getter与setter进行控制,则可以自定义getter与setetr方法
    // 自定义setter方法的时候一定要注意scala的语法限制,签名、=、参数间不能有空格
   class Student{
     private var myName="leo"
     def name="your name is "+myName
     def name_=(newValue:String){ // name_=中间不能有空格
       print("you cannot edit your name!!!")
     }
   } 
   val leo =new Student
   print(leo.name)
   leo.name="leo1"

 -- 仅暴露field的getter方法

//如果你不希望field有setter方法,则可以定义为val ,但是此时就再也不能改变field的值了
   // 但是如果希望能够仅仅暴露出一个getter方法,并且还能通过某些方法更改field的值,那么需要综合使用private以及自定义getter方法
   //此时,由于field是private的,所以setter和gette都是private,对外界没有暴露;自己可以实现修改field的值的方法,自己可以覆盖getter方法
   class Student{
     private var myName="leo"
     def updateName(newName:String){
        myName=newName    //自己实现修改field的值
     }
     def name="your name is "+myName
   }

-- private[this] 的使用

 // 如果将field使用private来修饰,那么代表这个field是类私有的,在类的方法中,可以直接访问类的其他对象的private field
  //这种情况下,如果不希望field被其他对象访问到,那么可以使用private[this],意味着对私有的field ,只有本类对象内可以访问到。
  class Student{
     private var myAge=0
     def age_=(newAge:Int){
       if(newAge >0)myAge=newAge
       else println("Illegal age!")
     }
     def age =myAge
     def older(s:Student)={
       myAge > s.myAge // 如果 myAge 设置为  private[this]  var myAge ,则这里报错
     }
  }

代码展示如下:


-- java风格的setter和getter方法

//Scala的getter和setter方法的命名与java是不同的,是field和field_=的方式
  // 如果想让scala自动生成java风格的getter和setter方法,只要给field添加@BeanProperty注解即可 
  此时会生成4个方法,name:String 、name_=(newValue:String):Unit、getName():String ,setName(newValue:String):Unit 

  import scala.beans.BeanProperty  //import scala.reflect.BeanProperty
  class Student{
    @BeanProperty var name:String=_
  }
 // class Student(@BeanProperty var name:String) //或者使用主构造函数的方式

  val s =new Student
  s.setName("leo")
  s.getName() 

代码展示如下:


 使用主构造函数的方式:

 

-- 辅助 构造函数

// Scala中可以给类定义多个辅助constructor,类似于java中的构造函数重载
  // 辅助构造函数之间可以相互调用,而且必须第一行调用主constructor
  class Student{
    private var name
    private var age
    def this(name:String){
      this()
      this.name=name 
    }
    def this(name:String,age:Int){
      this()
      this.age=age
    }
  }

-- 主constructor

// 在scala中 ,主constructor是与类名放在一起的 ,与java不同
  //而且类中,没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码
  class Student(val name:String ,val age:Int){
    println("your name is " +name+", your age is "+ age)
  }
  // 主 constructor中还可以通过使用默认参数 ,来给参数默认的值
  class Student(val name:String="leo",val age:Int=30){
    println("your name is "+ name+", your age is " +age)
  }
  val s=new Student // 直接使用默认参数
  // 如果主 constructor传入的参数什么修饰都没有,比如name:String ,那么如果类内部的方法使用到了,则会声明为private[this] name, ,否则没有该field ,就只能constructor代码使用而已 。

-- 内部类

// 在scala中,同样可以在类中定义内部类,但是与java不同的是,每个外部类的内部类都是不同的类。
  import scala.collection.mutable.ArrayBuffer
  class A{
    class Student(val name:String)

    val students=new ArrayBuffer[Student]()
    def getStudent(name:String)={
       new Student(name)
    }
  }

  val a1 = new A
  val s1=a1.getStudent("leo")
  a1.students +=s1

  val a2=new A
  val s2=a2.getStudent("leo")
  a1.students +=s2  //报错 error:type mismatch,a1.student和a2的student的实例不是同一个, 所以会报错

-- 面向对象编程之对象 object(scala中所特有的)

 //object ,相当于class的单个实例,通常在里面放一些静态的field或者method
    // 第一次调用object的方法时,就会执行object的constructor,也就是
      //object内部不在method中的代码,但是object不能定义接收参数的constructor
    // 注意: object的constructor只会在其第一次被调用时执行一次,以后再次调用就不会再执行constructor了
    // object通常用于作为单例模式的实现,或者放class的静态成员,比如工具方法
    object Person{
      private var eyeNum=2
      println("this Person object")
      def getEyeNum=eyeNum
    }

举例:

 

-- 伴生对象

//如果有一个class ,还有一个与class同名的object,那么就称这个object是class的伴生对象,class是object的伴生类
   // 伴生类和伴生对象必须存放在一个.scala文件中
   //伴生类和伴生对象 ,最大的特点就是可以互相访问private field
   class Person(val name:String,val age:Int){
     def sayHello=println("Hi, "+name+", I guess you are "+age+" years old!"+", and usually you must have "+ Person.eyeNum+"eyes.")
   } 
   object Person{
     private val eyeNum =2 
     def getEyeNum =eyeNum
   }

-- 让object继承抽象类

// object的功能其实和class类似,除了不能定义接收参数的constructor之外
   // object可以继承抽象类,并覆盖抽象类中的方法
   abstract class Hello(var message:String){ //定义抽象类
      def sayHello(name:String):Unit
   }

   object HelloImpl extends Hello("Hello"){ //继承抽象类,实现抽象方法
     override def sayHello(name:String)={
       println(message+"," +name)
     }
   }

   // 访问
    HelloImpl.sayHello("leo")

用法如下:


-- apply方法

//object非常重要的一个特殊方法,就是apply方法
   //通常在伴生对象中实现apply方法,并在其中实现构造伴生类的对象的功能
   //而创建伴生类的对象时,通常不会使用new Class的方式,而是使用Class()的方式,隐式第调用伴生对象的apply方法,这样会让对象创建更加简洁
   //比如,Array类的伴生对象的apply方法就实现了接收可变数量的参数,并创建一个Array对象的功能
   val a=Array(1,2,3,4,5)
   // 比如,定义自己的伴生类和伴生对象。
   class Person(val name:String)
   object Person{
     def apply(name:String)=new Person(name)
   } 

   val p1=new Person("leo")
   val p2=Person("leo") //更简洁

-- main方法

 //如同java中,如果要运行一个程序,必须编写一个包含main方法类一样,在scala中如果要运行一个程序,就必须有一个main方法作为入口
   //scala中的main方法定义为def main(args:Array[String]),而且
   object HelloWorld{
     def main(args:Array[String]){
       println("Hello World!")
     }
   }

   //除了自己实现main方法外,还可以继承App Trait,然后将需要在main方法中运行的代码,直接作为object的constructor代码,而且用args可以接受传入的参数
   object HelloWorld extends App{
     if(args.length >0)println("hello," +args(0))
     else println("Hello World!")
   }
   // 如果要运行上述代码,需要将其放入.scala文件,然后先使用scalac编译,再用scala执行

   scalac HelloWorld.scala
   scala -Dscala.time HelloWorld
 // App Trait的工作原理为:App Trait继承自DelayedInit Trait ,scalac命令进行编译时,会把
   // 继承 App Trait的object的constructor代码都放到DelayedInit Trait的delayedInit方法中执行
   scala -Dscala.time HelloWorld 显示执行时间

-- 用object实现枚举功能

// scala没有直接提供类似于java中的Enum这样的枚举特性,如果要实现枚举,则需要用object继承Enumeration类,并调用Value方法来初始化枚举值
object Season extends Enumeration{
   val SPRING,SUMMER,AUTUMN,WINTER=Value
}
// 还可以通过Value传入枚举值得id和name,通过id和toString可以获取,还可以通过id和name来查找枚举值   
object Season extends Enumeration{
  val SPARING=Value(0,"spring")
  val SUMMER=Value(1,"summer")
  val AUTUMN=Value(2,"autumn")
  val WINTER=Value(3,"winter")
}
Season(0)
Season.withName("spring")
// 使用枚举object.values可以遍历枚举值
for(ele <- Season.values)println(ele)

使用如下所示:



-- -- extends 关键字

// scala中,让子类继承父类,与java一样,也是使用extends关键字
 //继承就代表,子类可以继承父类的field和method,然后子类可以在自己的内部放入父类所没有,子类特有的field和method,使用继承可以有效复用代码
 //子类可以覆盖父类的field和method,但是如果父类用final修饰,field和method用final修饰,则该类是无法被继承的,field和method是无法被覆盖的

 class Person{
  private var name="leo"
  def getName=name
 }

 class Student extends Person{
   private var score="A"
   def getScore=score
 }

代码展示:


-- override和super

 //scala中,如果子类要覆盖一个父类中的非抽象方法, 则必须使用override关键字
  //override关键字可以帮助我们尽早的发现代码中的错误,比如:override修饰的父类方法的方法名写错了,比如要覆盖的父类的参数写错了等,父类中一旦没有就会报错。
  //此外,在子类覆盖父类的方法后,如果要用要调用父类的被覆盖的方法, 就可以使用super关键字,显式指定调用父类的方法。


-- -- override field

// scala中,子类可以覆盖父类的的val field,而且子类的val field还可以覆盖
    // 父类的val field的getter方法,只要在子类中使用override关键字即可
  class Person{
    val name:String="Person"
    def age:Int=0
  } 
  class Student extends Person{
    override val name:String="leo"
    override val age:Int=30
  }

-- isInstanceOf 和asInstanceOf

//如果我们创建子类的对象,但是又将其赋予父类类型的变量,则在后续的程序中,我们又需要将父类类型的变量转换为
     //子类类型的变量,该怎么做呢?
    // 首先,需要使用isInstanceOf判断对象是否是指定类的对象,如果是的话,则可以使用asInstanceOf将对象转为指定类型。
    //如果对象为null ,则isInstanceOf一定返回false,asInstanceOf一定返回null】
    //注意:如果没有用isInstanceOf先判断对象是否为指定类的实例,就直接使用asInstanceOf转换,则可能会抛出异常

    class Person
    class Student extends Person

    val p:Person=new Student
    var s:Student=null
    if(p.isInstanceOf(Student)) s=p.asInstanceOf(Student)

-- getClass 和classOf

// isInstanceOf只能判断出对象是否是指定类型以及其子类的对象,而不能精确判断出,对象就是指定类的对象
   //如果要精确地判断对象就是指定类的对象,那么就只能使用getClass和classOf了
   //对象.getClass可以精确获取对象的类,classOf[类]可以精确获取类,然后使用==操作符即可判断

   class Person
   class Student extends Person
   val p:Person=new Student
   p.isInstanceOf[Person]
   p.getClass == classOf[Person] // 精确判断
   p.getClass ==classOf[Student]

-- 使用模式匹配进行类型判断

 //实际开发中,比如spark源码中,大量的地方都是使用到模式匹配的方式进行类型的判断
  // 这种方式更加简洁明了,而且代码的可维护性和扩展性非常高
  // 使用模式匹配,功能性上来说,与isInstanceOf一样,也是判断主要是该类以及该类的子类的对象即可,不是精确判断的

  class Person
  class Student extends Person
  val p:Person =new Studnet
  p match{
    case per :Person => println("it's Person's object")
    case _ => println("unknown type ")
  }

 -- protected 

// 跟java一样,scala中同样可以使用protected关键字来修饰field和method,这样在子类中就不需要super关键字,直接可以访问field和method
   // 还可以使用protected[this],则只能在当前子类对象中访问父类的field和method,无法通过其他子类对象访问父类的method和field

   class Person{
     protected var name:String="leo"
     protected[this]var hobby:String="game"
   }

   class Student extends Person{
     def sayHello =println("Hello, "+name)
     def makeFriends(s:Student){
       println("my hobby is "+ hobby+", your hobby is" + s.hobby)
     }

使用如下:


-- 调用父类的constructor

//Scala中,每个类可以有一个主constructor和任意多个辅助constructor,而每个辅助constructor的第一行
   //都必须是调用其他辅助constructor或者是主constructor;因此子类的辅助constructor是一定不可能直接调用父类的constructor的
   // 只能在子类的主constructor中调用父类的constructor,以下这种语法,就是通过子类的构造函数来调用父类的构造函数
   //注意:如果是父类中接收的参数,比如name和age,子类中接收时,就不要用任何val或者var来修饰,否则会认为是子类要覆盖父类的field
   class Person(val name:String,val age:Int)
   class Student(name:String,age:Int,var score:Double) extends Person(name,age){
     def this(name:String){ this(name,0,0)}
     def this(age:Int){ this("leo",age,0) }
  }

-- 匿名子类

// 在scala中,匿名子类是非常常见的, 而且非常强大.spark源码中也大量使用了这种匿名子类
  // 匿名子类也就是说,可以定义一个类的没有名字的子类,并直接创建其对象,然后将对象的引用赋予一个变量,之后甚至可以将该子类的对象传递给其他函数。

  class Person(protected val name:String){
    def sayHello="Hello ,I'm "+ name
  }
  val p=new Person("leo"){  //匿名子类
    override def sayHello="Hi, I'm "+ name
  }
  def greeting(p:Person){
     println(p.sayHello)
  }

用法:


-- 抽象类

 // 如果在父类中,有某些方法无法立即实现,而需要依赖不同的子类来覆盖,重写实现自己不同的方法实现。
   // 此时可以将父类中的这些方法不给出具体的实现,只有方法签名,这种方法就是抽象方法
   //而一个类中如果有一个抽象方法,那么类就必须用abstract声明为抽象类,不可以被实例化
   // 在子类中覆盖抽象类的抽象方法时,不需要使用override关键字
   abstract class Person(val name:String){ def sayHello:Unit }
   class Student(name:String) extends Person(name){
      def sayHello:Unit=println("Hello , " + name)
   }

-- 抽象field

//如果在父类中定义了 field ,但是没有给出初始化值,则此field为抽象field
   //抽象field意味着,scala会根据自己的规则,为val或var类型的field生成对应的getter和setter方法,但是父类中是没有该field的
   //子类必须覆盖field,以定义自己的具体的field,并且覆盖抽象field, 不需要使用override关键字
   abstract class Person{  var name:String }
   class Student extends Person{
      val name:String="leo" // 不需要用override关键字 
   }

猜你喜欢

转载自blog.csdn.net/m0_37564404/article/details/80633827