<Zhuuu_ZZ>Scala(六)伴生类和伴生对象&特质&斜变、逆变、不变

伴生类和伴生对象&特质&斜变、逆变、不变

伴生类和伴生对象

  • 对比着Java的无参构造、有参构造、静态方法来看
public class MyStaticClass {
    public static String name="大唐";
    public static int age=289;
    public MyStaticClass(){ //无参构造
    }
    public MyStaticClass(String name,int age){ //有参构造
        this.name=name;
        this.age=age;
    }
 public static void showMsg(){
     System.out.println(name+"帝国共传承"+age+"年");
 }
    public static void main(String[] args) {
        MyStaticClass.showMsg();
    }

  • 伴生类和伴生对象在同一个文件中,名字相同,class类成为object类的伴生类,object称为class类的伴生对象
  • 伴生类可以直接访问伴生对象的私有属性和方法,伴生对象不能直接访问伴生类。
class Student(sname:String,sage:Int) {//主构造方法,在类头
  private val name: String = "zhu"
  val age: Int = 22

  def this(){  //从构造方法
    this("***",0) //必须是第一行,调用它进入主构造方法
  }
  def this(sage:Int){//有一个属性就要给另一个初始值。
    this("***",sage)
  }
  def this(sname:String){
    this(sname,0)
  }
 // def this(sname:String,sage:Int){ 没有这种从构造,因为这是主构造的方法。不能抢。
 // this(sname,sage)
 // }
  def cClass(): Unit = {
    println(sname + "年龄为:" + sage+",他的女朋友叫"+Student.oname+",年龄为:"+Student.oage)
//    Student.oObject()    //伴生类中我可以直接伴生对象名.属性、方法
  }
}
object Student {
  private val oname: String = "tang"
  val oage: Int = 19

  def oObject(): Unit = {
    println(oname + "芳龄为:" + oage)
  }

  //用函数访问伴生类的属性方法,其实也是新建一个对象。
  def visitBSLei(stu:Student):Unit={
    stu.cClass()
    println(stu.age)
    println(stu.name)
  }
  visitBSLei(Student())
  //在伴生类中定义了从构造方法后,就可以在伴生对象类中执行apply以省去在建立伴生类对象时new关键字
  def apply(sname: String, sage: Int): Student = new Student(sname, sage)
  def apply(sname: String): Student = new Student(sname)
  def apply(sage: Int): Student = new Student( sage)
  def apply(): Student = new Student()

  def main(args: Array[String]): Unit = {
   val banShengLei=new Student("lala",20)  //这里我只有new出一个对象才能访问伴生类中属性和方法
//  banShengLei.cClass()
    val banShengLei2=new Student("lala",20) //这里我再创建一个伴生类的对象,它会再次进入伴生类中从上至下走一遍创建一个不同的对象
//    if(banShengLei.eq(banShengLei2)) println(1) else println(2) //结果为2 可以看出即使输入的参数是一样,它也不是同一个对象。
    val banShengDuiXiang=Student   //在这里我不new的话,其实创建的是伴生对象类的对象。单纯只有这个时,执行main方法会把这个类从上至下走一遍最后进入main里面而且不会执行这个操作。
//    banShengDuiXiang.oObject()
    val banShengDuiXiang2=Student  //再创建伴生对象的对象时,它只从开始到结束走一遍,即只创建一个对象。
  //如果没有以上的apply方法需要new
    val stu1=new Student()
    val stu2=new Student(18)
    val stu3=new Student("haha")
    val stu4=new Student("hah",18)
    //apply函数之后就不要new了可以直接创建伴生类对象
    val stuApply1=Student("1",1)//会先在object里面从上至下走一遍然后进入main方法执行这个对象创建进入伴生类从上至下走一遍然后进入def this(sname:String,sage:Int)从构造方法再进入主构造。
    val stuApply2=Student("2")
    val stuApply3=Student(3)
    val stuApply4=Student()  //这个看上去和val banShengDuiXiang=Student很相似,但就是因为少一个括号导致这个是调用从构造方法,那个是创建伴生对象的对象。
    stuApply1.cClass()  //1年龄为:1,他的女朋友叫tang,年龄为:19
    stuApply2.cClass()  //2年龄为:0,他的女朋友叫tang,年龄为:19
    stuApply3.cClass()  //***年龄为:3,他的女朋友叫tang,年龄为:19
    stuApply4.cClass()  //***年龄为:0,他的女朋友叫tang,年龄为:19
  }
}

特质

*纵观全局,其实可以看出,Dog类是在创建类时with加上特质,然后调用时直接创建dog对象就欧克了
而Cat类创建类时只是简单的继承Animal,它需要在调用时创建类对象时with加上特质
简而言之,也就是一个是在原来的类加特质,一个是在调用时添加特质
显然第二种更加方便,而且不改动原代码就是我们程序员最大的贡献。
*/
trait Type4{
  def fly:String={
    "可以飞"
  }
  def eat:Unit={}  //带着大括号说明有函数体,那么下面with特质时可重写可不重写随意
   def sleep        //返回参数省掉是因为可以自动推导,没有大括号说明没有函数体,本身没有实现,那么下方就必须重写。
                    //以上两条规则在抽象类中同样应用,不过是在继承抽象类时应用规则。
}
trait Type5{
  def jiao:Unit={
    println("可以叫")
  }
}
trait  Type6{
  def run:Unit={
    println("可以跑")
  }
}
  abstract class Animal{
    def haha:Unit
    def yaya():Unit={
      println("yaya")
    }
  }
class Cat extends Animal {
  override def haha(): Unit = { //如果方法定义时没有输入参数省掉了小括号,那么下面实现时就没有
                               //如果没有输入参数但是带着小括号,那么下面实现时可有可没有。
    println("lala")
  }
}
class Dog extends Animal with Type4 with Type5 with Type6 {
  override def haha: Unit = {
    println("狗在猖狂的大笑")
  }
  override def sleep: Unit = {
    println("哈某睡觉去了")
  }
}

object demo{
  def main(args: Array[String]): Unit = {
    val cat:Cat with Type4 with Type5 with Type6=new Cat with Type4 with Type5 with Type6 {
      override def eat: Unit = {
        println("吃")
      }
      override def sleep: Unit = {
        println("睡觉了")
      }
    }
    println(cat.fly)
    cat.jiao
    cat.run
    cat.yaya
    cat.haha
    cat.eat
    cat.sleep
    val dog=new Dog
    println(dog.fly)
    dog.sleep

  }
}

不变、协变、逆变

object covariantAndContravariant {
  //协变点(covariant) 和 逆变点(Inversion)和 不变(invariant)
  class Animal {
    def eat(): Unit = {
      println("动物要吃食物")
    }
  }
 class Cat extends Animal{
   override def eat():Unit=println("猫吃鱼")
 }
  class Tiger extends Cat{
    override def eat(): Unit =println("老虎吃肉")
  }
  class Invariant[T]{  //泛型类不变

  }
  class Covariant[+T]{  //泛型类协变

  }
  class Inversion[-T]{   //泛型类逆变

  }
  def main(args: Array[String]): Unit = {
    val cat:Cat=new Cat
    val tiger:Tiger=new Tiger
    val cat2:Cat=new Tiger
    val cat3:Cat=tiger  //父类引用指向子类对象
    //泛型类不变---意味着就类型所限定不能改变
    val bubian:Invariant[Cat]=new Invariant[Cat]
    val bubianTiger:Invariant[Tiger]=new Invariant[Tiger]
//    val bubianCat2:Invariant[Cat]=bubianTiger  //报错,在泛型类不变的情况下,Invariant[Cat]和Invariant[Tiger]没有任何关系。

    //泛型类协变---类似于Java中的父类引用指向子类对象,即向上转型,也就是说老虎是猫类是没问题的
   val xiebianCat:Covariant[Cat]=new Covariant[Cat]
   val xiebianTiger:Covariant[Tiger]=new Covariant[Tiger]
    val xieCat2:Covariant[Cat]=xiebianTiger
    val xieCat3:Covariant[Cat]=new Covariant[Tiger]

    //泛型类逆变---类似于Java中的子类引用指向父类对象,即向下转型,也就是说猫类就是一个老虎也是没问题的
    val nibianCat:Inversion[Cat]=new Inversion[Cat]
    val nibianTiger:Inversion[Tiger]=new Inversion[Tiger]
    val niTiger2:Inversion[Tiger]=nibianCat
    val niTiger3:Inversion[Tiger]=new Inversion[Cat]
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值