Scala学习——类,继承,接口(中)
基本类的使用:(初)
1 package com.dtspark.scala.basics 2 /** 3 * trait是一个接口 4 * 接口的第一次继承用extends,多继承时用with 5 * 多继承时,如果这些接口中都有同样的方法时,则是从右往左的顺序执行 6 * 抽象类不能实例化 7 * 类继承的时候不继承伴生对象object,只能看见类的接口,而其父类可以使用自己伴生对象中的方法 8 * isInstanceOf 和 asInstanceOf的使用 9 * 前者是判断是不是其子类,后者是进行类型转换,但必须是父子之间的转换,不能是完全不同的类 10 */ 11 trait Logger{ 12 def log(message:String){ 13 println("logger "+message) 14 } 15 16 } 17 /** 18 * override 是表示重写原有方法,(必须要写) 19 */ 20 trait RichLogger extends Logger{ 21 override def log(message:String){ 22 println("RichLogger:"+message) 23 } 24 } 25 26 class Loggin( name:String)extends Logger{ 27 def loggin{ 28 println("Hi,welcome! "+name) 29 log(name) 30 } 31 32 } 33 34 35 36 trait Information{ 37 38 val information:String //抽象属性 39 //def getId:String 40 def getInformation:String //抽象方法(不存在方法体) 41 def checkIn:Boolean = { 42 getInformation.equals("Spark") 43 } 44 } 45 46 class Passenger( name:String)extends Information{ 47 //不可以定义为val 原因是因为_的值不明确 48 var id:String=_ //这种方法申明的不是抽象属性,而是可变属性,其值为String的默认值null 49 override val information = name //重写抽象属性 50 override def getInformation = information //不执行任何操作,直接给返回值,相当于实现了 51 /** 52 * this关键字为构造方法,重写构造方法 53 * 副构造函数的第一行必须是构造函数 54 */ 55 def this(name:String,iid:String){ 56 this(name) 57 id=iid; 58 println("hello!!") 59 } 60 def out:String={ 61 name+" : "+id 62 } 63 64 } 65 66 object HelloTrait { 67 def main(args: Array[String]): Unit = { 68 /** 69 * 对象实例化之后又继承了新的接口,可以使用新的方法 70 * 新的接口必须继承至原有的接口,并重写新的方法 71 */ 72 val Log = new Loggin("Spark")with RichLogger 73 Log.loggin 74 75 val person = new Passenger("zcb") 76 println(person.out) 77 person.id="123" //不具有封装性 78 println(person.out) 79 val person1 = new Passenger("zcb1","1234") 80 println(person1.out) 81 val a:Boolean=person.isInstanceOf[Information]//看它是不是其子类 82 println(a) 83 } 84 }
类的继承和判断对象是否相等(中)
1 package com.dtspark.scala.basics 2 /** 3 *class Creature { 4 * val range: Int = 10 5 * val env: Array[Int] = new Array[Int](range) 6 *} 7 8 *class Ant extends Creature { 9 * override val range = 2 10 *} 11 * 在构造时,发生的过程如下: 12 * Ant构造器在构造自己之前,调用超类构造器; 13 * Creature的构造器将range字段设为10; 14 * Creature的构造器初始化env数组,调用range字段的getter; 15 * range的getter被Ant类重写了,返回的Ant类中的range,但是Ant类还未初始化,所以返回了0; 16 * env被设置成长度为0的数组 17 * Ant构造器继续执行,将range字段设为2 18 * ------------------------------------------------------------- 19 * 第二种写法是先初始化sporter的成员,然后在初始化父类的成员,这样可保证将数据传给父类,with后面接要继承的类 20 * class Person10{ 21 * val counter=3 22 * val counterArray =new Array[Int](counter) 23 * } 24 25 * class Sporter extends { 26 * override val counter = 5 27 * }with Person10 28 29 **/这样初始化后sporter的counterArray的长度为5 30 class Person10{ 31 val counter=3 32 val counterArray =new Array[Int](counter) 33 } 34 35 class Sporter extends { 36 override val counter = 5 37 }with Person10 38 39 //判断两个对象是否相同的方法 40 class Programmer(val name:String,val salary:Double){ 41 final override def equals(other:Any)={ 42 val that = other.asInstanceOf[Programmer] 43 if(that==null)false 44 else name == that.name&&salary==that.salary 45 } 46 /** 47 * 1.hashCode()方法存在的主要目的就是提高效率。 48 * 2.在集合中判断两个对象相等的条件,其实无论是往集合中存数据, 49 * 还是从集合中取数据,包括如果控制唯一性等,都是用这个条件判断的,条件如下: 50 * 首先判断两个对象的hashCode是否相等,如果不相等,就认为这两个对象不相等,就完成了。 51 * 如果相等,才会判断两个对象的equals()是否相等,如果不相等,就认为这两个对象不相等, 52 * 如果相等,那就认为这两个对象相等。 53 54 * 所以说重写equals时必须重写hashcode,这是良好的习惯 55 */ 56 final override def hashCode=name.hashCode()*5+salary.hashCode()*9 57 58 } 59 60 //object OverrideField { 61 // def main(args: Array[String]): Unit = { 62 // val s = new Sporter 63 // println(s.counterArray.length) 64 // } 65 //} 66 //第二种写法,不用写main函数 67 object OverrideField extends App{ 68 val s = new Sporter 69 println(s.counterArray.length) 70 }
注:对于无序表寻找是否有相同的对象时,使用hashcode可以大大提高速度。equal的比较通常比较耗资源,可以先比较hashcode后,确认hashcode相同时再用equal比较。
hashcode相同,equal不一定相同;equal相同的hashcode一定相同。
类的提取器:(中)
1 package com.dtspark.scala.basics 2 /** 3 * 类的提取器 4 * 注解要搞懂下面两个 5 * @transient 注解将字段标记为瞬态的 6 * @volatile 注解标记为易失的 7 * 8 * 自己编写的注解要继承annotation.Annotation 9 */ 10 case class Person1(name:String ,age:Int) 11 12 class DTCoder(val name:String ,val salary:Int) 13 object DTCoder{ 14 //复写了其apply和unapply方法 15 def apply(name:String,salary:Int)={ 16 println("DTCoder apply method invoked!!!") 17 new DTCoder(name,salary) 18 } 19 // 方法1 (可使一个字符串转换成一个对象) 20 // def unapply(information:String)={ 21 // //根据空格区分参数 22 // println("DTCoder unapply method invoked!!!") 23 // Some((information.substring(0 , information.indexOf(" ")),information.substring(information.indexOf(" ")+1))) 24 // } 25 //方法二 26 def unapply(information:DTCoder)={ 27 //根据空格区分参数 28 println("DTCoder unapply method invoked!!!") 29 Some((information.name,information.salary)) 30 } 31 } 32 33 object HelloExtractor { 34 def main(args: Array[String]): Unit = { 35 val person=Person1("Spark",12)//调用apply的工厂构造方法,构造出类的实例 36 //= val person =Person1.apply("Spark",12) 37 val Person1(name,age)=person//调用Person1的unapply的方法把person实例中的name和age提取出来 38 println(person.name+" : "+person.age)//实现了name和age的赋值。 39 40 person match{ //val Person1(name,age)=person=match这个过程 41 case Person1(name,age)=>println(name+" : "+age) 42 } 43 //针对unapply方法一 44 //val DTCoder(dtname,salary) = "Spark 16000000" 45 46 //针对unapply方法二 47 val coder = DTCoder("Spark",2999999) 48 val DTCoder(dtname,salary)=coder 49 } 50 }
类的伴生对象:(高)
添加类的内容:
1 class Marker ( color:String) 2 class Marker (val color:String) 3 class Marker (var color:String)
这两种申明是有差的,第二种和第三种申明都可以通过Marker的对象+"."直接调用(相当于这个属性是静态的),而第一种申明不行
1 class Marker private (val color:String) { 2 3 println("创建" + this) 4 5 override def toString(): String = "颜色标记:"+ color 6 7 }
private写在类名和参数中间表示对主构造函数的私有化,这时是不能new出对象的(除非有伴生对象object或者有重构this构造函数)
1 def main(args: Array[String]) { 2 new Marker("black")//报错 3 }
第一种解决方案:
添加object后,同时要有apply方法,否则还是报错(伴生对象是不带参数的,但是apply方法需要与原类相同的参数)
由于有apply方法的存在,创建新对象时,不需要用new关键字,
用object初始化时,他会直接初始化object中存在的所有变量。
1 object zcb01 { 2 def main(args: Array[String]) { 3 Marker("white") //直接对象名进行初始化 4 } 5 6 } 7 class Marker (val color:String) { 8 9 println("创建" + this) 10 11 override def toString(): String = "颜色标记:"+ color 12 13 } 14 15 16 object Marker{ 17 18 private val markers: Map[String, Marker] = Map( 19 "red" -> new Marker("red"), 20 "blue" -> new Marker("blue"), 21 "green" -> new Marker("green") 22 ) 23 private val m = Set(new Marker("black")) 24 def apply(color:String) = { 25 if(markers.contains(color)) markers(color) else null 26 println("我输入的颜色:"+color) 27 } 28 29 }
结果:(先将object中的元素初始化,然后再调用apply方法初始化当前对象)
创建颜色标记:red
创建颜色标记:blue
创建颜色标记:green
创建颜色标记:black
我输入的颜色:white
第二种解决方案:
重构构造器(创建对象时必须用new,因为object(apply方法)不支持除主构造以外的任何重载构造方式)
1 object zcb01 { 2 def main(args: Array[String]) { 3 new Marker("white",4) 4 } 5 6 } 7 class Marker private (val color:String) {//主构造的形式与类名后面的形式相同 8 9 println("创建" + this) 10 var a=1; 11 12 def this(color:String,a:Int){ 13 this(color)//必须先调用主构造器 14 this.a=a; 15 } 16 17 override def toString(): String = "颜色标记:"+ color 18 19 }