【问题标题】:val and object inside a scala class?scala类中的val和object?
【发布时间】:2018-01-10 02:14:12
【问题描述】:

在 scala 类中将字段声明为 vallazy valobject 有什么区别,如下面的 sn-p:

class A

class B {
  val a1 = new A      { def foo = 1 }
  object a2 extends A { def foo = 1 }
  lazy val a3 = new A { def foo = 1 }
}

【问题讨论】:

标签: scala class object


【解决方案1】:

在前者中,包含的任何代码都会在创建类 B 后立即执行。然而,在后者中,在您实际使用该对象之前,它不会被实例化。

你可以在这里看到区别:

class A { println("Creating a new A") }
class B {
  val a1 = new A { println("a1"); def foo = 1 }
  object a2 extends A { println("a2"); def foo = 1 }
}

scala> val b = new B
Creating a new A
a1
b: B = B@1176e8a

scala> b.a2.foo
Creating a new A
a2
res0: Int = 1

在创建的 .class 文件的名称等方面也存在隐藏差异;当然,两者有不同的类型。

【讨论】:

  • 所以objectlazy val 一样工作。两者之间有什么实际区别吗?
  • 本质上,不,据我所知。 object 的字节码更紧凑一些。我不确定这是否意味着lazy val 的编写效率低下,或者object 在某些线程条件下是否不安全,或者两者兼而有之。
  • 或者不如我当时记得的那样。请参阅 Alex Boisvert 的评论,了解关键区别!
  • 就其价值而言,在我读过的 Scala 代码中,惰性 val 似乎确实优于对象成员。如果您在野外看到一个对象成员,它可能来自 Scala 2.6 之前的版本,当时引入了惰性 val。
  • 实际区别:调用对象中定义的方法比调用匿名类中定义的方法更有效。
【解决方案2】:

我不确定aioobe 是否认识到his answer 的重要性,但不同类型实际上代表了valsobjects 之间的关键区别。特别是,vallazy val 具有结构类型(例如A{def foo: Int}),而object 具有单例类型。因此,对vals 上的foo 方法的调用涉及反射,而对object 上的foo 方法的调用则不会:

class A

class B {
  val a1 = new A      { def foo = printStack }
  object a2 extends A { def foo = printStack }
  lazy val a3 = new A { def foo = printStack }

  def printStack() = 
     new Exception().getStackTrace take 3 foreach println
}

scala> val b = new B
b: B = B@5c750

scala> b.a1.foo   // the val
line124$object$$iw$$iw$B.printStack(<console>:12)
line124$object$$iw$$iw$B$$anon$1.foo(<console>:7)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

scala> b.a2.foo   // the object
line124$object$$iw$$iw$B.printStack(<console>:12)
line124$object$$iw$$iw$B$a2$.foo(<console>:8)
line128$object$$iw$$iw$.<init>(<console>:9)

scala> b.a3.foo   // the lazy val
line124$object$$iw$$iw$B.printStack(<console>:12)
line124$object$$iw$$iw$B$$anon$2.foo(<console>:9)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

【讨论】:

  • 佐伊克斯!我不知道匿名类使用反射。我想最好避免将它们用于 pimp-my-library 模式:最好定义一个命名类。匿名类在 scala-swing 中被广泛使用,但速度在那里并不重要。
  • @Luigi Plinge 匿名类仅对未在父类/接口中定义的方法使用反射。如果A 定义了foo,即使是抽象的,也不需要反射。
【解决方案3】:

一个主要区别是 val 可以被覆盖,而对象不能。

class C extends B {                           
  override val a1 = new A { def foo = 2 }     
  override object a2 extends A { def foo = 2 }
}

导致:

<console>:9: error: overriding object a2 in class B of type object C.this.a2;
object a2 cannot be used here - classes and objects cannot be overridden
override object a2 extends A { def foo = 2 }

【讨论】:

    【解决方案4】:

    我想一个区别是a1 将属于A 的一个子类型,而a2 将属于A 的另一个子类型,即a2.type

    scala> class A
    defined class A
    
    scala> val a1 = new A {def foo = 1}
    a1: A{def foo: Int} = $anon$1@a9db0e2
    
    scala> object a2 extends A {def foo = 1}
    defined module a2
    
    scala> a1
    res0: A{def foo: Int} = $anon$1@a9db0e2
    
    scala> a2
    res1: a2.type = a2$@5b25d568
    
    scala> 
    

    【讨论】:

    • 不完全是。两者都是A 的子类。
    • 当然可以,但它们不会属于同一类型。
    • 更准确地说,a2 的类型为 a2.type。
    • 请注意,您的示例 Scala 输出与您在答案的第一句话中所说的相矛盾:a1 的类型是 not A; type 是您创建的匿名子类的类型。您可以通过显式指定类型使其成为Aval a1: A = new A {def foo=1}
    • @Jesper:在这个具体的例子中,是的。然而,他确实提出了一个有效的观点。使用 object always 会创建一个新类型,而使用 val 不会(除非您在这种情况下添加行为)。
    【解决方案5】:

    另一个主要区别是对象知道自己的名称而 val 不知道。

    【讨论】:

      【解决方案6】:

      第一个实际区别是惰性 val 和对象是惰性的,而 val 是渴望的。

      对象和惰性 val 之间的主要区别在于,从语言的角度来看,对象被认为是“单例”,从 jvm 的角度来看,它通常被视为静态成员。给定示例中的对象定义不能被覆盖,正如其他人所展示的那样,静态成员不能被覆盖的原因相同:如果不绑定到实例,就无法进行虚函数查找。

      object Foo { object Bar extends A; }
      

      大致类似于下面的 java 代码:

      class Foo { 
        private static class Bar extends A{}
        public static Bar Bar = new Bar;
      }
      

      如果在上面的例子中,如果定义了一个子类 C extends Foo,它就不能覆盖 Bar 的定义。 Java 中的静态实例 Bar 将作为 Foo.Bar 访问。 C.Bar 与 (new C).Bar 的含义不同。我可能有点跑题了,我还没有真正尝试过反编译scala代码,这只是一个例子来说明对象作为静态成员的一般概念。

      lazy vals 的效率可能会低一些。上次我检查时,它们是通过在类中维护一个隐藏字段来实现的,该字段跟踪哪些惰性 val 已被初始化。维护此字段需要锁定,这可能会导致性能问题。

      lazy val 和 object 之间的一个主要实际区别是对失败的处理:

      如果我有:

      class Foo() { throw new Exception("blah!"); }
      object Stuff { object Bar extends Foo { val x = "hi" } }
      Stuff.Bar
      //exception "blah!" thrown.
      Stuff.Bar.x
      //NoClassDefFoundError: Could not initialize Stuff$Bar$
      

      如果我这样做:

      object Stuff2 { lazy val Bar = new Foo() { val x = "hi" } }
      Stuff2.Bar
      // "blah!"
      Stuff2.Bar.x
      // "blah!"
      

      “NoClassDefFoundError”可能真的令人困惑,因为它是错误而不是异常,它可能会破坏(适当地)捕获/记录“异常”但允许错误传播的错误处理代码。我什至可能会考虑 Scala 语言中的这种错误,因为这个用例确实表明了一种异常情况,而不是真正的 JVM 错误。在访问依赖于外部资源的对象(例如数据库连接或磁盘上的文件)时,我已经看到了 NoClassDefFoundErrors。只有第一次访问会记录根本原因,因此正确调试此类问题通常需要重新启动应用程序服务器。

      【讨论】:

        【解决方案7】:

        不是结构类型:val a = new A { def foo = 1 }

        它创建了一个唯一的匿名子类; a.foo 在该类中调用 foo。

        这里的x是一个结构类型:def bar( x: { def bass: Int} )

        x.bass 将自省 x(未知类型)以查找名称为“bass”的方法。它适用于鱼或乐器。 ;)

        lazy val 和 object 之间的一个区别是:

        var someA = (new B).a3
        someA = (new B).a3 // ok
        
        var anotherA = (new B).a2
        anotherA =  = (new B).a2 // compile error
        

        【讨论】:

        • 你得到一个编译错误的原因是因为你有两个等号一个接一个
        猜你喜欢
        • 1970-01-01
        • 2018-10-01
        • 2018-08-15
        • 2011-05-25
        • 2021-02-16
        • 1970-01-01
        • 2014-09-14
        • 2015-09-12
        相关资源
        最近更新 更多