为什么需要类型同义词来为Scala中的模式匹配Java类定义提取器?

时间:2013-03-21 01:49:49

标签: scala pattern-matching

如果存在现有的Java类pkg.Test,

package pkg;
public class Test {
  public int v;
  public Test(int v) { this.v = v; }
}

尝试通过定义scala伴随对象

来创建提取器
package pkg
object Test{
  def unapply(t : Test) : Option[Int] = Some(t.v)
}

产生错误“测试已被定义为对象测试”。 但是,如果我在新包中创建java类的同义词

,一切似乎都有效
package pkg
package object matchers {
  type Test = pkg.Test
}

并在新包中定义相应的对象

package pkg.matchers
object Test {
  def unapply(t : Test) : Option[Int] = Some(t.v)
}

现在,新包中的模式和原始类的所有成员都可用

import pkg.matchers._
object main {
  def main(args : Array[String]) {
    val t = new Test(1)
    t match {case Test(v) => println(v)}
    println(t.v)
  }
}

添加类型同义词似乎很奇怪。除了必须使用新包之外,以这种方式添加模式匹配是否有任何问题?有没有办法在原始包装中提供提取器?

1 个答案:

答案 0 :(得分:4)

这是因为在与Java库交互时,Scala需要一种方法将Java的静态方法映射到Scala的单例对象。

例如,如果您有以下Java类:

package pkg;

class Test {
  public static void printHello() {
    System.out.println("Hello");
  }
}

我们如何在Scala中调用printHello?它将如下所示:

import pkg.Test
Test.printHello()

如您所见,语法与调用object Test中的方法完全相同。从这一点来看,已经定义了Test单例,并且您无法定义具有相同FQN两次的object

这就是为什么你需要定义pkg.matchers.Test所以它不会与pkg.Test冲突。

Scala的编译器非常聪明,可以发现pkg.matchers.Test是一个无法使用new关键字构造它的单例,因此当您编写new Test(1)时,它是{{1}而不是pkg.Test。这就是您可以在代码示例中同时使用它们的原因。

事实上,您根本不需要pkg.matchers.Test,以下工作正常:

type Test = pkg.Test

更新

Extractor不需要是伴随对象,这意味着您不需要相应的类。任何package pkg package matchers { object Test { def unapply(t : Test) : Option[Int] = Some(t.v) } } object main { import pkg.matchers._ def main(args : Array[String]) { val t = new Test(1) t match {case Test(v) => println(v)} println(t.v) } } object方法都可以作为提取器使用。

unapply

因此,如果主要方法不在object StringLength { def unapply(x: String): Option[Int] = Some(x.length) } object Main { def main(args: Array[String]) { "Hello World" match { case StringLength(x) => println("length:" + x) } } } 中,您有以下选择:

  1. 将提取器重命名为其他名称,因此编译器可以知道您正在使用提取器。

  2. 使用FQN,因此编译器知道您是pkg而不是pkg.matchers.Test

    pkg.Test
  3. 使用package pkg.matchers { import pkg._ object Test { def unapply(t : Test) : Option[Int] = Some(t.v) } } object Main { def main(args: Array[String]) { import pkg.Test val t = new Test(1) t match {case pkg.matchers.Test(v) => println(v)} println(t.v) } } 语法将pkg.Test重命名为其他名称,因此不会与import冲突。

    pkg.matchers.Test
  4. 只需导入object Main { def main(args: Array[String]) { import pkg.{Test => JTest} import pkg.matchers.Test val t = new JTest(1) t match {case Test(v) => println(v)} println(t.v) } } 的所有内容,并专门导入pkg

    pkg.matcher.Test