1

如果存在现有的 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)
  }
}

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

4

1 回答 1

4

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

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

package pkg;

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

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

import pkg.Test
Test.printHello()

如您所见,语法与调用 中的方法完全相同object Test。从这个角度来看,Test单例已经定义好了,你不能object用相同的 FQN 定义两次。

这就是为什么你需要定义pkg.matchers.Test,这样它就不会与pkg.Test.

并且 Scala 的编译器足够聪明,可以判断出这pkg.matchers.Test是一个无法使用new关键字构造它的单例,所以当你编写时new Test(1),它是pkg.Test而不是pkg.matchers.Test. 这就是为什么您可以在代码示例中同时使用它们的原因。

实际上,您根本不需要type Test = pkg.Test,以下工作正常:

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)
  }
}

更新

Extractor 不需要是伴生对象,这意味着您不需要相应的类。任何objectwithunapply方法都可以用作提取器。

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)
    }
  }
}

因此,如果 main 方法不在 中pkg,您有以下选择:

  1. 将您的提取器重命名为另一个名称,以便编译器知道您正在使用提取器。

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

    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)
    
      }
    
    }
    
  3. 使用语法重命名pkg.Test为另一个名称import,因此它不会与pkg.matchers.Test.

    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)
    
      }
    }
    
  4. 只需从 导入所有内容pkg,特别是导入pkg.matcher.Test

      def main(args: Array[String]) {
    
        import pkg._
        import pkg.matchers.Test
    
        val t = new Test(1)
        t match {case Test(v) => println(v)}
        println(t.v)
    
      }
    
于 2013-03-21T01:57:47.463 回答