代码之家  ›  专栏  ›  技术社区  ›  Brandon

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

  •  1
  • Brandon  · 技术社区  · 12 年前

    如果有一个现有的Java类pkg测试,

    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 回复  |  直到 12 年前
        1
  •  4
  •   Brian Hsu    12 年前

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

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

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

    我们如何调用 printHello 在斯卡拉?看起来如下所示:

    import pkg.Test
    Test.printHello()
    

    正如您所看到的,语法与中的invoke方法完全相同 object Test 从这个角度来看, Test singleton已经定义,您不能定义 object 使用相同的FQN两次。

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

    Scala的编译器足够聪明,能够解决这个问题 pkg.匹配器测试 是一个无法使用构造它的singleton new 关键字,所以当你写作时 new Test(1) ,是的 pkg测试 而不是 pkg.匹配器测试 。这就是为什么您可以在代码示例中同时使用它们。

    事实上,你不需要 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)
      }
    }
    

    使现代化

    提取器不需要是伴随对象,这意味着您不需要相应的类。任何 对象 具有 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)
        }
      }
    }
    

    因此,如果主方法不在 pkg ,您可以进行以下选择:

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

    2. 使用FQN,使编译器知道您正在访问 pkg.匹配器测试 而不是 pkg测试 .

      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测试 换成另一个名字 import 语法,所以它不会与 pkg.匹配器测试 .

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