Scala面向对象

Scala的面向对象思想和Java的面向对象思想和概念是一致的,但Scala语法和Java不同,补充了更多的功能。

一.关于package

1.包的管理方式

Scala有两种包的管理风格:

  • 1)和Java的包管理风格相同,每个源文件一个包,包名用“.”进行分隔以表示包的层级关系,如com.example.scala

    • 包名和源文件所在路径不要求必须一致
  • 2)另一种风格,通过嵌套的风格表示层级关系,如下

package com{
	package example{
		package scala{
			object Test{
				def main(args: Array[String]): Unit = {
					println("Hello World!!!") 
				}
			}
		}
	}
}

包名和源文件所在路径不要求必须一致:

  • 将以上代码复制到Test.scala文件中
  • scalac编译
    在这里插入图片描述

第二种风格有以下特点:

  • 1)一个源文件中可以声明多个package
  • 2)子包中的类可以直接访问父包中的内容,而无需导包
  • 3)父包访问子包需要导包
package com {
	//导包语句可以出现在程序的任意位置
    import com.example.Inner //父包访问子包需要导包
    
	//外层包中的类
    object Outer {
        val out: String = "out"
        def main(args: Array[String]): Unit = {
            println(Inner.in)
        }
    }

    package example{
		//内层包中的类
        object Inner {
            val in: String = "in"
            def main(args: Array[String]): Unit = {
                println(Outer.out) //子包访问父包无需导包
            }
        }
    }
}

package other {

}

2.包对象

在Scala中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有class和object的共享变量,可以被直接访问。

package object com{
	val shareValue="share"
	def shareMethod()={}
}

1)若使用Java的包管理风格,则包对象一般定义在其对应包下的package.scala文件中,包对象名与包名保持一致

  • 包名:scala.collection = 包对象名:scala包下的collection

在这里插入图片描述
2)如采用嵌套方式管理包,则包对象可与包定义在同一文件中,但是要保证包对象与包声明在同一作用域中

package com {
    object Outer {
        val out: String = "out"
        def main(args: Array[String]): Unit = {
        	//调用包对象的name属性
            println(name)
        }
    }
}

package object com {
    val name: String = "com"
}

3.导包说明

1)和Java一样,可以在顶部使用import导入,在这个文件中的所有类都可以使用。
2)局部导入:什么时候使用,什么时候导入——在其作用范围内都可以使用
3)通配符导入:import java.util._
4)给类起名:import java.util.{ArrayList=>JL}
5)屏蔽类:import java.sql.{Date=>_,Array=>_,_}
6)导入相同包的多个类:import java.util.{HashSet, ArrayList}
7)导入包的绝对路径:new _root_.java.util.HashMap

注意:Scala中的三个默认导入分别是

  • import java.lang._
  • import scala._
  • import scala.Predef._

二.类和对象

在Java/Scala语言中,类是创建对象的模板,是客观事物在人脑中的主观反映;而只要是客观存在的事物都是对象(万物皆对象):

  • 类:可以看成一个模板
  • 对象:表示具体的事物

1.定义类

1)Java中的类

  • 如果类是public的,则必须和文件名一致。
  • 一般,一个.java文件有一个public类

注意:Scala中没有public,一个.scala中可以写多个类。

2)Scala基本语法

[修饰符] class 类名 {
    类体
} 

说明:

  • 1)Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)
  • 2)一个Scala源文件可以包含多个类

2.属性

属性是类的一个组成部分

1)基本语法

[修饰符] var|val 属性名称 [:类型] = 属性值

注:

  • 1)在Scala语言中,类、方法、属性默认修饰符是public,但是没有public关键字
  • 2)对于Scala中的默认属性,底层会用private修饰,同时提供公开的设置以及获取属性的方法----面向封装
  • 3)如果编译后要生成满足JavaBean规范的get和set方法的话,需要在属性上加@BeanProperty注解
    • 作用:实现对一些框架进行支持
  • 4)如果想给属性赋默认值,需要在声明的时候,赋 _:数值数据类型是各种各样的0,而其他数据类型则为null
object TestField {
  def main(args: Array[String]): Unit = {
    //创建对象
    val std: Student05 = new Student05()
    //通过对象.的方式访问属性
    std.name = "lisi"
    println(std.name)  //lisi
    println(std.sex)  //null
  }
}

class Student05{
  //在Scala语言中,属性、方法、类默认的修饰是public
  //但是public 不能显式加  类似java中default
  //底层生成的字节码文件对属性用private进行修饰,提供了公开的获取|设置属性值的方法
  //作用1:符合面向对象的封装特性
  var name:String = "zhangsan"

  //如果想生成符合JavaBean规范的get|set方法,需要在属性上加一个注解@BeanProperty
  //作用2:对一些框架进行支持
  @BeanProperty
  var age:Int = 18

  //如果想给属性赋默认值,需要在声明的时候,赋_
  var sex:String = _
}

Student类的反编译结果:

在这里插入图片描述

三.封装

封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。

Java封装操作如下:

  • 1)将属性进行私有化
  • 2)提供一个公共的set方法,用于对属性赋值
  • 3)提供一个公共的get方法,用于获取属性的值

Scala中默认public属性,底层实际为private,并通过get方法(obj.field())和set方法(obj.field_=(value))对其进行操作。所以Scala并不推荐将属性设为private,再为其设置public的get和set方法的做法。

如果属性设置为private:其编译结果对应的属性及方法都为私有

在这里插入图片描述

1.权限访问

Java中:

  • private 私有的,只能在当前类中被访问
  • default 默认的,当前类以及同包的其他类
  • protected 受保护的,可以在本类、同包的其它类以及非同包的子类中被访问
  • public 公开的,所有类

Scala中:

  • 在scala中,类、方法、属性默认就是public修饰,但是没有public关键字
  • private 私有的:只能在当前类以及伴生对象中使用
  • protected 受保护的:比Java中的权限设置更加严格,同类、子类可以访问,同包其它类不能访问
  • private[包名] 包访问权限:可以让指定的包进行访问

2.方法

基本语法

def 方法名(参数列表) [:返回值类型] = { 
	方法体
}

3.创建对象

1)基本语法

val | var 对象名 [:类型]  = new 类型()

2)说明:

  • 1)val修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。
  • 2)var修饰对象,可以修改对象的引用和修改对象的属性值
  • 3)自动推导变量类型不能多态,所以多态需要显示声明
class Person {
    var name: String = "canglaoshi"
}

object Person {
    def main(args: Array[String]): Unit = {
        //val修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。
        val person = new Person()
        person.name = "bobo"

        // person = new Person()// 错误的
        println(person.name)
    }
}

4.构造器

1.构造器(构造方法)的作用:

  • 构造对象
  • 初始化属性(给对象的属性赋值)

2.Java中构造器

  • 1)方法名和类名保持一致
  • 2)构造方法没有返回值类型
  • 3)构造方法可以重载

和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。

Scala类的构造器包括:主构造器和辅助构造器

3.Scala中构造器(构造方法)

  • 1)主构造方法
    • 在声明类的同时,主构造方法也被声明
    • 在一个类中,主构造方法只能有一个
    • 如果主构造方法没有参数,那么以及调用的时候,小括号可以省略的
  • 2)辅助构造方法
    • 方法名必须叫this
    • 辅助构造方法可以重载,可以有多个,编译器通过参数的个数及类型来区分
    • 辅助构造方法中的第一行必须直接或者间接调用主构造方法
    • 构造器调用其他另外的构造器,要求被调用构造器必须提前声明

4.基本语法如下:

//如果想让主构造器变成私有的,可以在()之前加上private
class 类名 [private](形参列表) {  // 主构造器
   // 类体
   def  this(形参列表) {  // 辅助构造器
   }
   def  this(形参列表) {  //辅助构造器可以有多个...
   }
} 

5.案例:

//(1)如果主构造器无参数,小括号可省略
// class Person (){
class Person {
  var name: String = _
  var age: Int = _
  //(2)方法名必须叫this,且可以重载
  def this(age: Int) {
  	//辅助构造方法中的第一行直接调用主构造方法
    this()
    this.age = age
    println("辅助构造器1")
  }
  //构造器调用其他另外的构造器,要求被调用构造器必须提前声明
  //注意辅助构造函数顺序:辅助构造器1必须在2之前声明
  def this(age: Int, name: String) {
    //辅助构造方法中的第一行调用辅助构造器1——达到间接调用主构造方法目的
    this(age)
    this.name = name
    println("辅助构造器2")
  }
  println("主构造器")
}

object Person {
  def main(args: Array[String]): Unit = {
    val person1 = new Person(18)  //输出结果:
	println("-----------------------")
    val person2 = new Person(18,"zhangsan")
  }
}
输出结果:
	主构造器
	辅助构造器1
	-----------------------
	主构造器
	辅助构造器1
	辅助构造器2

5.构造器参数

Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰

  • 1)未用任何修饰符修饰,这个参数就是一个局部变量(在外部访问不了)
  • 2)var修饰参数,作为类的成员属性使用,可以修改
  • 3)val修饰参数,作为类只读属性使用,不能修改
//1)只提供无参的主构造:可以省略小括号
class Student1{
  var name:String = _
  var age:Int = _
}

//2)在声明主构造方法参数的时候,如果参数前什么也不加,那么参数就作为当前类的局部变量使用,不能作为类的属性被访问
//在scala语言中,以下写法比较少,如果这样写,那就是受java毒害太深了
class Student2(namePararm:String,ageParam:Int){
  var name:String = namePararm
  var age:Int = ageParam
}

//3)如果需要将参数作为属性被访问的话,那么在参数声明前加var或者val关键字
class Student3(var name:String, var age:Int){
  def m1(): Unit ={
    println(name)
    println(age)
  }
}

四.继承

基本语法:

class 子类名 extends 父类名  { 类体 }

scala继承特点:

  • 1)子类继承父类的属性和方法
  • 2)scala是单继承
  • 3)继承的调用顺序:父类构造器->子类构造器

五.抽象属性和抽象方法

1.基本语法

  • 1)定义抽象类: 通过abstract关键字标记抽象类
  • 2)定义抽象属性:一个属性没有初始化,就是抽象属性
  • 3)定义抽象方法:只声明而没有实现的方法,就是抽象方法
//1)定义抽象类
abstract class Person {
	// 2)定义抽象属性
    val name: String
    //3)定义抽象方法
    def hello(): Unit
}
//实现类
class Teacher extends Person {
    val name: String = "teacher"
    def hello(): Unit = {
        println("hello teacher")
    }
}

2.继承&重写

  • 1)如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类
  • 2)如果重写(实现)抽象属性或者方法,那么override关键字可以省略
    • 如果重写(覆盖)非抽象属性或者方法,那么override关键字不能省略,必须得加
  • 3)子类中调用父类的方法使用super关键字
    • 可以通过super关键字调用父类的方法,但是不能super调用父类的属性
  • 4)子类对父类抽象属性进行实现,父类抽象属性==可以用var修饰;
    • 子类对父类非抽象属性重写,父类非抽象属性只支持val类型,而不支持var。
    • 因为var修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写
  • 5)Scala中,属性和方法都是动态绑定,而Java中只有方法为动态绑定——多态
    • 静态绑定(编译器绑定)
      在编译阶段,确定属性或者方法所属类型
    • 动态绑定
      在运行阶段,根据实际创建的对象类型来决定属性 或者方法所属类型
class Person {
    val name: String = "person"
    def hello(): Unit = {
        println("hello person")
    }
}

class Teacher extends Person {
	//重写父类的name常量;如果name是变量,没有必要重写,直接重写赋值使用即可
    override val name: String = "teacher"
	//重写hello方法
    override def hello(): Unit = {
        println("hello teacher")
    }
}
object Test {
    def main(args: Array[String]): Unit = {
        val teacher: Teacher = new Teacher()
        println(teacher.name)
        teacher.hello()
		//多态:都为动态绑定
        val teacher1:Person = new Teacher
        println(teacher1.name)	//teacher
        teacher1.hello()		//hello teacher
    }
}

3.匿名子类

Java一样,scala可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类。

//定义抽象类
abstract class Person {
    val name: String
    def hello(): Unit
}

object Test {
    def main(args: Array[String]): Unit = {
    	//使用匿名子类:实现抽象类Person
        val person = new Person {
            override val name: String = "teacher"
            override def hello(): Unit = println("hello teacher")
        }
    }
}

六.多态

多态:同一个对象,多种不同形态

  • 父类引用指向子类对象,或者是接口指向实现类
  • 只能调用其引用类型中定义的方法(编译看左)
    • 静态绑定(编译期绑定):在Java语言中,在程序编译的时候,确定属性所属的类型
  • 在运行的时候,会执行子类中覆盖的方法(运行看右)
    • 动态绑定(运行期绑定):在程序执行的时候,看实际创建对象的类型是什么,然后进行调用,方法属于动态绑定

Java中只有方法为动态绑定,而Scala中属性和方法都是动态绑定。

public class Test {
    public static void main(String[] args) {
        //不存在多态的情况
        Teacher tea = new Teacher();
        System.out.println(tea.name);//teacher
        tea.hello();//hello teacher

        //存在多态的情况:方法为动态绑定,属性为静态绑定
        Person tea2 = new Teacher();
        System.out.println(tea2.name);//person
        tea2.hello();//hello teacher
        //tea2.sayHi();  编译不能被通过
    }
}

class Person{
    String name = "person";
    public void hello(){
        System.out.println("hello Person");
    }
}
class Teacher extends Person11{
    String name = "teacher";
    public void hello(){
        System.out.println("hello teacher");
    }

    public void sayHi(){
        System.out.println("sayHI teacher");
    }
}

七.单例对象(伴生对象)

Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象

若单例对象名与类名一致,则称该单例对象为这个类的伴生对象这个类的所有“静态”内容都可以放置在它的伴生对象中声明

1.单例对象语法

object Person{
	val country:String = "China"
}

说明

  • 1)单例对象采用object关键字声明
  • 2)单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
  • 3)单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
//(1)伴生对象采用object关键字声明
object Person {
    var country: String = "China"
}

//(2)伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
class Person {
    var name: String = "bobo"
}

object Test {
    def main(args: Array[String]): Unit = {
        //(3)伴生对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
        println(Person.country)
    }
}

2.伴生对象的apply方法

  • 1)通过伴生对象的apply方法,实现不使用new方法创建对象。
  • 2)如果想让主构造器变成私有的,可以在()之前加上private
  • 3)apply方法可以重载。
  • 4)Scala中obj(arg)的语句实际是在调用该对象的apply方法,即obj.apply(arg)
    • 用来统一面向对象编程和函数式编程的风格。
  • 5)当使用new关键字构建对象时,调用的其实是类的构造方法;当直接使用类名构建对象时,调用的其实时伴生对象的apply方法。
object Test {
    def main(args: Array[String]): Unit = {
        //(1)通过伴生对象的apply方法,实现不使用new关键字创建对象
        val p1 = Person()  //实际上是调用的Person.apply()方法
        println("p1.name=" + p1.name)

        val p2 = Person("bobo") 
        println("p2.name=" + p2.name)
    }
}

//(2)如果想让主构造器变成私有的,可以在()之前加上private
class Person private(cName: String) {
    var name: String = cName
}

object Person {
	//3)apply方法可以重载
    def apply(): Person = {
        println("apply空参被调用")
        new Person("xx")
    }

    def apply(name: String): Person = {
        println("apply有参被调用")
        new Person(name)
}
//注意:调用apply方法也可以创建其它类型对象,并不一定是伴生类对象
}

注意:调用apply方法也可以创建其它类型对象,并不一定是伴生类对象

3.创建对象的方式总结

1)Java创建对象的方式

  • new
  • 反射
  • 工厂
  • 克隆
  • 反序列化

2)Scala创建对象的方式

  • a)new:底层调用类的构造方法
  • b)类():底层调用的是apply方法
    • apply方法定义在类的伴生对象中

单例设计模式

  • 1)构造方法私有化
  • 2)提供私有静态属性,接收单例对象
  • 3)公共的、静态的getInstance方法
object TestSingleton {
  def main(args: Array[String]): Unit = {
    val std1: Student1 = Student1.getInstance()
    println(std1)
    val std2: Student1 = Student1.getInstance()
    println(std1)
  }
}

//1.懒汉式
object Student1{
  // 2)提供私有静态属性,接收单例对象
  private var s:Student1 = null
  // 3)公共的、静态的getInstance方法
  def getInstance():Student1={
    if(s == null){
      s = new Student1
    }
    s
  }
}
//1)主构造方法私有化
class Student1 private(){}


//2.饿汉式
object Student2{
  // 2)提供私有静态属性,接收单例对象
  private var s:Student2 = new Student2
  // 3)公共的、静态的getInstance方法
  def getInstance():Student2={
    s
  }
}
//1)主构造方法私有化
class Student2 private(){}

八.Trait:特质

Scala语言中,采用特质trait(特征)来代替接口的概念,也就是说,多个类具有相同的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明。

Scala中的trait中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质。这种感觉类似于Java中的抽象类。

Scala引入trait特征,第一可以替代Java的接口,第二个也是对单继承机制的一种补充。

1.特质声明

1)基本语法:

trait 特质名 {
	抽象属性
	非抽象属性
	抽象方法
	非抽象方法
}

2)通过查看字节码,可以看到特质 = 抽象类 + 接口

2.特质继承

一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with关键字连接

基本语法:

  • 1)没有父类:

    class  类名 extends  特质1   with    特质2   with   特质3 …
    
  • 2)有父类:

    class  类名  extends  父类   with  特质1   with   特质2  with 特质3…
    

说明:

  • 1)类和特质的关系:使用继承的关系。
  • 2)当一个类去继承特质时,第一个连接词是extends,后面是with
  • 3)如果一个类在同时继承特质和父类时,应当把父类写在extends后

特点:

  • 1)特质可以同时拥有抽象方法和具体方法
  • 2)一个类可以混入(mixin)多个特质
  • 3)所有的Java接口都可以当做Scala特质使用
  • 4)动态混入:可灵活的扩展类的功能
    • 4.1)动态混入:创建对象时混入trait,而无需使类混入该trait
    • 4.2)如果混入的trait中有未实现的方法,则需要实现
trait PersonTrait {
    //(1)特质可以同时拥有抽象方法和具体方法
    // 声明属性
    var name: String = _
    // 抽象属性
    var age: Int
    // 声明方法
    def eat(): Unit = {
        println("eat")
    }
    // 抽象方法
    def say(): Unit
}

trait SexTrait {
    var sex: String
}

//(2)一个类可以实现/继承多个特质
//(3)所有的Java接口都可以当做Scala特质使用
class Teacher extends PersonTrait with java.io.Serializable {
	//实现PersonTrait 的方法与属性
    override def say(): Unit = {
        println("say")
    }
    override var age: Int = _
}

object TestTrait {
    def main(args: Array[String]): Unit = {
        val teacher = new Teacher
        teacher.say() //
        teacher.eat() //
        
        //(4)动态混入:可灵活的扩展类的功能
        val t2 = new Teacher with SexTrait {
            override var sex: String = "男"
        }

        //调用混入trait的属性
        println(t2.sex)
    }
}

3.特质叠加*

由于一个类可以混入(mixin)多个trait,且trait中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。冲突分为以下两种:

1)一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法

在这里插入图片描述

object TestTrait1 {
  def main(args: Array[String]): Unit = {
    val mc = new MyClass
    mc.m1()  //m1的实现
  }
}


trait TraitA{
  def m1():Unit={
    println("TraitA m1")
  }
}

trait TraitB{
  def m1():Unit={
    println("TraitB m1")
  }
}

class MyClass extends TraitA with TraitB{
  override def m1(): Unit = {
    println("m1的实现")
  }
}

2)一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait继承自相同的trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加的策略

在这里插入图片描述

  • 所谓的特质叠加,就是将混入的多个trait中的冲突方法叠加起来,案例如下:
trait Ball {
   def describe(): String = {
      "ball"
   }
}

trait Color extends Ball {
   override def describe(): String = {
      "blue-" + super.describe()
   }
}

trait Category extends Ball {
   override def describe(): String = {
      "foot-" + super.describe()
   }
}

class MyBall extends Category with Color {
   override def describe(): String = {
      "my ball is a " + super.describe()
   }
}

object TestTrait {
   def main(args: Array[String]): Unit = {
      println(new MyBall().describe())
      //输出结果:my ball is a blue-foot-boot
   }
}

4.特质叠加执行顺序

上述案例中的super.describe()是如何调父trait中的方法的?

当一个类混入多个特质的时候,scala会对所有的特质及其父特质按照一定的顺序进行排序,而此案例中的super.describe()调用的实际上是排好序后的下一个特质中的describe()方法。

排序规则如下:
在这里插入图片描述

结论:

  • 1)案例中的super,不是表示其父特质对象,而是表示上述叠加顺序中的下一个特质,即,MyClass中的super指代Color,Color中的super指代Category,Category中的super指代Ball
  • 2)如果想要调用某个指定的混入特质中的方法,可以增加约束:super[],例如super[Category].describe()
......
class MyBall extends Category with Color {
   override def describe(): String = {
   	  //1)任按照特质叠加顺序调用:输出结果与原结果一致
      //"my ball is a " + super[Color].describe()
      //2)直接调用某个指定的混入特质中的方法:不会按照原顺序去执行
      "my ball is a " + super[Category].describe()
      //输出结果:my ball is a foot-ball
   }
}

object TestTrait {
   def main(args: Array[String]): Unit = {
      println(new MyBall().describe())
   }
}

5.特质自身类型

特质自身类型:

  • 1.实现了依赖注入的功能
  • 2.要求混入该特质的同时,要混入特质自身类型
class User(val name: String, val age: Int)
//DAO层:和数据库打交道,完成数据的CRUD
trait Dao {
   def insert(user: User) = {
      println("insert into database :" + user.name)
   }
}
//
trait APP {
	//1.实现了依赖注入的功能
   _: Dao =>
   //可以注入多个:所有的java接口,都可以当做特质被混入
  // _: Dao with Exeception =>
   def login(user: User): Unit = {
      println("login :" + user.name)
      insert(user)
   }
}
//2.要求混入该特质(如APP)的同时要混入特质自身类型(APP中存在特质自身类型Dao)
object MyApp extends APP with Dao {
   def main(args: Array[String]): Unit = {
      login(new User("bobo", 11))
   }
}

6.特质和抽象类的区别

抽象类和特质关系:
1)抽象类中可以定义抽象属性、抽象方法、非抽象属性、非抽象方法

  • 特质中也可以定义抽象属性、抽象方法、非抽象属性、非抽象方法

2)抽象类和特质都不能被实例化

  • 抽象类有构造方法
  • 特质也有构造方法
  • 父类的构造方法调用就近原则(先继承谁先调用)

3)如果需要给构造方法传递参数的话,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行(有无参构造)。

4)优先选择特质,scala是单继承,如果直接继承不方便后续的扩展

5)对大量对象的共性进行抽象—>;对大量类共性进行抽象–>抽象类

  • 而特质一般是对行为进行抽象,定义规范
  • 一般的如果子父类满足 is-a 的原则,选择抽象类
object TestTraitAndAbstract {
  def main(args: Array[String]): Unit = {
    new MyClass
  }
}

trait TraitA{
  println("特质的构造方法")
  def m1():Unit
}

abstract class MyAbstract{
  println("抽象类的构造方法")
  def m1():Unit
}

class MyClass extends MyAbstract with TraitA{
  println("myclass 的构造方法")
  override def m1(): Unit = {
    println("myclass")
  }
}
输出结果:
抽象类的构造方法
特质的构造方法
myclass 的构造方法

九.其他

1.类型检查和转换

(1)obj.isInstanceOf[T]:判断obj是不是T类型。
(2)obj.asInstanceOf[T]:将obj强转成T类型。
(3)classOf[T]:获取对象的类名。

object TestInstanceOf {
  def main(args: Array[String]): Unit = {
    //创建一个Person23对象
    val per:Any = new String

    //判断per是否是Person23类型
    val res: Boolean = per.isInstanceOf[Person23]
    println(res)

    if(res){
      //将Any类型的对象per强转为Person23类型   注意:强转需要存在继承关系
      val p1: Person23 = per.asInstanceOf[Person23]
    }

    //获取类型的信息
    val clz: Class[Person23] = classOf[Person23]
    println(clz)

  }
}

class Person23{}

2.枚举类和应用类

说明:

  • 枚举类:需要继承Enumeration
  • 应用类:需要继承App
object TestEnum {
  def main(args: Array[String]): Unit = {
    println(Color.BLUE)
    //输出:blue
  }
}
// 枚举类:object修饰
object Color extends Enumeration {
  val BLUE = Value(1,"blue")
  val RED = Value(2,"red")
  val GREEN = Value(3,"green")
}

// 应用类:其可以直接执行
object Test extends App {
  println("xxxxxxxxxxx")
}

3.Type定义新类型

使用type关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名。

object Test {
    def main(args: Array[String]): Unit = { 
    	//S本质就是String的别名  
        type S = String
        var v:S = "abc"
        def test():S = "xyz"
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
scala是一门以java虚拟机(JVM)为目标运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言。 scala是纯粹的面向对象的语言。java虽然是面向对象的语言,但是它不是纯粹的,因为java的基本数据类型不是类,并且在java还有静态成员变量和静态方法。相反,scala是纯粹面向对象的,每个值都是对象,每个操作都是方法调用。 scala也是一个成熟的函数式语言。函数式编程有两个指导思想:①函数是头等值,也就是说函数也是值,并且和其他类型(如整数、字符串等)处于同一地位,函数可以被当作参数传递,也可以被当作返回值返回,还可以在函数定义函数等等;②程序的操作应该把输入值映射为输出值而不是就地修改,也就是说函数调用不应产生副作用,虽然函数式编程语言鼓励使用“无副作用”的方法,但是scala并不强制你必须这么做。scala允许你使用指令式的编程风格,但是随着你对scala的深入了解,你可能会更倾向于一种更为函数式的编程风格。向函数式编程转变,你就应该尽量去使用val、不可变对象、无副作用方法,而不是var、可变对象、有副作用方法。要明白的是,从指令式编程向函数式编程的转变会很困难,因此你要做好充分的准备,并不断的努力。 scala运行于JVM之上,并且它可以访问任何的java类库并且与java框架进行互操作,scala也大量重用了java类型和类库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值