如何为隐式方法实现中间类型? [英] How to implement intermediate types for implicit methods?

查看:91
本文介绍了如何为隐式方法实现中间类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我想在控件之外的现有类型A中提供方法foo.据我所知,在Scala中执行此操作的规范方法是实现从A到实现foo的某种类型的隐式转换.现在我基本上看到了两个选择.

Assume I want to offer method foo on existing type A outside of my control. As far as I know, the canonical way to do this in Scala is implementing an implicit conversion from A to some type that implements foo. Now I basically see two options.

  1. 为此目的定义一个单独的甚至是隐藏的类:

  1. Define a separate, maybe even hidden class for the purpose:

protected class Fooable(a : A) {
  def foo(...) = { ... }
}
implicit def a2fooable(a : A) = new Fooable(a)

  • 内联定义一个匿名类:

  • Define an anonymous class inline:

    implicit def a2fooable(a : A) = new { def foo(...) = { ... } }
    

  • 变体2)当然会减少样板,特别是当发生许多类型参数时.另一方面,我认为这会产生更多的开销,因为(从概念上来说)每次转换创建一个类,而不是在1中全局创建一个类.

    Variant 2) is certainly less boilerplate, especially when lots of type parameters happen. On the other hand, I think it should create more overhead since (conceptually) one class per conversion is created, as opposed to one class globally in 1).

    是否有一般准则?有没有区别,因为编译器/VM消除了2)的开销?

    Is there a general guideline? Is there no difference, because compiler/VM get rid of the overhead of 2)?

    推荐答案

    我相信1和2被编译为相同的字节码(情况2中生成的类名称除外). 如果Fooable仅存在于您能够将A隐式转换为Fooable(并且您永远不会直接创建和使用Fooable),那么我将选择选项2.

    I believe 1 and 2 get compiled to the same bytecode (except for the class name that gets generated in case 2). If Fooable exists only for you to be able to convert implicitly A to Fooable (and you're never going to directly create and use a Fooable), then I would go with option 2.

    但是,如果您控制A(意味着A不是您不能子类化的Java库类),我会考虑使用特征而不是隐式转换来向A添加行为.

    However, if you control A (meaning A is not a java library class that you can't subclass) I would consider using a trait instead of implicit conversions to add behaviour to A.

    更新: 我必须重新考虑我的答案.我会使用代码的变体1,因为变体2原来正在使用反射(在Linux上为scala 2.8.1).

    UPDATE: I have to reconsider my answer. I would use variant 1 of your code, because variant 2 turns out to be using reflection (scala 2.8.1 on Linux).

    我用相同的代码编译了这两个版本,使用jd-gui将它们反编译为Java,结果如下:

    I compiled these two versions of the same code, decompiled them to java with jd-gui and here are the results:

    具有命名类的源代码

    class NamedClass { def Foo : String = "foo" }
    
    object test {
      implicit def StrToFooable(a: String) = new NamedClass 
      def main(args: Array[String]) { println("bar".Foo) }
    }
    

    带有匿名类的源代码

    object test {
      implicit def StrToFooable(a: String) = new { def Foo : String = "foo" } 
    
      def main(args: Array[String]) { println("bar".Foo) }
    }    
    

    使用java-gui编译并反编译为Java. 命名"版本生成一个NamedClass.class,将其反编译为以下java:

    compiled and decompiled to java with java-gui. The "named" version generates a NamedClass.class that gets decompiled to this java:

    public class NamedClass
      implements ScalaObject
    {
      public String Foo()
      {
        return "foo";
      }
    }
    

    匿名者生成一个test $$ anon $ 1类,将其反编译为以下Java

    the anonymous generates a test$$anon$1 class that gets decompiled to the following java

    public final class test$$anon$1
    {
      public String Foo()
      {
        return "foo";
      }
    }
    

    几乎一样,除了匿名是最终的"(他们显然想确保您不会迷失方向去尝试对匿名类进行子类化……)

    so almost identical, except for the anonymous being "final" (they apparently want to make extra sure you won't get out of your way to try and subclass an anonymous class...)

    但是在呼叫站点,我得到的是该Java的命名"版本

    however at the call site I get this java for the "named" version

    public void main(String[] args) 
    { 
      Predef..MODULE$.println(StrToFooable("bar").Foo());
    }
    

    这是匿名的

      public void main(String[] args) { 
        Object qual1 = StrToFooable("bar"); Object exceptionResult1 = null;
        try { 
          exceptionResult1 = reflMethod$Method1(qual1.getClass()).invoke(qual1, new Object[0]); 
          Predef..MODULE$.println((String)exceptionResult1); 
          return; 
        } catch (InvocationTargetException localInvocationTargetException) { 
          throw localInvocationTargetException.getCause();
        }
      }
    

    我在Google上搜索了一下,发现其他报告了同样的事情,但是我没有发现关于为什么会这样的更多见解.

    I googled a little and found that others have reported the same thing but I haven't found any more insight as to why this is the case.

    这篇关于如何为隐式方法实现中间类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆