zoukankan      html  css  js  c++  java
  • Beginning Scala study note(7) Trait

      A trait provides code reusability in Scala by encapsulating method and state and then offing possibility of mixing them into classes thus allowing code reuse.

    #define Trait Gliding
    scala> trait Gliding{
    | def gliding(){ println("gliding")}
    | }
    defined trait Gliding

       Gliding does not declare a superclass, so like a class, it has the default superclass of AnyRef. It defines one concrete method.

      1. Using Traits as Mixins
      You can create a trait that inherits from a Class, as well as a Class that extends a trait. Once a trait is defined, it can be mixed in to a class using either the extends or with keywords.

    # Mixin by extending the trait 
    scala> class Glider extends Gliding{
    | override def toString = "glider"
    | }
    defined class Glider
    scala> val glider = new Glider
    glider: Glider = glider
    scala> glider.gliding
    gliding

       A trait also defines a type.

    scala> val g: Gliding = glider
    g: Gliding = glider
    scala> g.gliding
    gliding
    
    # override the trait's method
    scala> class Glider extends Gliding{
    | override def toString = "glider"
    | override def gliding(){
    | println("race for now "+ toString)
    | }
    | }
    defined class Glider
    scala> val glider = new Glider
    glider: Glider = glider
    scala> glider.gliding
    race for now glider
    
    # declaring methods in a trait
    scala> trait TraitA{
    | def methodA
    | def methodAWithParam(param: String)
    | def methodWithReturnType: String
    | }
    defined trait TraitA
    
    # trait extending another trait
    scala> trait TraitB extends TraitA{
    | def methodB
    | }
    defined trait TraitB

       When a class extends a trait, it use the extends and with keywords based on whether the class extends one trait or several traits.

    # extending one trait
    scala> class ClassA extends TraitA{}
    # extending multiple traits
    class ClassA extends TraitA with TraitB{}
    # extending class and traits
    class ClassA extends ClassB with TraitA with TraitB{}

       A class extending the trait must implement all the abstract methods of trait, unless the class extending a trait is itself abstract.

    # concrete class must implement all abstract methods of trait
    scala> class ClassA extends TraitA{
    | def methodA {}
    | def methodWithParam(param: String){}
    | def methodWithReturnType: String{}
    | }

       Note that a trait can be comprised of both abstract and concrete methods. If a class extends a trait but does not implement the abstract methods defined in the trait, the class extending the trait must be declared abstract.

    # extending class not implementing abstract methods of trait must be abstract
    scala> abstract class ClassA extends TraitA{
    | def methodA {}
    | def methodWithParam(param: String){}
    | }
    defined class ClassA
    
    # trait with implementation
    scala> trait Vehicle{
    | def drive{println("Driving")}
    | def race
    | }
    defined trait Vehicle
    # subclass does not override the trait's drive method
    scala> class car extends Vehicle{
    | def race {("Racing the car")}
    | }
    defined class car
    # subclass overrides the trait drive method
    scala> class boat extends Vehicle{
    | override def drive{("float")}
    | def race{("Racing boat")}
    | }
    defined class boat

       Although Scala has abstract classes, it's recommended to use traits instead of abstract classes to implement base behavior because a class can extend only one abstract class, but it can implement multiple traits. If you want the base behavior to be inherited in Java code, use an abstract class.

    # trait with abstract and concrete fields
    scala> trait CarTrait{
    | var door: Int
    | var seat = 4
    | }
    defined trait CarTrait
    # override keyword not necessary for var field
    scala> class Car extends CarTrait{
    | var door = 4
    | seat = 5
    | }
    defined class Car

       You need to use the override keyword in a subclass of a trait to override a val field.

    # override keyword necessary for val field
    scala> trait CarTrait{
    | val door: Int
    | }
    defined trait CarTrait
    scala> class Car extends CarTrait{
    | override val door = 5
    | }
    defined class Car

       A class definition can have the parameters passed to the primary constructor of a class but a trait definition cannot have such parameters.

      2. Trait and Class Hierarchies
    Traits also can have rules about what kind of classes and other traits they can be mixed into. Further, you can declare method parameters that are a consolidation of types.

    # only instances of classes that extends Baz, Blarg, and FruitBat may be passed into this method
    def foo(bar: Baz with Blarg with FruitBat)

       事例:

    # modelling living things
    abstract class LivingThing
    abstract class Plant extends LivingThing
    abstract class Animal extends LivingThing
    
    # trait HasLegs
    trait HasLegs extends Animal{
    def walk(){println("Walking")}
    }

       A trait extending a class means that the compiler will only let you mix HasLegs into something that subclasses from Animals.

    # trait HasWings
    trait HasWings extends Animal{
    def flap(){println("Flap Flap")}
    }

       We define the rules of the self type with "this: HasWings =>". The compiler flags an error if this trait is not mixed into a class that also extends HasWings.

    trait Flies{
    this: HasWings =>
    def fly(){println("I'm flying")}
    }
    
    abstract class Bird extends Animal with HasWings with HasLegs
    
    # concrete Birds
    class Robin extends Bird with Flies
    class Ostrich extends Bird
    
    # mammal behavior
    abstract class Mammal extends Animal{
    def bodyTemperature: Double
    }
    
    # KnowsName Trait
    trait KnowsName extends Animal{
    def name: String
    }
    
    # Dog has legs knows its name
    class Dog(val name: String) extends Mammal with HasLegs with KnowsName{
    def bodyTemperature: Double = 99.3
    }
    
    # Ignores Names Trait
    trait IgnoresName{
    this: KnowsName =>
    def ignoreName(when: String): Boolean
    
    def currentName(when: String): Option[String] = 
    if (ignoreName(when)) None else Some(name)
    }
    
    # Cat Ignores Name except at dinner time
    class Cat(val name: String) extends Mammal with HasLegs with KnowsName with IgnoresName{
    def ignoreName(when: String) = when match{
    case "Dinner" => false
    case _ => true
    }
    def bodyTemperature: Double = 99.5
    }
    
    trait Athlete extends Animal
    
    # Runner Trait
    trait Runner{
    this: Athlete with HasLegs =>
    def run(){println("I'm running")}
    }
    
    # Person is mammal with legs and knows its name
    class Person(val name: String) extends Mammal with HasLegs with KnowsName{
    def bodyTemperature: Double = 98.6
    }
    
    # Biker Trait
    trait Biker extends Person{
    this: Athlete =>
    def ride(){println("I'm riding my bike")}
    }
    
    # define Gender
    trait Gender
    trait Male extends Gender
    trait Female extends Gender
    
    # the compiler enforced our rule about Bikers needing to be Persons.
    scala> val bikerDog = new Dog("biker") with Athlete wit Biker
    <console>:14: error: value wit is not a member of Dog with Athlete
    val bikerDog = new Dog("biker") with Athlete wit Biker
    ^

       Please note that we can combine different traits as part of the object creation.

    scala> val archer = new Dog("archer") with Athlete with Runner with Male
    archer: Dog with Athlete with Runner with Male = $anon$1@6106258e
    
    scala> val dpp = new Person("David") with Athlete with Biker with Male
    dpp: Person with Athlete with Biker with Male = $anon$1@6a0147e1
    
    scala> val john = new Person("John") with Athlete with Runner with Male
    john: Person with Athlete with Runner with Male = $anon$1@5c97a513
    
    scala> val annette = new Person("Annette") with Athlete with Runner with Female
    annette: Person with Athlete with Runner with Female = $anon$1@3c42eee3
    
    scala> def goBiking(b: Biker) = println(b.name + " is biking")
    goBiking: (b: Biker)Unit
    
    scala> goBiking(dpp)
    David is biking
    
    # The method requires a Biker, and Annette is not a Biker.
    scala> goBiking(annette)
    <console>:21: error: type mismatch;
    found : Person with Athlete with Runner with Female
    required: Biker
    goBiking(annette)
    ^

       However, just as we can compose a class out of traits, we can require that a class implement more than one trait in order to be the parameter to a method:

    scala> def charityRun(r: Person with Runner) = r.run()
    charityRun: (r: Person with Runner)Unit
    
    scala> charityRun(john)
    I'm running
    scala> charityRun(archer)
    <console>:21: error: type mismatch;
    found : archer.type (with underlying type Dog with Athlete with Runner with Male)
    required: Person with Runner
    charityRun(archer)
    ^
    # only be called with a parameter that's both a Runner and a Female:
    scala> def womensRun(r: Runner with Female) = r.run()
    womensRun: (r: Runner with Female)Unit
    scala> womensRun(annette)
    I'm running
    scala> val madeline = new Cat("Madeline") with Athlete with Runner with Female
    madeline: Cat with Athlete with Runner with Female = $anon$1@38d5857c
    scala> womensRun(madeline)
    I'm running

       Scala's compositional rules are very powerful tools for defining complex class hierarchies and for specifying the rules for composing classes as well as the rules for passing parameters into methods.

  • 相关阅读:
    NOIp2018集训test-9-23
    NOIp2018集训test-9-22(am/pm) (联考三day1/day2)
    NOIp2018集训test-9-21(am/pm)
    NOIp2018集训test-9-19(am&pm)
    day41.txt
    day40表关系
    day39
    day38数据库
    day37
    day36
  • 原文地址:https://www.cnblogs.com/mengrennwpu/p/6124232.html
Copyright © 2011-2022 走看看