类(class)
- 类通过class关键字定义
- 类通过new关键字创建实例
- 类拥有成员变量和方法
- 类的成员默认为public,也支持private,protected
- 类中无法定义静态成员变量和方法
- 类无需明确定义构造方法,通过构造参数列表声明为类的一部分
构造器:
//Book后面的括号如果没有参数可以省略,为类的主构造器
class Book(){
var title:String="" //成员变量
var author:String=""
//声明类的辅助构造器
def this(titlt:String,author:String)={
this() //第一句必须调用其它构造器 这里调用的是主构造器
this.author=author
this.title=titlt
}
//成员方法
def show(title:String,author:String)={
println("书名是:"+title+"作者是:"+author)
}
}
类的实例化
object Book{
def apply(titlt: String, author: String): Book = new Book(titlt, author)
def main(args: Array[String]): Unit = {
val book1 = Book("hello","zhangsan")
val book2 = Book("hello1","zhangsan1")
val book3 = Book("hello2","zhangsan2")
book1.show(book1.title,book1.author)
}
}
类的继承
使用关键字“extends”实现继承
子类重写父类方法必须使用“override”关键字
class Story extends Book {
override def show(title: String, author: String): Unit = {
println("小说的名字是:"+title+"作者是:"+author)
}
}
抽象类(abstract)
- 抽象类可以包含未实现的方法,即抽象方法
- 抽象类无法实例化
- 抽象类使用“abstract”关键字修饰
- 子类重写父类抽象方法时,“override”关键字可选
- 子类重写父类非抽象方法,“override”关键字必写
- 只要是重写父类的方法,不管是不是抽象方法,都加上override准没错
//创建一个父类
abstract class Shape {
//写一个抽象方法
def draw():Unit
//写一个非抽象方法
def write={
println("写代码")
}
}
//创建一个子类继承
class Circle extends Shape {
override def draw(): Unit = {
println("画一个圆")
}
}
//单例对象 main方法必须定义在object中 无法定义在class中
object Test3{
def main(args: Array[String]): Unit = {
val circle = new Circle
circle.draw()
circle.write
}
}
单例对象(object)
Scala的类中无法定义静态成员,即无static关键字。如何像Java一样表达类的静态成员变量、成员方法与静态代码块?
scala解决方案:单例模式
- 使用“object”关键字声明,可包含变量、方法与代码定义
- 单例对象中的成员变量、成员方法通过单例对象名直接调用
- 单例对象第一次被访问时初始化,并执行全部代码块
- 单例对象不能new,且无构造参数
- 程序入口main()方法必须定义在单例对象中
- 单例对象与同名类定义在同一文件中时形成绑定关系–伴生(Companion)
//创建一个子类继承
class Circle extends Shape {
override def draw(): Unit = {
println("画一个圆")
}
}
//单例对象 main方法必须定义在object中 无法定义在class中
object Test3{
def main(args: Array[String]): Unit = {
val circle = new Circle
circle.draw()
circle.write
}
}
伴生(Companion)
- 单例对象与同名类定义在同一文件中时形成绑定关系
同名类称为单例对象的伴生类
单例对象称为同名类的伴生对象 - 伴生类与伴生对象可相互访问各自私有成员
- 伴生对象可为伴生类增加静态成员
class Student(n: String, a: Int) {
private var name: String = n
var age: Int = a
}
object Student {
//apply方法创建一个Student对象,后面是用的时候不用new
def apply(n: String, a: Int): Student = new Student(n, a)
def main(args: Array[String]): Unit = {
val stu1 = Student("zhangsan", 12)
println(stu1.age)
println(stu1.name)
//自带apply,新建对象不用new
val list = List(1, 2, 3, 4)
}
}
特质(trait)
- scala中没有接口(interface)的概念
- 特质用于在类之间共享程序接口和字段,类似Java接口
- 特质是字段和方法的集合,可以提供字段和方法的实现
- 类和单例对象都可以扩展特质(extends)
- 特质不能被实例化,因此没有构造参数,类似Java接口
- 特质使用“trait”关键字定义
- 实现特质中的方法使用“override”
import scala.collection.mutable.ArrayBuffer
//声明一个特质
trait Pet {
val name: String //属性
def cry():Unit //方法
}
//创建一个类实现这个特质
class Dog(val name: String) extends Pet{
//重写特质的方法
override def cry()=println("wow ...")
}
//创建一个单例对象用来测试
object Test001 extends App {
val dog = new Dog("Harry")
val animals = ArrayBuffer.empty[Pet]
animals.append(dog)
animals.foreach(pet => {println(pet.name);pet.cry()}) // Prints Harry wow ...
}
混入特质
- 当某个特质被用于组合类时,被称为混入
- 一个类只能有一个父类但是可以有多个混入(分别使用关键字extends和with)
//定义一个抽象类
abstract class A{
val message:String
}
//B继承抽象类A(普通类继承抽象类必须要实现抽象方法或者变量)
class B extends A{
override val message: String ="this is B"
}
//定义一个特质(继承抽象类不需要实现抽象方法或者变量)
trait C extends A{
def mes_toUpper=message.toUpperCase()
}
class D extends B with C
object MixinTrait extends App {
val d = new D
println(d.message)
println(d.mes_toUpper)
}
动态混入特质
class Drawing {
//实现动态混入
this:Shaping=>
def start():Unit=draw()
}
trait Shaping{
def draw()={
}
}
trait Square extends Shaping{
override def draw(): Unit = println("画一个方")
}
trait T extends Shaping{
override def draw(): Unit = println("画一个三角")
}
object Test08 extends App{
//在创建对象的时候混入
private val d1 = new Drawing with Square
d1.start()
private val d2 = new Drawing with T
d2.start()
}
class Graph {
class Node {
var connectedNodes: List[Node] = Nil
def connectTo(node: Node) { //可以接受外部类对象实力中的内部类
if (connectedNodes.find(node.equals).isEmpty) {
connectedNodes = node :: connectedNodes
}
}
}
var nodes: List[Node] = Nil
def newNode: Node = {
val res = new Node
nodes = res :: nodes
res
}
}
object Test002 extends App {
val g: Graph = new Graph
val n1: g.Node = g.newNode
val n2: g.Node = g.newNode
n1.connectTo(n2) // legal
val h: Graph = new Graph
val n3: h.Node = h.newNode
n1.connectTo(n3) // illegal! n1与n3被认为是不同的对象
}
样例类(case class)
样例类常用于描述不可变的值对象(Value Object)
当一个类被声名为case class的时候,scala会帮助我们做下面几件事情:
- 1、构造器中的参数如果不被声明为var的话,它默认的是val类型的,但一般不推荐将构造器中的参数声明为var。
- 2、自动创建伴生对象,同时在里面给我们实现子apply方法,使我们在使用的时候可以不直接使用new创建对象。
- 3、伴生对象中同样会帮我们实现unapply方法,从而可以将case class应用于模式匹配。
- 4、实现自己的toString、hashCode、copy、equals方法
- 除此之此,case class与其它普通的scala类没有区别
case class Student(name:String,age:Int) //定义样例类
val stu=Student("Jason",19) //创建样例类的实例,无需new关键字
println(stu.name) //访问对象属性
- 样例类构造参数默认声明为“val”,自动实现类构造参数的getter
- 样例类构造参数声明为“var”时,自动实现类构造参数的setter和getter
- 样例类自动创建伴生对象
- 样例类自动实现的其他方法
toString()、equals()、copy()、hashCode()
伴生对象中的apply()、unapply()
实例样例类:
//声明一个样例类 属性是name和age
case class Student(name:String,age:Int)
//创建一个测试类
object Test003{
def main(args: Array[String]): Unit = {
//样例类会自动创建apply方法,所以创建实例的时候不用new
val stu = Student("zhangsan",19)
//样例类自动实现的一些方法
println(stu.toString)
val stu1=stu.copy()
println(stu1.eq(stu))
println(stu1.equals(stu))
}
}
结果:
Student(zhangsan,19)
false
true
样例类与枚举
object Weekday extends Enumeration {
val Mon,Tue=Value //枚举值从0开始计数
}
object Test09{
def main(args: Array[String]): Unit = {
println(Weekday.Mon)
println(Weekday.Mon.id) //枚举的使用
}
}
样例类与枚举区别
1)枚举更简单,代码更少
2)样例类的字段比枚举的值更强大
3)样例类可扩展
泛型类
泛型类指可以接受类型参数的类,定义泛型类使用"[]"
型变
协变
class Test[+T]
[]中使用+,表示协变,当有两种类型A和B有继承关系,B是A的子类,那么泛型类Test[+B]就是Test[+A]的子类
逆变
class Test[-T]
[]中使用+,表示逆变,当有两种类型A和B有继承关系,B是A的子类,那么泛型类Test[+A]就是Test[+B]的子类
不变
class Test[T]
那么无论AB什么关系 都不影响Test类