scala基础之类和对象

一 创建一个简单的类

class Simple{
    private var name = "nicky"
   
def sayHello(): Unit = {
        println("Hello,"+this.name)
    }

    def sayHi = sayHello()
}

object Simple{
    def main(args:Array[String]) {
        val s = new Simple
       
s.sayHello()
        s.sayHi
   
}
}

 

二 字段的get 和 set方法

2.1 变量由private修饰 和 不加修饰符的区别

class Simple {
    /*
     * JVM会自动定义为private域,但是提供publicgettersetter方法
     * 这就意味着该字段可以在其他类或者包中被调用和设置值
     */
    var username:String = null
    /*
     * 定义private 修饰的字段,但是提供privategettersetter方法
     * 这就意味着该字段不能在其他类或者包中被调用和设置值
     */
    private var password:String = null;

    def sayHello:Unit = {
        printf("UserName:"+username+", Password:"+password)
    }
}

 

2.2 定义val 和 var字段的区别

如果我们定义字段成val类型,那么该类只会生成get方法,不会生成set方法,也就是只能取值,不能被赋值,根本原因在于val类型修饰的变量,不可变

class Hello {
    val name = "nicky";
    def say {
        println("Are u "+name+" ?")
        println("yes!!")
    }
}
object Hello {
    def main(args:Array[String]):Unit = {
        val hello = new Hello
        print(hello.name)//但是可以get
        hello.name = "belly"//编译报错,不能set
        hello.say
    }
}

 

2.3 private[this]

如果不希望提供get/set方法,那么我们可以通过private[this]来修饰。

private var field 和private[this] var field 比较:

都只是能在本类使用

private var field 可以生成getter和setter方法,但是是私有的

private[this] var field 压根儿就没有get和set方法

class Hello {
    private[this] val name = "nicky";
    def say {
        println("Are u "+name+" ?")
        println("yes!!")
    }
}
object Hello {
    def main(args:Array[String]):Unit = {
        val hello = new Hello
        print(hello.name)//不能取值
        hello.name = "belly"//也不能设置值
        hello.say
    }
}

 

2.4 自定义get/set方法

如果希望简单的get/set方法,按照scala提供的语法规则就可以了,根据需要为字段选择合适的修饰符,val var private private[this]

但是你如果希望更好的控制get & set函数,你可以自定义get 和 set函数

 

首先,scala里面的get和 set方法格式如下:

get函数: 字段名()

set函数: 字段名_=()

其次定义自己的get set方法,不能重写它本身默认的get set方法,也就是说不能提供和属性名一样的get 和 set方法,这应该是一个坑,

如果你需要构造函数参数或者字段提供get set方法建议你在参数前面加一个_下划线,因为scala是不允许get set方法和字段或者构造函数参数名字完全一样
    

举一个例子:

class Hello{
    var message:String= null;

    def message = "Message=>" + message;

    def message_=(message:String): Unit ={
        this.message= message
   
}
}

这样是有问题的,而且编译会报错

所以你的get set方法应该和属性名不一样:

你可以去一个不同的名字,通常做法是在字段或者构造函数参数前面加一个下划线

class Simple{
    private var name:String= null;
    private var passwd:String= null;

    def sayHello:Unit = {
        printf("UserName:"+username+",Password:"+password)
    }

    def username="UserName => "+this.name
   
def username_=(name:String){
        this.name=name
   
}

    def password=this.passwd
   
def password_=(passwd:String): Unit ={
        this.passwd= passwd
   
}
}

object ClassClient{
    def main (args: Array[String]) {
        val s = new Simple
       
s.username_=("Nicky")
        s.password_=("123abcABC")
        //UserName:UserName=> Nicky, Password:123abcABC
       
print
(s.sayHello)
    }
}

 

比如Simple里字段是name,我们就可以使用username作为get 和set函数名

 

2.5让Scala自动生成java风格的getter和setter方法

给field添加@BeanProperty注解即可,注意一旦你使用@BeanProperty,那么该字段不能被private修饰

class Student{
    @BeanProperty
   
var name:String= null;
}

 

def main(args: Array[String]) {
    val stu = new Student
   
stu.setName("小明")
    print(stu.getName)
}

 

2.6 受保护的字段protected

如果成员是protected,在java中是同包可见,或者不同包但是是其子类也可以见。但是scala中只是子类可见。和private相比,private只要是其他类都不可见,不管你是不是子类,如果private[x],连get 和 set方法都不会有。

区别:

无任何修饰符

任何地方都可以使用

private[x]

在定义的类中可以访问,在x包及子包中可以访问

private[this]

只能在定义的类中访问,即使伴生对象也不能访问团

private

在定义的的类及伴生对象中可以访问,其它地方不能访问

protected[x]

在定义的类及子类中可以访问,在x包及子包中可以访问,

protected[this]

只能在定义的类及子类中访问,即使伴生对象也不能访问

protected

在定义的类及子类中访问,伴生对象可以访问,其它地方不能访问

package com {
    package scala {
        package traits {
        import com.scala.classes.ClassClient
        class XNicky {
            private var userName:String = "admin";
            private[this] var passwd:String = "123abcABC";
            private[traits] var ptype:String = "";
            protected[Animal] var test:String = "true";
            protected var lanuage:String = "Chinese";
            protected[this] var country:String = "China";
            protected[traits] var city:String = "北京";

            private def func1(): Unit ={
                println("[private] 方法")
            }
            private[traits] def func2(): Unit ={
                println("[private[traits]] 方法")
            }
            protected def func3(): Unit ={
                println("[protected] 方法")
            }
            protected[traits] def func4(): Unit ={
                println("[protected[traits]] 方法")
            }
        }
        }
    }
}

 

 

三 构造器Constructor

 

3.1 主构造器

# Scala中主构造器适合类名放在一起的

# Scala中没有定义在任何方法或者代码块中的代码,属于主构造器的代码,这就意味着在构造该类的对象的时候,就会调用这些代码

# 在创建对象的时候,就必须提供主构造器的相对应的属性,否则报错

# 如果变量类型是val,那么初始化完毕之后,你只能访问该字段,不能进行赋值,原理就在于它只提供get方法,没有提供set方法;

如果是var,提供get set方法;如果没有val var 不提供get set方法,

# 我们可以在变量类型前面加上private修饰符,可以防止生成get set方法

3.1.1 普通的主构造器

Java中的实现:

public class SKU{
    protected StringdisplayName;
    protected Stringdesc;
    protected float price;

    public SKU() {
    }

    public SKU(StringdisplayName, String desc, float price) {
        this.displayName= displayName;
        this.desc = desc;
        this.price= price;
    }
   
    public String getDisplayName() {
        return displayName;
    }

    public String getDesc() {
        return desc;
    }

    public float getPrice() {
        return price;
    }

    public void setDisplayName(StringdisplayName) {
        this.displayName= displayName;
    }

    public void setDesc(Stringdesc) {
        this.desc = desc;
    }

    public void setPrice(floatprice) {
        this.price= price;
    }
}

 

Scala实现:

class SKU(vardisplayName:String,var desc:String,varprice:Float) {
    print("displayName=>"+ displayName+"\nDesc=>"+desc+"\nPrice=>"+price)
}

 

3.1.2 为构造参数提供默认值

# 默认参数必须全部提供才能在初始化的时候使用默认构造方法,也就是不必加上参数

# 如果只是一部分提供了默认参数,那么在初始化的时候,不管你有没有提供默认参数,你都需要给该字段赋值

 

class Product(valname:String="B.C",varkind:String="default",varonsale:Boolean=false) {
    println("name:"+name)
    println("kind:"+kind)
    println("onsale:"+onsale)
}

 

3.1.3 构造参数没有提供变量的声明

如果构造参数没有提供变量的声明,比如name:String而不是val name:String或者 var name:String,如果在类里面有方法使用到了该字段,该字段就是private[this],就是不能被其他类访问;如果没有使用到,表示没有该字段

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

  println("you name is "+name+", age is"+age);

}

 

3.2 辅助构造器

在Scala中我们可以定义多个辅助构造器类似于java中构造函数重载,辅助构造函数其实调用的都是主构造函数

 

class Pizza(varcrustSize:Int, var crustType:String) {
    /* 如果你需要构造函数参数或者字段提供get set方法
     *
建议你在参数前面加一个_下划线,因为scala是不允许get set方法
     *
和字段或者构造函数参数名字完全一样
     */
   
var _store:String= null;
    //0个构造参数。就可以在初始化的时候不用提供参数
   
def this(){
        this(10,"default")
    }
    //一个辅助的构造参数,就可以在初始化的时候提供一个参数
   
def this(crustSize:Int){
        this(crustSize,"default")
        this.store= store
   
}

    def this(store:String) {
        this(10,"default")
        this._store= store
       
print("CrustSize=>"+crustSize+"CrustType=>"+crustType+" Store=>"+_store)
    }
    //没有必要提供2个构造参数的辅助函数,因为那样和主构造器是一样的没有必要,而且还会报错
    //   def this(crustSize:Int,crustType:String){
    //        this(crustSize,crustType)
    //   }

   
def store= _store
   
def store_=(store:String): Unit ={
        this._store= store
   
}
}

 

 

class Student {
    private var name = "";
    private var age = 0;

    def this(name:String){
        this();//主构造器
       
this.name= name;
    }

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

 

 

四 将代码块或者函数赋给字段

在Java里面,我们是不可能将代码块或者函数赋给字段的,只能通过get set方法调用函数;Scala中允许直接某一个代码块或者函数直接赋给某一个字段

class Foo{
    /*代码块赋给变量*/
   
val text = {
        var lines = "";
        try {
            lines = io.Source.fromFile("E:\\Bench\\BigData\\scala\\ScalaMain\\content.txt").getLines().mkString
       
} catch {
            case e:Exception=> lines = "ErrorHappened"
       
}
        lines
   
}
    print("text => "+text)

    /*函数调用结果赋给字段*/
   
val content = readContent("E:\\Bench\\BigData\\scala\\ScalaMain\\content.txt")
    print("content => "+content)

    def readContent(path:String):String = {
        var lines = "";
        try {
            lines = io.Source.fromFile(path).getLines().mkString
       
} catch {
            case e:Exception=> lines = "ErrorHappened"
       
}
        lines
   
}
}

 

五 设置未初始化的var字段类型

在一个类中,设置一个未初始化的var字段类型,可以这样开始:

var x = 然后思考如何表达?

解决办法:通常会把字段定义为一个Option。对于某些类型,如string和数字类型字段,你们可以给他指定默认的初始值

case class Person(varusername:String,varpassword:String) {
    var age = 0;
    var firstName = "";
    var lastName = "";
}

 

但这时候你可能需要一个Address对象来存储或者表达用户的地址信息,你可以这样赋一个None:Option[Address]

case class Person(varusername:String,varpassword:String) {
    var age = 0;
    var firstName = "";
    var lastName = "";
    var address = None:Option[Address]
}

case class Address(city:String,state:String,zip:String)

 

然后当用户提供一个地址的时候,可以赋给一个字段Some[Address]的值,像这样:

object ClassClient{
    def main (args: Array[String]) {
        val p = new Person("nicky","123abcABC")
        p.address= Some(Address("Sanfrancisco","CA","02134"))

        /**
         *
需要访问这个address字段的时候有许多不同的方法可以使用,如果想要打印一个Address字段,
         *
可以在 address上调用foreach方法
         *
         *
如果该字段没有值,address就是一个None,调用foreach方法是没有任何害处的,循环跳过
         *
如果该字段有值,他会是一个Some[Address],就会循环打印出来
         */
       
p.address.foreach(a=>
            print(a.city,a.state,a.zip)
        )
    }
}

 

六 在继承类的时候,处理构造函数参数

将父类的构造函数参数定义为val 或者 var。当定义一个子类构造函数时,不要用var或者val声明和父类的公用字段。然后在子类中用val 或者 var字段定义新的构造函数参数

简单一句话: 子类和父类共同字段,父类可以使用var val,但是子类不能加这些修饰符,子类自己独立的字段可以使用var 或者 val变量修饰符

先定义一个父类Parent:

class Parent(varname:String,var address:Address) {
    override def toString:String = if (address == null)name else s"$name @$address"
}

再定一个子类Children

class Children(name:String,address:Address,varage:Int) extends Parent(name,address){

}

 

测试:

object ClassClient{
    def main (args: Array[String]) {
        val p = new Parent("Nicky",newAddress("红瓦寺","成都","637300"))
        println(p.name,p.address.city)
        val c = new Children("Bely",newAddress("磨子桥","成都","637301"),28)
        println(c.name,c.address.city,c.age)
    }
}

 

为什么要这样做?

首先我们需要知道的是:父类已经提供val 或者var 变量修饰符针对共有字段,那么势必就会生成默认的get 和 set方法,比如name字段,则会生成name() 和 name_=,如果子类也是用该字段,但是仍然提供val 或者 var就有问题了,我们知道这会和父类的相同的字段的get set方法冲突,因为父类已经有了嘛。所以 子类只能不加 val 或者 var 修饰符,这样scala就不会生成get set方法。

 

其次:如果父类在val 和 var前面加了修饰符private,那么子类可以加val 或者 var吗?

根据上面的解释,我们知道是可以,因为在父类的构造函数参数前面的变量修饰符前加了private,不管设计var 还是 val都不会生成get 和 set方法,也就说父类没有,父类没有话,子类当然可以有了,所以子类可以再加上val 或者 var

 

七 调用父类的构造函数

在子类创建构造函数的时候,需要控制被调用的父类构造函数

可以在子类的主构造函数中控制被调用的父类的构造函数,但是却无法控制被子类辅助构造调用的父类构造函数

class Animal(varname:String) {
    print(name)
}

 

class Dog(name:String)extends Animal(name){

}

 

如果父类有多个构造函数,Dog可以调用其中任何一个,比如下面Dog类的主构造函数通过extends的时候语句指定构造函数调用Animal类中接收参数的辅助构造函数

class Animal(varname:String,var age:Int) {
    def this(name:String){
        this(name,0)
    }
}

 

然后子类在继承的时候,既可以指定调用父类的一个参数构造函数,也可以指定两个参数的构造函数

class Dog(name:String)extends Animal(name){

}

class Cat(name:String)extends Animal(name,5){

}

 

八  何时使用抽象类

第一种情况:需要创建一个有构造函数的父类

第二种情况:需要被Java调用

 

在抽象父类定义属性:

一个抽象类定义抽象或者实现的属性,这样就可以被子类锁引用

abstract class Pet(name:String,age:Int) {
    /*定义抽象属性,子类来实现*/
   
val greeting:String
   
var color:String
   
def sayHello{println(greeting)}
    /*定义抽象方法,子类来实现*/
   
def action
   
override def toString:String= s"I say: $greeting, and I'm$age, my color is$color"
}

 

class Dog(name:String,age:Int)extends Pet(name,age){
    val greeting = "汪汪"
   
var color = "白色"

   
override def action: Unit = {
        print("我正在啃骨头")
    }
}

class Cat(name:String,age:Int) extendsPet(name,age){
    val greeting = "喵喵"
   
var color = "黑色"

   
override def action: Unit = {
        print("我正在卖萌")
    }
}

object ClassClient{
    def main (args: Array[String]): Unit = {
        val dog = new Dog("旺财",5)
        val cat = new Cat("喵咪",2)
        dog.sayHello
       
cat.sayHello

       
dog.action
       
cat.action
    }
}

 

注意:抽象父类属性使用的变量修饰符val和var应该类型一致

 

九 用case类

当我们使用case类的时候,它会在底层为我们生成一些代码,这样为我们开发提供了一些好处:

# 会生成apply方法,所以我们在初始化对象的时候,就可以不用new

# 默认构造函数参数类型是val,会自动生成get方法,如果是var会自动生成var

# 生成一个默认的tostring方法

# 会产生一个equals方法和hashcode方法

# 回产生一个copy方法

case class People(name:String,var age:Int) {
    def func(): Unit ={
        println(name,age)
    }
}

object ClassClient{
    def main (args: Array[String]): Unit = {
        val p = People("Bala",25)
        p.func()
        println(p.toString)
        println(p.hashCode())
        val p1 = p.copy()
        print(p1.func())
    }
}

 

 

 

十 创建内部类

创建一个内部类,这个类不会被外部API调用

10.1Java和Scala也有不一样的地方

import java.util.ArrayList;

import java.util.List;

public class Classes {

      class Student {

           private Stringname;

 

           public Student(Stringname) {

                 super();

                 this.name =name;

           }

          

      }

      List<Student> students =newArrayList<Student>();

      public Student getStudent(Stringname){

           return new Student(name);

      }

     

      public static void main(String[] args) {

           Classes c1 = new Classes();

           Student s1 = c1.getStudent("Nicky");

           c1.students.add(s1);

          

           Classes c2 = new Classes();

           Student s2 = c2.getStudent("Bell");

           c1.students.add(s2);

      }

}

Classes实例 c1的students是可以添加Classes 实例产生的Student实例

 

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

 

object ClassClient {
    def main (args: Array[String]): Unit = {
        val c1 = new Classes("Java")
        val c2 = new Classes("C#")
        val s1 = c1.getStudent("LiLei")
        val s2 = c1.getStudent("HanMeimei")

        val s3 = c2.getStudent("Jim")
        val s4 = c2.getStudent("Kitty")
        c1.students += s1
        c1.students += s1
        c2.students += s3
        c2.students += s4

        c1.students += s3 //不能添加,类型不匹配
    }
}

在Java里只要内部类是同一个类型,都可以加入到某一个班级中,而不用区别Student是哪一个Classes产生的实例

Scala里,即使内部类是同一个类型,也要区别Student属于的Class实例不一样

 

10.2内部类访问外部类

 

方式一:外部类名.this.成员

class Classes(val name:String ="全真教") {

  var age:Int =0;

  def this(age:Int){

    this();

    this.age = age;

  }

 

  class Student(val name: String) {

    def info():Unit = {

      Classes.this.print();

      println("OuterClass Name: "+Classes.this.name+"; InnerClass Name: "+name)

    }

  }

 

  def print(){

    println(this.name+"  "+this.age)

  }

}

 

方式二 :别名,outrer =>

但是必须放在第一行

class Classes(val name:String ="全真教") {

  outer =>

  class Student(val name: String) {

    def info():Unit = {

      outer.print();

      println("OuterClass Name: "+outer.name+"; Inner Class Name: "+name)

    }

  }

  var age:Int =0;

  def this(age:Int){

    this();

    this.age = age;

  }

 

  def print(){

    println(this.name+"  "+this.age)

  }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫言静好、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值