scala_2_伴生对象,隐式转换,case类

scala_2_伴生对象,隐式转换,case类

要点都在注释中!!!

目录

  • companion object
  • implicit convert
  • case class
  • def val
  • _ in scala 见征服scala_1
  • => in scala
  • () {} in scala
  • type,variance,covariance
  • 并发

伴生对象

//伴生对象是和class同名的一个objcet,必须定义在同一个文件里面.
class Person(name: String, age: Int) {
  private var skill: String = "no skill"
  def introduce() = println(s"my name is $name, I am $age years old")
}

// apply方法很重要,还有unapply方法.Extractors调用的就是伴生对象的unapply方法
object Person {
  def apply(name: String, age: Int): Person = {
    new Person(name, age)
  }

  //apply method override
  def apply(name: String, age: Int, skill: String): Person = {
    val p = new Person(name, age)
    p.skill = skill
    p
  }
}

val dahu = Person("dahu", 30)
dahu.introduce 
// scala集合中初始化其实是调用伴生对象的apply方法
val list = List("1", "2", "3") //object List 的apply方法

//关于抽取器和unapply方法的进一步示例:
trait User

class FreeUser(
                val name: String,
                val score: Int,
                val upgradeProbability: Double)
  extends User

class PremiumUser(
                   val name: String,
                   val score: Int)
  extends User

object FreeUser {
  def unapply(user: FreeUser): Option[(String, Int, Double)] =
    Some((user.name, user.score, user.upgradeProbability))
}

object PremiumUser {
  def unapply(user: PremiumUser): Option[(String, Int)] =
    Some((user.name, user.score))
}

val freeUsr = new FreeUser("john", 70, 0.5)
freeUsr match {
  case FreeUser(name, _, p) => if (p > 0.75) println(s"what can I do for you,$name")
  else println(s"hello,$name")
  case _ => println("who are you")
}

//bool抽取器
object premiumCandidate {
  def unapply(user: FreeUser): Boolean = user.upgradeProbability > 0.4
}

// bool抽取器的用法
freeUsr match {
  case freeUser@premiumCandidate() => println(s"恭喜成为黄金会员候选人")
  case _ => println("欢迎回来")
}
//来源: [Scala初学者指南](http://danielwestheide.com/scala/neophytes.html)

implicit

//分为隐式参数和隐式转换方法
//1.隐式参数
class Prefixer(val prefix: String)

def addPrefix(s: String)(implicit p: Prefixer) = p.prefix + s

// 需要提供一个隐式实际参数,否则报错
implicit val myImplicitPrefixer = new Prefixer("***")
addPrefix("abc") // returns "***abc"

//2.隐式转换,这里我们实现一个自己的mkStr方法,简单的给string添加一个Ops前缀再返回.
class StrOps(s:String) {
  def mkStr(): String = {
    return "Ops! " + s
  }
}

//告诉scala编译器string有mkStr这个方法:
implicit def str2StrOps(s: String) = new StrOps(s)

//现在用户可以直接认为String有mkStr方法
val s = "who changed my string"
s.mkStr() //res2: String = Ops! who changed my string

//在scala.Predef中定义了大量的隐式转换,例如RichInt,RichDouble,StringOps这些,提供了类似mkString这些方法
//太阳底下无新事,scala常用对象的灵活丰富的语法都是通过隐式转换添加的.

//集合类的转换
//scala集合和java集合的转换是scala编程最常用的,毕竟java有大量第三方库.
//scala提供了两种方法,第一种方法就是隐式转换collection.JavaConversions(scala 2.8)
//很快意识到隐式转换对于使用者的代码阅读比较复杂,在2.8.1提供了显示转换collection.JavaConverters,
//先看JavaConversions隐式转换:
object JavaConversions extends WrapAsScala with WrapAsJava
//在WrapAsJava
  implicit def mapAsJavaMap[A, B](m: Map[A, B]): ju.Map[A, B] = m match {
    case null                 => null
    case JMapWrapper(wrapped) => wrapped.asInstanceOf[ju.Map[A, B]]
    case _                    => new MapWrapper(m)
  }

//看下collection.JavaConverters._,稍微复杂一些,但是换汤不换药,底层还是隐式转换,
object JavaConverters extends DecorateAsJava with DecorateAsScala
//在DecorateAsJava中有很多隐式转换方法,这些方法将scala集合转换为AsJava对象
//(注意下面的ju,是java.util缩写,详情见[征服scala_1](https://zhuanlan.zhihu.com/p/22670426))
implicit def seqAsJavaListConverter[A](b : Seq[A]): AsJava[ju.List[A]] = new AsJava(seqAsJavaList(b))
// 而AsJava中定义了asJava方法,这样我们就可以在scala集合上面调用asJava
class AsJava[A](op: => A) {
    /** Converts a Scala collection to the corresponding Java collection */
    def asJava: A = op
}
//并且asJava方法的实现是作为构造参数传入AsJava的
//上面的seqAsJavaList就是将scala.Seq转换为ju.List的具体实现
def seqAsJavaList[A](s: Seq[A]): ju.List[A] = s match {
  case null                   => null
  case JListWrapper(wrapped)  => wrapped.asInstanceOf[ju.List[A]]
  case _                      => new SeqWrapper(s)
}

//综上,JavaConverters用的还是隐式转换,只不过增加了一个中间类AsJava/AsScala.

//隐式转换的scope
//无论是隐式参数还是隐式转换,编译器都要知道去哪里查找这些implicit参数或者方法,
//例如import collection.JavaConverters._
//由于scala import可以出现在任何地方,这为控制implicit的scope提供了灵活性
//这一块我不是完全清楚,只提供一个自己的理解
// 1.首先是当前scope的Implicits定义,例如,当前方法内,class内
// 2.显式导入 import collection.JavaConversions.asScalaIterator
// 3.通配符导入 import collection.JavaConverters._
// 4.类型的伴生对象内(这个常用)
// 5.参数类型的隐式scope (2.9.1添加):class构造参数的隐式转换搜索返回会被应用到
class A(val n: Int) {
  def +(other: A) = new A(n + other.n)
}
object A {
  implicit def fromInt(n: Int) = new A(n)
}

new A(1) + 2 // new A(1) + A.fromInt(2)
//6.类型参数的隐式转换,下面的sorted方法期望有一个Ordering[A],
//在伴生对象中提供了一个 A -> Ordering[A] ,
class A(val n: Int)
object A {
    implicit val ord = new Ordering[A] {
        def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n)
    }
}
List(new A(5), new A(2)).sorted
// 注意implicitly[Ordering[Int]] 表示在当前scope内搜索一个隐式参数值
def implicitly[T](implicit e: T): T = e

what-is-the-difference-between-javaconverters-and-javaconversions-in-scala
scala-implicit-resolution

case class

//case class类似data class,主要有以下五点特性:
// 自动生成companion object以及apply方法,unapply方法
// 自动有toString, hashCode and equals and copy methods
case class Student(name: String, marks: Int)

val s1 = Student("Rams", 550)
val s2 = s1.copy()
val s3 = s1.copy(marks = 590)
s2 == s1 //true
s3 == s1 //false

// 构造函数参数自动成为成员变量,即自动给构造参数添加val前缀
// 模式匹配的匹配,这个常用
// 默认的,case class和case object是可序列化的(实现Serializable),也即是可以网络传输的
编辑于 2019-02-25 23:06