第4章——善用对象
Scala 是一门完全面向对象的编程语言,为类的创建和对象的处理提供了简洁的语法。 Java 中能做的,在 Scala 中都可以做,Scala 还额外提供了一些更强大的特性,以帮助我们进 行面向对象编程。尽管 Scala 是一门纯面向对象的编程语言,但是它也支持一些 Java 中不是 那么纯粹的面向对象概念,如静态方法 ① 。利用伴生对象,Scala 以一种相当有趣的方式处理 了这个问题。伴生对象是伴随一个类的单例,在 Scala 中非常常见。
4.1 创建并使用类
WorkingWithObjects/Car.java
//Java example
public class Car {
private final int year;
private int miles;
public Car(int yearOfMake) { year = yearOfMake; }
public int getYear() { return year; }
public int getMiles() { return miles; }
public void drive(int distance) {
miles += Math.abs(distance);
}
}
WorkingWithObjects/UseCar.scala
class Car(val year: Int) {
private var milesDriven: Int = 0
def miles: Int = milesDriven
def drive(distance: Int): Unit = {
milesDriven += Math.abs(distance)
}
}
运行结果
Car made in year 2015
Miles driven 0
Drive for 10 miles
Miles driven 10
WorkingWithObjects/CreditCard.scala
class CreditCard(val number: Int, var creditLimit: Int)
运行结果
Compiled from "CreditCard.scala"
public class CreditCard {
private final int number;
private int creditLimit;
public int number();
public int creditLimit();
public void creditLimit_$eq(int);
public CreditCard(int, int);
}
WorkingWithObjects/Construct.scala
class Construct(param: String) {
println(s"Creating an instance of Construct with parameter $param")
}
println("Let's create an instance")
new Construct("sample")
运行结果
Let's create an instance
Creating an instance of Construct with parameter sample
WorkingWithObjects/Person.scala
class Person(val firstName: String, val lastName: String) {
var position: String = _
println(s"Creating $toString")
def this(firstName: String, lastName: String, positionHeld: String) {
this(firstName, lastName)
position = positionHeld
}
override def toString: String = {
s"$firstName $lastName holds $position position"
}
}
val john = new Person("John", "Smith", "Analyst")
println(john)
val bill = new Person("Bill", "Walker")
println(bill)
运行结果
Creating John Smith holds null position
John Smith holds Analyst position
Creating Bill Walker holds null position
Bill Walker holds null position
反编译结果
private java.lang.String position;
public java.lang.String position();
public void position_$eq(java.lang.String);
4.2 遵循 JavaBean 惯例
WorkingWithObjects/Dude.scala
import scala.beans.BeanProperty
class Dude(@BeanProperty val firstName: String, val lastName: String) {
@BeanProperty var position: String = _
}
反编译结果
Compiled from "Dude.scala"
public class Dude {
private final java.lang.String firstName;
private final java.lang.String lastName;
private java.lang.String position;
public java.lang.String firstName();
public java.lang.String lastName();
public java.lang.String position();
public void position_$eq(java.lang.String);
public void setPosition(java.lang.String);
public java.lang.String getFirstName();
public java.lang.String getPosition();
public Dude(java.lang.String, java.lang.String);
}
4.3 类型别名
WorkingWithObjects/PoliceOfficer.scala
class PoliceOfficer(val name: String)
WorkingWithObjects/CopApp.scala
object CopApp extends App {
type Cop = PoliceOfficer
val topCop = new Cop("Jack")
println(topCop.getClass)
}
运行结果
class PoliceOfficer
4.4 扩展一个类
WorkingWithObjects/Vehicle.scala
class Vehicle(val id: Int, val year: Int) {
override def toString = s"ID: $id Year: $year"
}
class Car(override val id: Int, override val year: Int, var fuelLevel: Int) extends Vehicle(id, year) {
override def toString = s"${super.toString} Fuel Level: $fuelLevel"
}
val car = new Car(1, 2015, 100)
println(car)
运行结果
ID: 1 Year: 2015 Fuel Level: 100
4.5 参数化类型
WorkingWithObjects/Parameterized.scala
def echo[T](input1: T, input2: T): Unit =
println(s"got $input1 (${input1.getClass}) $input2 (${input2.getClass})")
WorkingWithObjects/Parameterized.scala
echo("hello", "there")
echo(4, 5)
运行结果
got hello (class java.lang.String) there (class java.lang.String)
got 4 (class java.lang.Integer) 5 (class java.lang.Integer)
WorkingWithObjects/Parameterized.scala
echo("hi", 5)
运行结果
got hi (class java.lang.String) 5 (class java.lang.Integer)
WorkingWithObjects/EchoErr.scala
echo[Int]("hi", 5) //error: type mismatch
WorkingWithObjects/Parameterized.scala
def echo2[T1, T2](input1: T1, input2: T2): Unit =
println(s"received $input1 and $input2")
echo2("Hi", "5")
WorkingWithObjects/Parameterized.scala
class Message[T](val content: T) {
override def toString = s"message content is $content"
def is(value: T): Boolean = value == content
}
WorkingWithObjects/Parameterized.scala
val message1: Message[String] = new Message("howdy")
val message2 = new Message(42)
println(message1)
println(message1.is("howdy"))
println(message1.is("hi"))
println(message2.is(22))
运行结果
message content is howdy
true
false
false
WorkingWithObjects/Message.scala
message1.is(22) //error: type mismatch
WorkingWithObjects/Message.scala
message2.is('A') //No error!
4.6 单例对象和伴生对象
WorkingWithObjects/Singleton.scala
import scala.collection._
class Marker(val color: String) {
println(s"Creating ${this}")
override def toString = s"marker color $color"
}
object MarkerFactory {
private val markers =
mutable.Map("red" -> new Marker("red"), "blue" -> new Marker("blue"), "yellow" -> new Marker("yellow"))
def getMarker(color: String): Marker =
markers.getOrElseUpdate(color, new Marker(color))
}
println(MarkerFactory.getMarker("blue"))
println(MarkerFactory.getMarker("blue"))
println(MarkerFactory.getMarker("red"))
println(MarkerFactory.getMarker("red"))
println(MarkerFactory.getMarker("green"))
运行结果
Creating marker color red
Creating marker color blue
Creating marker color yellow
marker color blue
marker color blue
marker color red
marker color red
Creating marker color green
marker color green
WorkingWithObjects/Marker.scala
import scala.collection._
class Marker private (val color: String) {
println(s"Creating ${this}")
override def toString = s"marker color $color"
}
object Marker {
private val markers =
mutable.Map("red" -> new Marker("red"), "blue" -> new Marker("blue"), "yellow" -> new Marker("yellow"))
def getMarker(color: String): Marker =
markers.getOrElseUpdate(color, new Marker(color))
}
println(Marker.getMarker("blue"))
println(Marker.getMarker("blue"))
println(Marker.getMarker("red"))
println(Marker.getMarker("red"))
println(Marker.getMarker("green"))
运行结果
Creating marker color red
Creating marker color blue
Creating marker color yellow
marker color blue
marker color blue
marker color red
marker color red
Creating marker color green
marker color green
WorkingWithObjects/Static.scala
import scala.collection._
class Marker private (val color: String) {
override def toString = s"marker color $color"
}
object Marker {
private val markers =
mutable.Map("red" -> new Marker("red"), "blue" -> new Marker("blue"), "yellow" -> new Marker("yellow"))
def supportedColors: Iterable[String] = markers.keys
def apply(color: String): Marker = markers.getOrElseUpdate(color, op = new Marker(color))
}
println(s"Supported colors are : ${Marker.supportedColors}")
println(Marker("blue"))
println(Marker("red"))
运行结果
Creating marker color red
Creating marker color blue
Creating marker color yellow
marker color blue
marker color blue
marker color red
marker color red
Creating marker color green
marker color green
WorkingWithObjects/Greeter.scala
object Greeter {
def greet(): Unit = println("Ahoy, me hearties!")
}
运行结果
Compiled from "Greeter.scala"
public final class Greeter {
public static void greet();
}
4.7 创建枚举类
WorkingWithObjects/Currency.scala
package chapter4.finance1.finance.currencies
object Currency extends Enumeration {
type Currency = Value
val CNY, GBP, INR, JPY, NOK, PLN, SEK, USD = Value
}
WorkingWithObjects/finance1/finance/currencies/Money.scala
package chapter4.finance1.finance.currencies
import Currency._
class Money(val amount: Int, val currency: Currency) {
override def toString = s"$amount $currency"
}
WorkingWithObjects/UseCurrency.scala
import chapter4.finance1.finance.currencies.Currency
object UseCurrency extends App {
Currency.values.foreach { currency =>
println(currency)
}
}
运行结果
CNY
GBP
INR
JPY
NOK
PLN
SEK
USD
4.8 包对象
WorkingWithObjects/finance1/finance/currencies/Converter.scala
package chapter4.finance1.finance.currencies
import Currency._
object Converter {
def convert(money: Money, to: Currency): Money = {
//fetch current market rate... using mocked value here
val conversionRate = 2
new Money(money.amount * conversionRate, to)
}
}
WorkingWithObjects/finance1/finance/currencies/Charge.scala
package chapter4.finance1.finance.currencies
object Charge {
def chargeInUSD(money: Money): String = {
def moneyInUSD = Converter.convert(money, Currency.USD)
s"charged $$${moneyInUSD.amount}"
}
}
WorkingWithObjects/finance1/CurrencyApp.scala
import chapter4.finance1.finance.currencies._
object CurrencyApp extends App {
var moneyInGBP = new Money(10, Currency.GBP)
println(Charge.chargeInUSD(moneyInGBP))
println(Converter.convert(moneyInGBP, Currency.USD))
}
WorkingWithObjects/finance2/finance/currencies/package.scala
package chapter4.finance2.finance
package object currencies {
import Currency._
def convert(money: Money, to: Currency): Money = {
//fetch current market rate... using mocked value here
val conversionRate = 2
new Money(money.amount * conversionRate, to)
}
}
WorkingWithObjects/finance2/finance/currencies/Charge.scala
package chapter4.finance2.finance.currencies
object Charge {
def chargeInUSD(money: Money): String = {
def moneyInUSD = convert(money, Currency.USD)
s"charged $$${moneyInUSD.amount}"
}
}
WorkingWithObjects/finance2/CurrencyApp.scala
package chapter4.finance2
import chapter4.finance2.finance.currencies._
object CurrencyApp extends App {
var moneyInGBP = new Money(10, Currency.GBP)
println(Charge.chargeInUSD(moneyInGBP))
println(convert(moneyInGBP, Currency.USD))
}
运行结果
CNY
GBP
INR
JPY
NOK
PLN
SEK
USD