如何"应用"在Scala中的伴随对象(使用Trait)中工作?

Som*_*kar 2 scala traits

来源:Scala MEAP v10中的功能编程

在下面粘贴的代码中

sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[+A](head: A, tail: List[A]) extends List[A]

object List {
  def sum(ints: List[Int]): Int = ints match {
    case Nil => 0
    case Cons(x,xs) => x + sum(xs)
 }
  def product(ds: List[Double]): Double = ds match {
    case Nil => 1.0
    case Cons(0.0, _) => 0.0
    case Cons(x,xs) => x * product(xs)
  }
  def apply[A](as: A*): List[A] = {

    if (as.isEmpty) Nil
    else Cons(as.head, apply(as.tail: _*))
  }
  val example = Cons(1, Cons(2, Cons(3, Nil)))
}
Run Code Online (Sandbox Code Playgroud)

Cons对象可能由apply()构造,但类型签名不同,scala如何最终组装Cons实例.

虽然下面的代码工作绝对没问题,但是没有不适用,将List拆成Cons(head,tail)

object a{
  val x = List(1,2,3,4,5) match {
    case Cons(x, Cons(2, Cons(4, _))) => x
    case Nil => 42
    case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y
    case Cons(h, t) => h + List.sum(t)
    case _ => 101
  }
}
Run Code Online (Sandbox Code Playgroud)

Cyä*_*gha 5

一个Cons实例总是被构造Cons的构造函数.你可以直接打电话:

val myList = new Cons(1, new Cons(2, Nil)) // list containing the elements 1 and 2
Run Code Online (Sandbox Code Playgroud)

apply同伴对象的方法是一个工厂方法,让你构建一个更好的语法列表:

val myList = List(1, 2) // expanded to List.apply(1, 2) by the compiler ; same result as above
Run Code Online (Sandbox Code Playgroud)

没有理由他们需要具有相同类型的签名,因为它们不需要以相同的方式调用.

至于为什么你可以在不定义unapply方法的情况下使用模式匹配:那是因为你定义了NilCons作为案例类/对象.的情况类获取一个免费的多个功能(由编译器产生的),包括unapply方法(而且equals,hashcodetoString例如).


编辑:基于评论的一些精确度:

Scala中的构造函数: 在Scala中,每个类都有一个默认构造函数,其参数可自动作为类中的字段使用 - 因此默认构造函数不需要拥有自己的方法体.例如,以下Scala类:

class Foo1(bar: Int)
class Foo2(val bar: Int)
Run Code Online (Sandbox Code Playgroud)

大致相当于以下Java类:

public class Foo1 {
    public Foo1(int bar) {
        this.bar = bar;
    }
    private int bar;
}

public class Foo2 {
    public Foo2(int bar) {
        this.bar = bar;
    }
    private int bar;
    public int getBar() {
        return bar;
    }
}
Run Code Online (Sandbox Code Playgroud)

多态性: Nil并且Cons都延伸List.这意味着a Cons是一种List.因此,如果你创建一个Conswith 的实例val myList = new Cons(1, Nil),myList是一个类型的对象Cons......但它也是:

  • 类型 List
  • 类型AnyRef,因为它是所有引用类型的根类.Scala中的所有类都AnyRef默认扩展,因此List扩展AnyRef(因为它没有显式extends子句).
  • 类型Any,这是所有Scala类型的根.

以下代码使用您的List/ Cons/ Nilimplementation:

val myList = List(1, 2, 3)
  // Cons(1,Cons(2,Cons(3,Nil))) => result of the toString method generated by the compiler because it's a case class
myList.getClass.getName
  // Cons   => this is the concrete type of myList
myList.isInstanceOf[Cons[_]]
  // true   => myList is a Cons
myList.isInstanceOf[Nil.type]
  // false
myList.isInstanceOf[List[_]]
  // true   => but it's also a kind of List
val foo: List[Int] = myList
  //        => this is allowed
myList.isInstanceOf[AnyRef]
  // true
myList.isInstanceOf[Any]
  // true

val anotherList = List()
  // Nil
anotherList.getClass.getName
  // Nil$   => the name is mangled with a $ sign to differentiate the 'object Nil' in case you also declare a 'class Nil'
anotherList.isInstanceOf[List[_]]
  // true
Run Code Online (Sandbox Code Playgroud)