Scala笔记整理:Scala面向对象—类详解1

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala笔记整理:Scala面向对象—类详解1相关的知识,希望对你有一定的参考价值。

[TOC]


getter和setter

简单类和无参方法

需要说明的是,官方给出的参考资料对于这方面讲解得十分详细,目前入门来说,不需要达到这样的理解,只需要可以进行开发即可,这一部分我给出的是官方的一些文档说明,但是后面的定制和注解,就直接简单说明能用就好了。

class PersonOps {
    private var age = 0 //你必须初始化一个字段
    def increment() {age += 1} //方法默认是公有的
    def currentAge() = age
}

1)在Scala中,类并不声明为public。Scala源文件可以包含多个类,所有这些类都具有公有可见性。

2)如何进行调用?

object MainOps{
    def main(args: Array[String]) {
        val personOps = new PersonOps
        personOps.increment()
        println("currentAge=> " + personOps.currentAge())
    }
}

3)不能用类直接调用age,因为age是私有字段,只能在PersonOps中访问到。

4)无参方法

println("currentAge=>" + personOps.currentAge())
println("currentAge=>" + personOps.currentAge)

建议:对设置值的时候,即改变对象状态值或者状态时使用(),而对于取值来说(不会改变对象的状态和值)去掉()是个不错的风格,这也是我们事例中的方法。

personOps.increment()
println("currentAge=> " + personOps.currentAge)

5)你可以通过以不带()的方式声明currentAge来强制这种风格:

class PersonOps {
    private var age = 0 //你必须初始化一个字段
    def increment() {age += 1} //方法默认是公有的
    def currentAge = age
}

结果:这样一来在调用时,必须要用不带()的进行方法调用

println("currentAge=>" + personOps.currentAge)

6)带getter和setter的属性

Java中属性定义

public class PersonAge{ //这是Java
    private int age;
    public int getAge() { return age; }
    public void setAge(int age) { this.age=age; }
}

getter和setter方式比共有字段(public)好在什么地方呢?

之所以说getter和setter方法比公有字段更好,是因为它们让你可以从简单的get/set机制出发,并在需要的时候做改进。

5)scala中的getter和setter方法

class PersonA{
    var age = 0
}

a.Scala生成面向JVM的类,其中有一个私有的age字段以及相应的getter方法和setter方法。这两个方法是公有的,因为我们没有将age声明为private。而对私有字段而言,getter和setter方法也是私有的。

a.在Scala中,getter和setter分别叫做age和age_=例如:

val personA = new PersonA
println("startAge=> " + personA.age)
personA.age = 21
println("endAge=> " + personA.age)

执行下面的命令:

Scalac PersonOps.scala
\src\main\scala\tw\tw>javap -p PersonA.class
Compiled from "PersonOps.scala"

然后会生成下面的内容:

public class tw.PersonA {
    private int age;
    public int age();
    public void age_$eq(int);
    public tw.PersonA();
}

正如你看到的那样,编译器创建了ageage_$eq,是因为JVM不允许在方法名中出现=

说明:在Scala中,getter和setter方法并非被命名为getXxx和setXxx,不过它们的用意是相同的。后面会介绍如何生成Java风格的getXxx和setXxx方法,以使得你的Scala类可以与Java工具实现互操作

定制的getter/setter方法

这部分先不用搞太复杂,直接使用Java版本的getter/setter方法即可,如下面一个类:

class Student {
  private var age:Int = 0

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

  def getAge() = age

}

测试如下:

scala> val s = new Student()
s: Student = [email protected]

scala> s
res200: Student = [email protected]

scala> s.getAge()
res201: Int = 0

scala> s.getAge
res202: Int = 0

scala> s.setAge(18)

scala> s.getAge
res204: Int = 18

官方关于getter/setter的知识内容非常多,入门的时候这些案例可以先不用管,先从Java的角度出来能够使用scala进行开发即可。

@BeanProperty注解

如果对属性使用了@BeanProperty注解,那么就会自动生成getter/setter方法,但是需要注意的是,其并没有隐藏原来的属性,也就是说原来的属性还是可以直接访问的,并且其不可以使用private进行修饰。

看下面一个例子:

import scala.beans.BeanProperty
class Student {
  @BeanProperty var age:Int = 0 
}

测试如下:

scala> var s = new Student
s: Student = [email protected]

scala> s.getAge()
res205: Int = 0

scala> s.setAge(18)

scala> s.getAge()
res207: Int = 18

scala> s.age
res208: Int = 18

总结

上面啰嗦了很多,其实直接用下面的代码来进行说明就OK了:

/**
  * scala面向对象第一章
  *     关于对象的构造
  *     类的构造(和java一样)
  *
  *   要运行一个scala的程序,不能在class来执行,需要在object中执行
  *   你可以将这个object中的所有的字段、成员,都作为java中的静态的成员对待
  *
  *   在创建scala函数的时候,如果函数空参,我们可以在创建的时候省略掉这个(),但是在访问的时候也就不能使用带()的方式
  *   反之我们既可以使用带(),也可以使用不带()的方式进行调用
  *   ab.foreach(println)
  *
  *   为什么我们要在javabean对字段创建对应的getter和setter
  *     带getter和setter的属性
  *       就是为了程序的安全性?
  *         屏蔽一些不合法的操作,提高程序的健壮性
  */
object _01ClazzFieldsOps {
    def main(args: Array[String]): Unit = {
        val person:Person = new Person()
        person.increment()
        println(person.currentAge())
        println(person.currentAge)

        val stu = new Student

//        stu.age = -1
        stu.setAge(-4)
        stu.getName

        val e = new Employee
    }
}

class Person {
    private var age:Int = 0 //创建一个类型为Int的私有化成员

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

    def currentAge() = age
}

class Student {
    private var age:Int = 15

    private var name:String = "刘向前"
    def setAge(a:Int):Unit = {
        if(a < 1) {
            throw new RuntimeException(s"你们家的孩子才是($a)岁呢")
        }
        age = a
    }
    def getAge = age
    def getName = name
}

class Employee {
    @BeanProperty var age:Int = 5
}

构造器

关于构造器的说明和使用,直接看下面的测试代码就可以了,非常简洁:

package cn.xpleaf.bigdata.p2.constructor

/**
  * scala中的类的构造器说明
  *   scala类的构造器分为主构造器和辅助构造器
  *   其构造方式不同于java中的构造方式,public 类名(参数列表){},在scala中的构造器如何定义?
  * scala中把类名后面的一对{}括起来的内容称为主构造器的函数体,默认的主构造器为空参构造器,
  *   如何定义一个有参的主构造器呢?
  *   就在类名后面写上函数的定义即可
  *
  *   当主构造器满足不了我们的需求之后,我们就可以创建更多的辅助构造器来配合我们的业务,辅助构造器如何创建?
  *   def this  // 辅助构造器的函数名是this
  *   注意,在辅助构造中的第一句话,必须是调用该类的主构造器或者其他辅助构造器,this(参数)
  *   
  *   在一个类中只能有一个主构造器,可以有若干辅助构造器,在辅助构造器的第一句话,调用this
  *
  */
object _02ConstructorOps {
  def main(args: Array[String]): Unit = {
    val p = new Person
    // p.show()
    println("=================================")
    val stu = new Student("前向刘", 17)
    stu.show()
    println("=================================")
    val s = new Student
    println("=================================")
    val s1 = new Student(true)
  }
}

class Person {
  private var name:String = "刘向前"
  private var age:Int = 18

/*  def Person(n:String, a:Int): Unit = {
    name = n
    age = a
    println("这是scala中的构造器吗?")
  }

  def Person(): Unit = {
    println("这是scala中的构造器吗?")
  }*/

  println("这是scala中的构造器吗?")

  def show() = println(s"name is $name and age is $age")
}

class Student(var name:String, var age:Int) {

  def this() {
    this("刘银鹏", 25)
    println("this is 辅助构造器")
  }

  private  var married:Boolean = false
  def this(isMarried:Boolean) {
    this()
    married = isMarried
    println(s"this($isMarried) 是另外一个构造器")
  }

  println("这是scala中的构造器吗?")
  def show() = println(s"name is $name and age is $age")
  // show() // 类构造时就会执行该方法,属性name和age也是默认有的,因为在主构造器中有
}

内部类

直接看下面的一个完整案例:

package cn.xpleaf.bigdata.p2.inner

/**
  * 事物的内部还包含着事物,被包含的事物不能使用非常简单的成员变量来描述,只能使用更为复杂的结构去描述,
  * 这个时候就用我们的内部类去定义
  *
  * 当多个变量重名的时候,遵循一个原则:局部优先
    public class InnerClassOps {
        public static void main(String[] args) {
            Outer.Inner i = new Outer().new Inner();
            i.show();
        }
    }
    class Outer {
        private int x = 5;

        class Inner {
            private int x = 6;
            public void show() {
                int x = 7;
                System.out.println("Inner show: " + x);
            }
        }
    }

  this关键字,是本类的引用,当前类的实例的引用
  外部类的引用使用外部类名.this
  scala同样提供了一种较为简洁的写法,就是在我们的最外层大括号里写一个引用名称outer =>
  在内部类中需要调用外部类的成员的时候,直接使用outer.成员代替外部类名.this
  */
object _03InnerClassOps {
  def main(args: Array[String]): Unit = {
    val outer = new Outer
    val inner = new outer.Inner
    inner.show()
  }
}

class Outer { o =>
  private val x = 5

  class Inner { i =>
    private var x = 6
    def show(): Unit = {
      val x = 7
      println("Inner show: " + this.x)  // 6
      println("Inner show: " + i.x)     // 6
      println("Inner show: " + Outer.this.x)  // 5
      println("Inner show: " + o.x)           // 5
    }
  }
}

以上是关于Scala笔记整理:Scala面向对象—类详解1的主要内容,如果未能解决你的问题,请参考以下文章

大数据笔记(二十四)——Scala面向对象编程实例

scala学习笔记-面向对象编程之Trait(13)

Scala笔记整理:scala基本知识

Scala入门:面向对象编程之Trait 详解

scala入门笔记

(转)scala apply方法 笔记