zoukankan      html  css  js  c++  java
  • Beginning Scala study note(3) Object Orientation in Scala

    1. The three principles of OOP are encapsulation(封装性), inheritance(继承性) and polymorphism(多态性).
    example:

    class Shape{ def area: Double = 0.0 } # supertype
    # subtypes
    class Rectangle(val  Double, val height: Double) extends Shape{
    override def area: Double = width*height
    }
    class Circle(val radius: Double){
    override def area: Double = math.Pi*radius*radius
    }

     A subtype is guaranteed to have all the members of the supertype. Changing the implementation of a method of the supertype is called overriding. In encapsulation, the fields of an object are accessible only through its methods

    example:

    def draw(s: Shape)
    val circle = draw(new Circle(3))
    val rectangle = draw(new Rectangle(2,3))

     Inheritance guarantees that any method we could call on an instance of Shape will be defined in the subtypes. This is the heart of polymorphism.

    2. Classes and Objects
    (1) example:

    scala> class Book # defined Book class, corresponding to java declaration: public class Book{ }
    defined class Book
    scala> :javap -c Book # decompiling the Book class
    Compiled from "Book.scala"
    public class Book implements scala.ScalaObject {
    public Book();
    Code:
    0: aload_0 
    1: invokespecial #10; // Method java/lang/Object."<init>":()V
    4: return 
    }
    scala> new Book # create an object or an instance of Book
    res1: Book = Book@9206d91
    scala> new Book()
    res2: Book = Book@3d189f33

     (2) Constructors

      1) Constructors with Parameters
        a. Parameter Declared as a val
        In this case, Scala generates only a getter method for it, a setter method is not generated.

    scala> class Book(val title: String)
    scala> :javap -c Book
    Compiled from "Book.scala"
    public class Book implements scala.ScalaObject {
    public java.lang.String title();
    //...... 
    public Book(java.lang.String);
    //......
    }

         Scala generates a title method, which you can use to access the field. The value of the field is immutable by definition, because of val type.

    scala> val book = new Book("Beginning Scala")
    book: Book = Book@39a02a5d
    scala> book.title
    res2: String = Beginning Scala
    scala> book.title = "new title"
    <console>:9: error: reassignment to val
    book.title = "new title"
    ^

         In Scala, if the constructor or method takes zero parameters, you can omit the parameter list.

        b. Parameter Declared as a var
        In this case, Scala generates both setter and getter method.

    scala> class Book(var title: String)
    scala> :javap -c Book
    Compiled from "Book.scala"
    public class Book implements scala.ScalaObject {
    public java.lang.String title();
    // ...... 
    public void title_$eq(java.lang.String);
    // ...... 
    public Book(java.lang.String);
    // ...... 
    }

         If you decompile this code, you can see the generated mutator method with an unusual name, title_$eq

    scala> val book = new Book("Beginning Scala")
    book: Book = Book@5b375aff
    scala> book.title
    res0: String = Beginning Scala
    scala> book.title = "new title"
    book.title: String = new title

         c. Parameter Declared as a private val or var

        In this case, it can prevent getter and setter methods from being generated. The fields could only be accessed from within members of the class.

    scala> class Book(private var title: String)
    scala> :javap -c Book
    Compiled from "Book.scala"
    public class Book implements scala.ScalaObject {
    public Book(java.lang.String);
    // ...... 
    }
    scala> class Book(private var title: String){
    | def printTitle {println(title)}
    | }
    defined class Book
    scala> val book = new Book("Beginning Scala")
    book: Book = Book@70f19734
    scala> book.title
    <console>:10: error: variable title in class Book cannot be accessed in Book
    book.title
    ^
    scala> book.printTitle
    Beginning Scala

         d. Parameter Declared without val or var

        Scala does not generate getter or setter when neither val nor var are specified on constructor parameters.

    scala> class Book(title: String)
    defined class Book
    scala> val book = new Book("Beginning Scala")
    book: Book = Book@1222007
    scala> book.title
    <console>:10: error: value title is not a member of Book
    book.title
    ^

         The different Parameter with val and Parameter without val/var:

    scala> class Book(private val title: String){
    | def printTitle(b: Book){ 
    | println(b.title)
    | }
    | }
    defined class Book
    scala> val book = new Book("Beginning Scala")
    book: Book = Book@659a1780
    scala> book.printTitle(book)
    Beginning Scala
    scala> book.printTitle(new Book("Beginning Erlang"))
    Beginning Erlang

         You can change the title of the Book because the title is a private field that is accessible to "this " object and the other objects of Book.

    class Book(title: String){
      def printTitle(b: Book){
        println(b.title)
      }
    }
    scala> scala Book.scala
    Book.scala:3: error: value title is not a member of this.Book
    println(b.title)
    ^
    one error found

         You can provide a default value for a constructor parameter that gives other classes the option of specifying that parameter when calling the constructor.

    scala> class Book(val title: String = "Beginning Scala")
    defined class Book
    scala> val book = new Book # you can call the constructor without specifying a title
    book: Book = Book@7a0ad359
    scala> book.title
    res0: String = Beginning Scala
    scala> val book = new Book("new title") # you can specify the title value
    book: Book = Book@23a79657
    scala> book.title
    res2: String = new title
    scala> val book = new Book(title="Beginning Scala") # you can also choose to provide named parameters
    book: Book = Book@5f6af31
    scala> book.title
    res3: String = Beginning Scala

       2) Auxiliary Constructor(辅助构造函数)

      Auxiliary constructors are defined by creating methods named "this". In this way, you can define multiple auxiliary constructors, but they must have different signatures. Each auxiliary must begin with a call to a previously defined constructor.

    scala> class Book(var title: String, var ISBN: Int){
    | def this(title: String){
    | this(title,222)
    | }
    | def this(){
    | this("Beginning Scala")
    | this.ISBN=111
    | }
    | override def toString = s"$title ISBN- $ISBN"
    | }
    defined class Book
    scala> val book1 = new Book
    book1: Book = Beginning Scala ISBN- 111
    scala> val book2 = new Book("Beginning Erlang")
    book2: Book = Beginning Erlang ISBN- 222
    scala> val book3 = new Book("Beginning Clojure",2222)
    book3: Book = Beginning Clojure ISBN- 2222

       (3) Method Declaration

        1) It is recommended to use type inferencing judiciously(明智而谨慎地); if it's not immediately obvious what the return type is, declare it explicitly.

    def myMethod(): String = "Moof"
    def myOtherMethod() = "Moof"
    def foo(a: Int):String = a.toString
    def f2(a: Int, b: Boolean):String = if(b) a.toString else "false"

         2) You can pass the type of a parameter or the return type as a parameter. The following code takes a parameter p and a type parameter T and returns a List of T.

    scala> def list[T](p:T):List[T]=p::Nil
    list: [T](p: T)List[T]
    scala> list(10)
    res9: List[Int] = List(10)
    scala> list("Hello")
    res10: List[String] = List(Hello)
    scala> list(List("1"))
    res11: List[List[String]] = List(List(1))

         3) If the last parameter is a variable-length argument(可变长度参数), it is a Seq of the type of the variable-length argument, so in this case the as parameter is a Seq[Int]:

    scala> def largest(as: Int*): Int = as.reduceLeft((a,b)=> a max b)
    largest: (as: Int*)Int
    scala> largest(1)
    res13: Int = 1
    scala> largest(2,3,99)
    res14: Int = 99
    scala> largest(33,22,33,22)
    res15: Int = 33

         You can mix type parameters with variable-length arguments:

    scala> def mkString[T](as: T*):String = as.foldLeft("")(_ + _.toString)
    mkString: [T](as: T*)String
    scala> mkString(2)
    res31: String = 2
    scala> mkString(1,2,3)
    res32: String = 123
    scala> mkString(1,2,3.0)
    res33: String = 1.02.03.0

         And you can put bounds on the type parameters. In this case, the types that are passed in must be Number or a subclass of Number:

    scala> def sum[T <: Number](as: T*): Double = as.foldLeft(0d)(_ + _.doubleValue)
    sum: [T <: Number](as: T*)Double
    scala> sum()
    res50: Double = 0.0

         4) Methods can be declared within any code scope, except at the top level, where classes, traits, and objects are declared. Methods can reference any variables in their scope.

    import java.io.BufferedReader
    def readLines(br: BufferedReader) = {
      var ret: List[String] = Nil
      def readAll(): Unit = br.readLine match {
        case null =>
        case s => ret ::= s ; readAll()
      }
      readAll()
      ret.reverse
    }

         The readAll method is defined inside the scope of the readLines method, Thus readAll method has access to the variables br and ret. The readAll method calls a method on br, and it updates ret.

        5) Method that override declared methods must include the override modifier, except overriding abstract methods.

    scala> abstract class Base{
    | def thing: String
    | }
    defined class Base
    scala> class One extends Base{
    | def thing = "Moof"
    | }
    defined class One
    scala> val one = new One
    one: One = One@1bb62bd
    scala> one.thing
    res1: String = Moof
    scala> class Base{
    | def thing: String = ""
    | }
    defined class Base
    scala> class One extends Base{
    | def thing: String = "Moof"
    | }
    <console>:9: error: overriding method thing in class Base of type => String;
    method thing needs `override' modifier
    def thing: String = "Moof"
    ^

         Methods that take no parameters and variables can be accessed the same way, and a val can override a def in a superclass.

    scala> class Two extends One{
    | override val thing = (new java.util.Date).toString
    | }
    defined class Two
    scala> val two = new Two
    two: Two = Two@38ec38ad
    scala> two.thing
    res3: String = Tue Jun 28 16:24:51 CST 2016
    scala> class Three extends One{
    | override lazy val thing = super.thing + (new java.util.Date).toString
    | }
    defined class Three
    scala> val three = new Three
    three: Three = Three@779122a0
    scala> three.thing
    res4: String = MoofTue Jun 28 16:26:14 CST 2016

       (4) Code Blocks

      1) Methods and variables also can be defined in code blocks that are denoted by curly braces:{ }. Code blocks may be nested. The result of a code block is the last line evaluated in the code block.

    scala> def meth3(): String = {"Moof"}
    meth3: ()String
    scala> meth3
    res5: String = Moof
    scala> def meth4(): String = {
    | val d = new java.util.Date()
    | d.toString
    | }
    meth4: ()String
    scala> meth4
    res6: String = Tue Jun 28 16:42:29 CST 2016

       Variable definitions can be code blocks as well.

    scala> val x3:String = {
    | val d = new java.util.Date
    | d.toString
    | }
    x3: String = Tue Jun 28 16:45:28 CST 2016

       (5) Call-by-Name

      1) Scala gives an additional mechanism for passing parameters to methods( and functions): call-by-name, which passes a code block to the callee.

    scala> def nano() = {
    | println("Getting nano")
    | System.nanoTime
    | }
    nano: ()Long
    scala> nano
    Getting nano
    res7: Long = 9437790603685212
    scala> def delayed(t: => Long) = { # call by name parameter(按名调用参数)
    | println("In delayed method")
    | println("Param:"+t)
    | t
    q | }
    delayed: (t: => Long)Long
    scala> delayed(nano)
    In delayed method
    Getting nano
    Param:9438187859989956
    Getting nano
    res10: Long = 9438187860305135

       This indicates that delayed is entered before the call to nano and that nano is called twice.

    scala> def notDelayed(t: Long) = {
    | println("In notDelayed method")
    | println("Param: "+t)
    | t
    | }
    notDelayed: (t: Long)Long
    scala> notDelayed(nano)
    Getting nano
    In notDelayed method
    Param: 9438465403584919
    res11: Long = 9438465403584919

       nano is called before notDelayed is called because the parameter nano is calculated before notDelayed is called. This is the way Java programmers expect code to work.

      (6) Method Invocation(方法调用)

    instance.method() # java dot notation
    instance.method # if a method does not take any parameters
    instance.method(param) # a single parameter in java
    instance.method param # methods that take a single parameter can be invoked without dots or parentheses
    instance.method(p1,p2) # invoke multiparameter methods
    instance.method[Type Param](p1,p2) # the type param will be inferred by the compiler, also you can explicitly pass

       Because Scala allows method names to contain symbols such as +,-,*,?, Scala's dotless method notation creates a syntactically neutral way of invoking methods that are hard-coded operators in Java.

    scala> 2.1.*(4.3)
    res10: Double = 9.03
    scala> 2.1*4.3
    res11: Double = 9.03

       (7) Objects

      In Scala, you can use object to refer to an instance of a class or a keyword.
      1) Singleton Objects
      Scala does not have static members. Instead, Scala has singleton objects. A singleton is a class that can have only one instance.

    scala> object Car{ # define car object
    | def drive {println("drive car")}
    | }
    defined module Car
    scala> Car.drive
    drive car
    scala> object Main extends App{ # Call car methods
    | Car.drive
    | }
    defined module Main

       Singleton objects cannot take parameter. There are two ways to create a launching point for your application: define an object with a properly defined main method or define an object that extends the App trait.

    object Book extends App{
        println("Hello World")    
    }

       Note that in both approaches, Scala applications are launched from an object, not a class.

      2) Companion Objects(伴生对象)
      When an object shares a name with a class, it's called a companion object, and the class is called a companion class. A companion object is an object that shares the same name and source file with another class or trait. Using this approach lets you create static members on a class. The companion object is useful for implementing helper methods and factory.
      We use a companion class Shape and a companion object Shape, which acts as a factory.

    trait Shape {
         def area: Double
    }
    object Shape{ 
        private class Circle (radius: Double) extends Shape{ 
            override val area = 3.14*radius*radius 
        } 
        private class Rectangle(height: Double, length: Double) extends Shape{ 
            override val area = height * length 
        }  
        def apply(height: Double, length: Double): Shape = new Rectangle(height,length) 
        def apply(radius: Double): Shape = new Circle(radius)
    }    

       A singleton object that does not share the same name with a companion class is called a standalone object.

      (8) Packaging and Imports
      1) The following statement imports the contents of the scala.xml package:  

    import scala.xml._

       Import statements are made in the scope of prior imports. The following statement imports the scala.xml.transform._ package:

    import transform._

       You can import more than one class or object from a single package, for example:

    import sclala.collection.immutable.{TreeMap, TreeSet}

       You can import a class or object and rename it.

    import scala.util.parsing.json.{JSON=> JsonParser}

      Import can be used inside any code block, and the import will be active only in the scope of that code block.

    scala> class Frog{ 
    | import scala.xml._ 
    | def n:NodeSeq = NodeSeq.Empty 
    | }
    defined class Frog

      Scala's import statement can also import the methods of an object so that those methods can be used without explicit reference to the object that owns them.This is much like Java's static import.

    scala> object Moose{ 
    | def bark = "woof" 
    | }
    defined object Moose
    scala> import Moose._
    import Moose._
    scala> bark
    res2: String = woof

       (9) Inheritance

      1) Scala supports single inheritance.

    scala> class Vehicle(speed: Int){ 
    | val mph: Int = speed 
    | def race() = println("Racing") 
    | }
    defined class Vehicle

       The different extending class between Scala and Java are: 

      a. method overriding requires the override keyword, except trait
      b. only the primary constructor can pass parameters to the base constructor.

    scala> class Car(speed: Int) extends Vehicle(speed){ 
    | override val mph = speed
    | override def race() = println("Racing Car") 
    | }
    defined class Car
    scala> class Bike(speed: Int) extends Vehicle(speed){ 
    | override val mph: Int = speed 
    | override def race() = println("Racing Bike") 
    | }
    defined class Bike
    scala> val vehicle1 = new Car(200)
    vehicle1: Car = Car@1cfe29c
    scala> vehicle1.race
    Racing Car
    scala> vehicle1.mph
    res4: Int = 200
    scala> val vehicle2 = new Bike(20)
    vehicle2: Bike = Bike@1973547
    scala> vehicle2.mph
    res5: Int = 20
    scala> vehicle2.race
    Racing Bike

       (10) Traits

      In Scala, when a class inherits from a trait, it implements the interface of the trait, and inherits all the code contained in the trait.

    scala> trait flying{ 
    | def fly() = println("flying") 
    | }
    defined trait flying
    scala> trait gliding{ 
    | def glide() = println("gliding")
    | }
    defined trait gliding

       Now you can create the Batmobile class extends Vehicle class along with the flying and gliding traits.

    scala> class Batmobile(speed: Int) extends Vehicle(speed) with flying with gliding{ 
    | override val mph: Int = speed 
    | override def race() = println("Racing Batmobile") 
    | override def fly() = println("Flying Batmobile") 
    | override def glide() = println("Gliding Batmobile")
    | }
    defined class Batmobile

       n Scala, traits can inherit classes. The keyword extends is used: when a class inherits a trait, when the class mixes in other traits using the with keyword, when one trait is the child of another trait or class.

      Now create a list of vehicles, then you can use the maxBy method provided by Scala collections library to find the fastest vehicle in the list.

    scala> val vehicleList = List(vehicle1, vehicle2, batmobile)
    vehicleList: List[Vehicle] = List(Car@1cfe29c, Bike@1973547, Batmobile@8c2321)
    scala> val fastestVehicle = vehicleList.maxBy(_.mph)
    fastestVehicle: Vehicle = Batmobile@8c2321

       (11) Case Classes

      A case class provides the same facilities as a normal class, but the compiler generates toString, hasCode, and equals methods(which you can override). Case classes can be instantiated without the use of the new statement. By default, all the parameters in the case class's constructor become properties on the case class.

    scala> case class Stuff(name: String, age: Int) 
    defined class Stuff
    scala> val s = Stuff("David",45) # create an instance of Stuff without the keywod new(you can use new if you want)
    s: Stuff = Stuff(David,45)
    scala> s.toString # case class's toString method does the right thing
    res0: String = Stuff(David,45)
    scala> s == Stuff("David",45) # equal methods does a deep comparison
    res1: Boolean = true
    scala> s.name # the instance has properies
    res2: String = David
    scala> s.age
    res3: Int = 45
    class Stuff(val name: String, val age: Int){ 
      override def toString = "Stuff("+name+","+age+")"
      override def hashCode = name.hashCode + age
      override def equals(other: AnyRef) = other match {
        case s: Stuff => this.name == s.name && this.age == s.age
        case _ => false
      }
    }
    object Stuff {
      def apply(name String, age: Int) = new Stuff(name,age)
      def unapply(s: Stuff) = Some((s.name,s.age))
    }

       (12) Value Classes

      With value classes, Scala allows user-defined value classes that extend AnyVal. Value classes are a new mechanism in Scala to avoid allocating runtime objects. Value classes allow you to add extension methods to a type without the runtime overhead of creating instances.

    Class SomeClass(val underlying: Int) extends AnyVal

       SomeClass has a single, public val parameter that is the underlying runtime representation. The type at compile time is SomeClass, but at runtime, the representation is an Int. A value class can defines defs, but no vals, vars, or nested traits classes or objects.

    # wrap the Int parameter and encapsulates a twice method.
    scala> class SomeClass(val i: Int) extends AnyVal{
     | def twice() = i*2 
    | }
    defined class SomeClass
    scala> val v = new SomeClass(9)
    v: SomeClass = SomeClass@9
    scala> v.twiceres5: Int = 18
    scala> :javap –c SomeClass
    …...
    SomeClass$.twice$extension # at runtime, the expression
    a static object 
    …...

       One use case for value classes is to combine them with implicit classes. Using an implicit class provides a more convenient syntax for defining extension methods, while value classes remove the runtime overhead.

      (13) Scala verus Java
      The uniformly of instances in Scala means that the developer does not have to perform special tests or to have different code paths to deal with primitive types.

    scala> 1.hashCode
    res6: Int = 1 scala> 2.toString
    res7: String = 2 scala> def with42(in: Int => Int) = in(42) # define a method that takes a function that transforms an Int to an Int
    with42: (in: Int => Int)Int
    scala> with42(33+)
    res8: Int = 75


















  • 相关阅读:
    设计模式
    三——第二部分——第二篇论文 计划建设SQL Server镜像
    非正确使用浮点数据由项目产生BUG讨论的问题
    重写ViewPager实施单一交有关切换到这个问题,并没有缓存
    1159 Palindrome(最小插入回文串)
    CSDN-markdown编者LaTex数学公式
    找工作总结
    开展:随笔记录 OSGI的jar增加了一些小问题和注意事项
    js jquery版本号 金额千分之一转换功能(非规范,高效率)
    dom4j.jar有什么作用?
  • 原文地址:https://www.cnblogs.com/mengrennwpu/p/6103367.html
Copyright © 2011-2022 走看看