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 width: 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

 


















posted @   mengrennwpu  阅读(252)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示