10

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

 3 years ago
source link: https://zhuanlan.zhihu.com/p/36507797
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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),也即是可以网络传输的

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK