Scala总结之基础练习和面向对象

Scala总结二之基础练习和面向对象

  • 一. scala基础练习
    • 1.1. 字符串转换为数字
    • 1.2. 使用scala完成选择排序
    • 1.3. 使用scala完成二分查找
  • 二. scala面向对象
    • 2.1. 类的基本操作
      • 2.1.1. 类的创建与对象的构造
      • 2.1.2. 成员变量的封装getter和setter
      • 2.1.3. case class模拟javabean
      • 2.1.4. scala类的构造器
      • 2.1.5. 嵌套类
      • 2.1.6. 对象object
      • 2.1.7. 伴生对象和伴生类
      • 2.1.8 没有main函数如何执行scala程序
    • 2.2. 继承体系
      • 2.2.1. 扩展类
      • 2.2.2. 重写方法
      • 2.2.3. 类型检查和转换
      • 2.2.4. 受保护字段和方法
      • 2.2.5. 超类的构造
      • 2.2.6. 匿名子类
      • 2.2.7. 抽象类和抽象字段
      • 2.2.8. trait特质

一. scala基础练习

1.1. 字符串转换为数字

不使用str.toLong,str.toInt/Integer.valueOf()/Long.valueOf/Integer.parseInt()等,将字符串"123456789" 转化成数字123456789
Scala总结之基础练习和面向对象_第1张图片

def method3(): Unit = {
    val str = "123456789"
    var sum = 0L
    for(ch <- str) {//字符串本身就是一个字符数组
        sum = sum * 10 + (ch - '0')
    }
    println(sum)

    str.toCharArray.map(ch => ch - '0').reduce((v1, v2) => v1 * 10 + v2)
}

1.2. 使用scala完成选择排序

   def main(args: Array[String]): Unit = {
       val array = Array(1, 5, 3, 5, 6, 7, 2, 9)
       println("排序前的数组:" + array.mkString("[", ", ", "]"))
       selectSort(array)
       println("排序后的数组:" + array.mkString("[", ", ", "]"))
   }
   /**选择排序
     *
     */
   def selectSort(arr:Array[Int]): Unit = {
       for(i <- 0 until arr.length) {
           for (j <- i until arr.length) {
               if(arr(i) > arr(j)) {
                   swap(arr, i, j)
               }
           }
       }
   }
   /*
       位运算 ^ 相同为0,反之为1
       a=3 b=5
       a=0011
       b=0101
       a= a^b =0110
               0101
       b= a^b =0011=3
               0110
       a= a^b= 0101=5
       这里没有使用第三方变量完成两个数字的交换,其中使用^效率最高
    */
   def swap(arr:Array[Int], i:Int, j:Int): Unit = {
       /*arr(i) = arr(i) ^ arr(j)
       arr(j) = arr(i) ^ arr(j)
       arr(i) = arr(i) ^ arr(j)*/
       arr(i) = arr(i) + arr(j)
       arr(j) = arr(i) - arr(j)
       arr(i) = arr(i) - arr(j)
   }

1.3. 使用scala完成二分查找

//  在一个有序数组中找到某一个值,如果存在,返回索引,没有返回-1或者返回如果存在,应该在的位置
def binarySearch(arr:Array[Int], key:Int):Int = {
        var start = 0
        var end = arr.length - 1
        while(start < end) {
            val mid = (start + end) / 2
            if (key < arr(mid)) {
                //左边
                end = mid - 1
            } else if (key > arr(mid)) {
                //又变
                start = mid + 1
            } else {
                return mid
            }
        }
        return -(start + 1)
    }

二. scala面向对象

2.1. 类的基本操作

2.1.1. 类的创建与对象的构造

* 定义scala中的类使用关键字class
*     1、定义scala中的任意一种结构,都不可以使用public关键字修饰,因为scala中没有public关键字
*         不加任何访问权限修饰符就相当于java中的public
*     2、类中可以定义成员信息
*         成员变量
*         成员方法
*     3、创建类的实例--对象
*         在scala中和java中是一样,都使用关键字new来构建类的实例
object ClassOps {
    def main(args: Array[String]): Unit = {
        val p:Person = new Person()
        p.name = "郭靖"
        p.age = 18
        p.show()
    }
}

class Person {
    var name:String = _
    var age:Int = _

    def show(): Unit = {
        println(s"name: ${name}\tage: ${age}")
    }
}

2.1.2. 成员变量的封装getter和setter

*     4、scala没法直接为成员变量提供getter和setter方法,只能自己编写
*         scala做了一种尝试,通过注解(@BeanProperty)的方式来给成员变量提供getter和setter,
*         前提是该getter或者setter不能被private修饰,此时数据的安全性无法得到保证
*
*     5、这个getter和setter的使用其实javabean中的规范,javabean的主要作用是什么?
*         封装、传递数据

object ClassOps {
    def main(args: Array[String]): Unit = {
        val p:Person = new Person()
//        p.name = "郭靖"
//        p.age = 18

        p.setName("黄蓉")
        p.setAge(-2)
        p.setSalary(123.0f)
        p.show()
    }
}

class Person {
    private var name:String = _
    private var age:Int = _

    @BeanProperty var salary:Float = _
    def setName(n:String) = {
        name = n //单行函数
    }

    def getName = name

    def setAge(a:Int) = {
        if(a < 0) {
            throw new RuntimeException(" 赋值错误!")
        }
        age = a
    }

    def getAge = age

    def show(): Unit = {
        println(s"name: ${name}\tage: ${age}")
    }
}

2.1.3. case class模拟javabean

/*
     在scala中一般不用这些普通的class类进行封装数据、传递数据,那用什么呢?
  *         case class样例类
  *         作用就相当于java bean
  *   case class的定义非常简单,在class关键字的前面加上另外一个关键字case即可
  *   
  *   样例类的定义必须要有一个参数列表---->构造器,
  *   case class的应用是非常广泛的,但凡数据的传递,一般都用case class
 */
object _02CaseClassOps {
    def main(args: Array[String]): Unit = {
		val category = Category(1, "手机")
        println(category.id)
        println(category.name)
    }
}
//定义了一个case class Category
case class Category(id:Int, name:String)

2.1.4. scala类的构造器

*  2、构造器:
*     按照java中的知识,val stu = new Student是使用Student类的无参构造器创建对象
*     在一个类中,如果局部变量和成员变量名发生冲突,便通过给成员变量加this关键字进行区分
*  3、如何定义构造器:
*        尝试使用def Student(name:String, age:Int)定义构造器,
*        调用的时候:
*         new Student("郭靖", 20) too many arguments to constructor报错
*         所以该方法就不是scala中的构造器
*  4、scala的构造,分为主构造器和辅助构造器,
*     主构造器的定义和类的定义交织在一起,如何去定义一个主构造器
*     class Xxx(参数列表) {
*     }
*     类名后面的内容就是主构造器,如果参数列表为空的话,()可以省略
*     主构造器的函数体,就是类体的内容,所以如果我们使用主构造器创建对象
*  5、scala的类有且仅有一个主构造器,要想提供更加丰富的构造器,就需要使用辅助构造器
*     def this(参数列表)
*     scala中的辅助构造器,在函数体的第一行,必须以调用其它辅助构造器或者主构造器开始
*     也就是说要使用this(参数列表)去调用其它构造器
*     但是归根到底,一个辅助构造器最终还是要从主构造器的调用开始
*  6、scala和java的构造器的区别
*     java的构造器没有主构造器和辅助构造器之分,但是有默认的无参构造器和有参构造器之分
*     scala中默认的构造器就是类名后面的构造器,被称之为主构造器,同时还拥有辅助构造器
*     java的构造器名称和类名一直,而scala中的构造器名称就是this,其余和java一模一样
class Student(n:String, a:Int) {

    private var name:String = _
    private var age:Int = _

    def Student(name:String, age:Int): Unit = {
        this.name = name
        this.age = age
    }
    //辅助构造器
    def this() {
        this("黄蓉", 18)
        println("---辅助构造器def this()-----")
    }

    def this(age:Int) {
        this()
        this.age = age
        println("---辅助构造器def this(age:Int)-----")
    }

    def show(): Unit = {
        println(s"name: ${n}\tage: ${a}")
    }
    println("如果这是构造器的方法体的话,这句话应该会被调用!")
}

2.1.5. 嵌套类

​ scala中称之为嵌套类,在java中称之为内部类

java中的成员内部类实例

public class InnerClassOps {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer().new Inner();
        oi.show();
    }
}
class Outer {
    class Inner {
        public void show() {
            System.out.println("inner show");
        }
    }
}

​ 为啥要有内部类?

​ 从业务逻辑上理解,定义复杂是否在外部类内部定义更加合理,这样便有了内部类,比如,定义一个类Person,类有心脏,Heart优势一个复杂的抽象事物,显然应该把Heart定义在Person内部更加的准确与合理。

​ scala的内部类如何定义

object _03InnerOps {
    def main(args: Array[String]): Unit = {
        val outer = new Outer
        val inner = new outer.Inner()
        inner.show()
    }
}

class Outer { oo => //外部类的引用
    var x = 5
    class Inner {
        var x = 6
        def show(): Unit = {
            var x = 7
            println("Inner: x=" + x)//7
            println("Inner: x=" + this.x)//6
            println("Inner: x=" + Outer.this.x)//5
            println("Inner: x=" + oo.x)//5 简写方式
        }
    }
}

2.1.6. 对象object

​ scala并没有像java中的静态,所以按照java中的观点的话,主函数是没有办法被执行public static void main(xxx)

​ scala为了来模拟java中的static这个关键字,设计出了object这一结构,它是和class平级。

​ 在object定义的方法我们可以理解为函数,class中的行为称之为方法,而且在object中定义的变量和函数都是可以当做java中的静态来进行调用。

object _04ObjectOps {
    def main(args: Array[String]): Unit = {
        val ret = Tool.add(13, 14)//相当于java的静态方法
    }
}
object Tool {
    val x = 5
    def add(a:Int, b:Int) = a + b
}

如何在scala中去定义单例对象呢?

java中的单例

/**
 * 单例
 *      饿汉式
 *      懒汉式
 *  一个类只能创建一个对象,
 *  定义的步骤:
 *   恶汉式:
 *      1、私有构造器
 *      2、提供一个public的static的返回值为本类引用的方法
 *      3、为了给第2步中提供实例,创建一个private的static的成员变量
 *   懒汉式:
 *      1、私有构造器
 *      2、创建一个private的static的成员变量,没有初始化
 *      3、提供一个public的static的返回值为本类引用的方法
 */

饿汉式:

class Singleton {//饿汉式
    private Singleton(){}
    //成员位置
    {//构造代码块

    }
    private static Singleton instance = new Singleton();
//    static {
//        instance = new Singleton();
//    }

    public static Singleton getInstance() {
        return instance;
    }
}

懒汉式:

class Singleton {
    private Singleton(){}
    private static Singleton instance;

    public static Singleton getInstance() {
        if(instance == null) {
            synchronized (Singleton.class) {
                if(instance == null)
                    instance = new Singleton();
            }
        }
        return instance;
    }
}

scala中的单例

object _05SingletonOps {
    def main(args: Array[String]): Unit = {
        val s1 = Singleton
        val s2 = Singleton
        println("s2.x=" + s2.x)//1
        s1.x = 5
        println("s1 == s2? " + (s1 == s2))//true 单例
        println("s2.x=" + s2.x)//5
    }
}
object Singleton {
    var x = 1
}

2.1.7. 伴生对象和伴生类

​ 为什么要有伴生对象?

​ 我们都知道,在scala中是没有静态这个概念,而在java中一个类是既可以有非静态的成员,也可以有静态成员,表达非常丰富。scala由于没有静态的概念,类只能拥有非静态成员。所以scala为了弥补这个缺憾,定义这么一个和该类同名的object结构,而且该object结构必须要和该类在同一个.scala源文件中被定义

​ 这样我们就可以让该类拥有了静态和非静态的成员。

把这个和类同名的object称之为该类的伴生对象,反过来,该类称之为该object的伴生类。

object _06CompanionOps {
    def main(args: Array[String]): Unit = {
        val worker = new Worker("乔峰", 38)
        worker.show()
        println("-----------------------------")

        val w1 = Worker()
        w1.show()
        println("静态属性:" + Worker.x)
        val w2 = Worker("段誉", 20)
        w2.show()

//        val arr = new Array[Int](5)
//        val arr1 = Array(1, 2, 3, 4)
    }
}

class Worker /*private ()*/ {
    private var name:String = _
    private var age:Int = _

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

    def show(): Unit = {
        println(s"name:${name}, age:${age}")
    }
}

object Worker {
    var x = 123
    //apply方法必须要重写
    def apply(): Worker = new Worker()

    def apply(name: String, age: Int): Worker = new Worker(name, age)
}

2.1.8 没有main函数如何执行scala程序

object _07AppOps extends App {
    /*def main(args: Array[String]): Unit = {
        println("xxxxxx")
    }*/
    println(args.mkString("[", ",", "]"))
    println("yyyyyyyyyyyy")
}
extends:扩展
trait:特质

2.2. 继承体系

2.2.1. 扩展类

2.2.2. 重写方法

* scala的继承或者扩展
*    如何去实现继承或者扩展,在scala中使用的关键字和java的继承是一模一样extends
*    总结:
*     1、子类可以拥有父类的非私有的成员(成员变量和成员方法)
*     2、子类可以扩展|复写父类的方法,但是在复写的时候,必须要添加override关键字(除非该方法是抽象方法)
*     3、哪些成员无法被子类继承
*         private
*             如果private关键字不加如何限制,就和java中的private作用是相同的,但是我们在scala中可以更加精准
*          的控制被private或者被protected关键字修饰的成员的权限。
*             private[可以被访问的包package]
*                 这就意味着,该成员可以在包package,及其子包下面被访问
*             private[this] <=> private
*                 只能在前类中被访问
*         static scala中没有
*         final
*     4、子类要想使用父类的某些行为,可以使用super关键字来调用,和java中一致
class Dog {
    private[extendz] var id:String = "哺乳类的人类的好朋友"
    var eyes:Int = 2
    var name:String = _

    def eat(): Unit = {
        println("吃吃吃")
    }

    private def cry(): Unit = {
        println("汪汪汪汪")
    }
}

class ZHTYQ extends Dog {
    name = "大黄"

    override def eat(): Unit = {
        super.eat()
        println("大黄啃骨头。。。")
    }
    id = "haha"
    def keepDoor(): Unit = {
        println("大黄在看家")
    }
}

2.2.3. 类型检查和转换

​ 多态:一种事物有多重状态的表现,是继承体系中非常重要的概念。

​ 反映到编程语言中的体现:父类引用指向子类对象。这个在java中是很明显的,但是在scala不明显,因为一般在定义变量的时候,可以不添加类型,直接做类型推断,所以要想很直接的体现出,就应该像java定义那样,eg.

val obj:Fu = new Zi() —> 父类引用指向子类对象 val obj = new Zi()

​ 同时在进行多态操作中,一般都会涉及到类型转化,scala中也有类似java中的类型检查和类型转化,不同之处在于具体操作方式略有差异,进行类型检查是:引用.isInstanceof[类型],进行类型转化使用引用.asInstanceof[类型]。

class Person {
    var name:String = _
    var age:Int = _
    def this(name:String, age:Int) {
        this()
        this.name = name
        this.age = age
    }
    override def equals(obj: scala.Any): Boolean = {
        if(!obj.isInstanceOf[Person]) {
            return false
        }
        val that = obj.asInstanceOf[Person]
        if(this.name.equals(that.name) && this.age == that.age) {
            return true
        }
        return false
    }
}

​ 但是,考虑到scala是一个非常简约的编程语言,又为大家提供了一个类型检查和转化的简化的操作方式——模式匹配,和java中switch case很像,但是要比java中的switch case强大的多的多的多。

private def method3 = {
    val dh: Dog = new ZHTYQ
    dh.eat()
    if (dh.isInstanceOf[ZHTYQ]) {
        //会避免出现ClassCastException
        val gg = dh.asInstanceOf[ZHTYQ]
        gg.keepDoor()
    }
    println("-----------------------")
    //使用模式匹配的操作方式 和switch case很像
    dh match {
        case gg:ZHTYQ => {
            gg.keepDoor()
        }
        case p:Person => {
            p.name
        }
            //还有默认的操作
        case _ => {
            println("没有匹配到任何")
        }
    }
}

2.2.4. 受保护字段和方法

​ 所谓受保护的字段或者方法,就是被protected修饰的field或者method。

​ 被protected有啥特点?本类及其子类方法。

> **被protected[this]修饰了成员,便无法在实例(包括本类对象和子类对象)中被调用,只能在定义中被调用**

举例:

/**
* 因为salary被protected[this]修饰了,便无法在实例中被调用,只能在定义中被调用
*/
class Staff {
 var name:String = _
 var age:Int = _
 protected[this] var salary:Float = 2120

 def doWork(): Unit = {
     println(this.name + " is working sth.")
 }
}

class Programmer extends Staff {
 name = "郭靖"
 age = 52
 salary = 1000000000
 override def doWork(): Unit = {
     println(this.name + " 监督杨过练武")
 }

 def makeFields(staff: Staff): Unit = {
     println(this.name + "准备要和" + staff.name + "建立革命友谊")
     println(staff.name + "的薪资是多少呢?" + staff.salary)
 }
}

上述代码出现如下异常:
在这里插入图片描述

2.2.5. 超类的构造

/**
  * 子父类的构造
  *     子类在构造的过程中,先要初始化相关的父类构造器
  */
object _04ExtendsOps {
    def main(args: Array[String]): Unit = {
        val zi = new Zi
        zi.name = "郭靖"
        zi.age = 22
        zi.show()
    }
}

class Fu {
    println("-----Fu---main-constructor---")
    var name:String = _
    var age:Int = _
    def this(name:String, age:Int) {
        this()
        this.name = name
        this.age = age
        println("-----Fu---assist-constructor(name:String, age:Int)---")
    }
}

class Zi(gender:Int) extends Fu {
    println("-----Zi---main-constructor(gender:Int)---")
    def this() {
        this(1)
        println("-----Zi---assist-constructor()---")
    }

    def show(): Unit = {
        println(s"name:${name}, age: $age, gender:$gender")
    }
}
执行过程:
-----Fu---main-constructor---
-----Zi---main-constructor(gender:Int)---
-----Zi---assist-constructor()---
name:郭靖, age: 22, gender:1

子类调用父类有参构造

class Fu {
    println("-----Fu---main-constructor---")
    var name:String = _
    var age:Int = _
    def this(name:String, age:Int) {
        this()
        this.name = name
        this.age = age
        println("-----Fu---assist-constructor(name:String, age:Int)---")
    }
}

class Zi(name:String, age:Int, gender:Int) extends Fu(name, age) {
    println("-----Zi---main-constructor(gender:Int)---")
    def this() {//辅助构造器
        this("zhangsan", 14, 1)
        println("-----Zi---assist-constructor()---")
    }

    def show(): Unit = {
        println(s"name:${name}, age: $age, gender:$gender")
    }
}
执行过程:
-----Fu---main-constructor---
-----Fu---assist-constructor(name:String, age:Int)---
-----Zi---main-constructor(gender:Int)---
-----Zi---assist-constructor()---
name:zhangsan, age: 14, gender:1

​ 总结:

scala在加载子类构造器的同时去加载父类构造器,其道理和java中是一致的,不同点在于,scala只有主构造器才 
可以直接调用父类的构造器(主构造器和辅助构造器),辅助构造器是无法直接调用父类的构造器,因为在辅助构造
器的第一句话必须是this,去调用其他辅助构造器或者主构造器,所以辅助构造器只能通过主构造器来间接的调用父
类的构造器。

2.2.6. 匿名子类

​ 其实说白了就是匿名内部类,啥叫匿名类?匿名子类?

​ 没有名字的类就叫匿名类,没有名字的类就叫做匿名子类。

什么又叫做有名字的类。

​ public class Person{}—>这就是有名字的,定义好有名字的类。源码阶段的概念

​ new Person() {} —>这就是一个没有名字的类 —>运行时的概念

​ 匿名子类最常见的作用,一般都在某个操作仅仅出现一次的时候被使用,在方法的参数上面多出现匿名子类,

在java中出现最多的匿名类或者匿名子类,就是接口或者抽象类。

java中匿名类的使用

public class DynamicProxyOps {
    public static void main(String[] args) {

        /*
            构建一个Singer动态代理的对象
            类加载器
            源代码 .java --->编译-->.class --类加载器-> jvm
         */
        final Singer singer = new Singer("郭靖", 22);
        Singers proxySinger = (Singers) Proxy.newProxyInstance(
                singer.getClass().getClassLoader(),
                singer.getClass().getInterfaces(),
                new InvocationHandler() {
                    //代理的方式

                    /**
                     *
                     * @param proxy 这个就是代理之后的对象
                     * @param method    要代理的对象上面的方法
                     * @param args      当前method对应的参数
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("出任丐帮帮主");//前置通知
                        Object result = method.invoke(singer, args);
                        System.out.println("打狗棒法传授一下");//后置通知
                        return result;
                    }
                }
        );
        proxySinger.sing();

    }
}
interface Singers {
    void sing();
}
class Singer implements Singers {
    private String name;
    private int age;

    public Singer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void sing() {
        System.out.println(name + " 正在练武");
    }
}

scala中使用匿名类

object _05ExtendsOps {
    def main(args: Array[String]): Unit = {
        val worker = new Worker("郭靖", 22)
        val 警察叔叔 = new Worker("阿 sir.", 28){//匿名子类
            override def work(): Unit = {
                super.work()
                println("为莘莘学子保驾护航~~~")
                println("阿 sir。辛苦了")
            }
            def fetchThief(): Unit = {
                println("阿 sir专抓偷心贼")
            }
        }
        警察叔叔.fetchThief()
        worker.makeFriend(警察叔叔)
    }
}
class Worker {
    var name:String = _
    var age:Int = _

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

    def makeFriend(worker: Worker): Unit = {
        println(this.name + "和 " + worker.name + "成为了工友")
        worker.work()
    }

    def work(): Unit = {
        println("劳心者治人,劳力者治于人")
    }
}

2.2.7. 抽象类和抽象字段

​ what is abstract?

​ 编程语言中的抽象,就是只给出了定义,并没有给出实现。只能交由具体的实现者来完成,为了表示抽象和非抽象(具像,具体),在java或者scala中使用关键字abstract来进行标识。

> 需要指出的是,在java中只有抽象方法,没有抽象字段,但是在scala既有抽象方法,又有抽象字段。

特点

  1. scala既有抽象方法,又有抽象字段
  2. 抽象方法和抽象字段,不用abstract进行修饰,只有类采用abstract进行修饰
  3. 所谓抽象字段,只有在抽象类中才可以存在,也就是没有进行初始化的值
  4. 子类去覆盖父类的抽象的时候,不想非抽象类,可以省略掉override关键字
object _06AbstractOps {
    def main(args: Array[String]): Unit = {
        val p = new Chinese
        p.eat()
    }
}

abstract class Human {
    var name:String
    var birthday:String = _
    def eat()
    def sleep(): Unit = {
        println("中午不睡,下午崩溃~")
    }
}

class Chinese extends Human {
    def eat(): Unit = {
        println(name + "使用筷子吃饭")
    }

    override  var name = "炎黄子孙"
}

有一个疑问?

​ 大伙在java学习过,只能单继承,无法多继承,原因是什么?

​ 如果一个类,继承两个类,而这两个类都有相同的方法,子类在使用过程中无法辨别该方法到底来源于哪一个父类。

​ java中为了解决这个只能单继承的问题,提出了两个方案,第一个就是多层继承,这是从纵向解决;第二个就是多实现。

​ scala也只能进行单继承和多层继承,无法多继承。

2.2.8. trait特质

​ trait特质是一个和class、object同一level的结构。

​ trait和java中的接口有点像,但不完全一样,java中的interface中的所有的方法都是抽象的,而trait既可以拥有抽象方法,也可以拥有非抽象方法,所以其实可以理解为抽象类。

​ 已经有了一个抽象类,那干嘛有搞出一个trait特质呢?

​ 那是因为,如果只有抽象类的话,在继承体系中只能单继承和多层继承,无法多extends,所以需要一个新的结构来弥补,这个结构就是trait特质。

​ 需要大家注意的是,java中实现一个interface使用关键字implement,实现多个interface时候,interface之间使用“,”分割,scala中扩展trait的时候,同样也是用extends关键字,扩展多个trait的时候,trait之间使用with关键字连接。

​ 如果trait中的所有的方法都是抽象的,那就是java中的interface。

示例:

object _07TraitOps {
    def main(args: Array[String]): Unit = {
        val mp = new MainOperation
        mp.debug("取法乎上仅得乎中,取法乎中仅得其下,取法乎下仅得其下下")
    }
}

//特质
trait Logger {
    def log(msg:String)
}

trait ConsoleLogger extends Logger {
    override def log(msg: String): Unit = {
        println("ConsoleLogger> " + msg)
    }
}

class FileLogger extends Logger {
    override def log(msg: String): Unit = {
        println("FileLogger> " + msg)
    }
}

class DailyFileLogger extends Logger {
    override def log(msg: String): Unit = {
        println("DailyFileLogger> " + msg)
    }
}


class MainOperation extends ConsoleLogger with Logger{
    def debug(msg:String): Unit = {
        print("debug: ")
        super.log(msg)
    }
}

混入:

​ 把上述的MainOperation修改一下:

class MainOperation extends ConsoleLogger {
    def debug(msg:String): Unit = {
        print("debug: ")
        super.log(msg)
    }
}

​ 如果这是张三开发的代码,李四在用的时候,觉得之extends ConsoleLogger不完善,还需要扩展其他的trait,但是联合开发的话,一般都不要随便修改别人的代码。

​ 但是还是想要增加新的功能,怎么做呢,一种方式新写一个类集成张三写的类的同时又扩展新的的功能,可以,但是略微麻烦一点,所以这里在scala中使用混入这样一个概念去处理。

​ 在运行时去扩展一个特质,如下,便让李四写的对象在原有的基础之上增加新的功能,对原先的类没有侵入。

val mmp = new MainOperation with MyTrait //scala特质的混入 mix in
mmp.debug("dafaff")
mmp.show()

trait MyTrait {
    def show(): Unit = {
        println("江上如此多娇~")
    }
}

你可能感兴趣的:(学习总结)