Scala 通过手动调用构造函数创建道具对象是否安全且推荐?

Scala 通过手动调用构造函数创建道具对象是否安全且推荐?,scala,akka-actor,Scala,Akka Actor,我正试图与Akka演员建立亲缘关系,但我无法理解这两个问题: 首先,正如所解释的,闭包可能导致序列化问题。下面的示例包含一个不可序列化的Props对象,因为它关闭在一个不可序列化的对象上: case class Helper(name: String) object MyNonserializableObject { val helper = Helper("the helper") val props7 = Props(new MyActor(helper)) } 所以建议

我正试图与Akka演员建立亲缘关系,但我无法理解这两个问题: 首先,正如所解释的,闭包可能导致序列化问题。下面的示例包含一个不可序列化的Props对象,因为它关闭在一个不可序列化的对象上:

case class Helper(name: String)

object MyNonserializableObject {

   val helper = Helper("the helper")

   val props7 = Props(new MyActor(helper))
}
所以建议不要创造这样的演员。以上答案与Akka docs有关。 另一方面,当我们将值类作为构造函数参数处理时,Akka文档通过手动调用构造函数来创建道具,下面代码中的
props3
就是一个例子:

class Argument(val value: String) extends AnyVal

class ValueClassActor(arg: Argument) extends Actor {
  def receive = { case _ => () }
}

object ValueClassActor {
  def props1(arg: Argument) = Props(classOf[ValueClassActor], arg) // fails at runtime
  def props2(arg: Argument) = Props(classOf[ValueClassActor], arg.value) // ok
  def props3(arg: Argument) = Props(new ValueClassActor(arg)) // ok
}
这两个概念在我看来似乎自相矛盾。
顺便说一句,由于我的排名,我不能把这个问题作为一个评论来回答。

如果你知道JVM是如何工作的,这更容易理解。如果使用[ValueClassActor]的类和参数列表实例化对象,JVM必须从
类中提取
构造函数
,然后使用Java反射API实例化对象

同时,如果你看一下什么是
AnyVal
s,你会看到这个类正在使用
AnyVal

class Argument(val value: String) extends AnyVal

class ValueClassActor(arg: Argument)
汇编至:

Compiled from "test.scala"
public class ValueClassActor {
  public ValueClassActor(java.lang.String);
    Code:
       0: aload_0
       1: invokespecial #14                 // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 3: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       5     0  this   LValueClassActor;
          0       5     1   arg   Ljava/lang/String;
}
要避免处理此问题,可以使用不依赖运行时反射的:

def apply[T <: Actor](creator: => T)(implicit arg0: ClassTag[T]): Props
看看生成的字节码,你会发现

Compiled from "foo.scala"
public class Foo {
  public static Foo hello();
    Code:
       0: getstatic     #16                 // Field Foo$.MODULE$:LFoo$;
       3: invokevirtual #18                 // Method Foo$.hello:()LFoo;
       6: areturn

  public Foo(scala.Function0<java.lang.String>);
    Code:
       0: aload_0
       1: invokespecial #25                 // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 3: 0
      line 1: 4
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       5     0  this   LFoo;
          0       5     1     s   Lscala/Function0;
}
闭包将包含对
Baz
的引用,这将包含对
Bar
的引用,并且如果扩展的
Bar
不可序列化,那么闭包也不会序列化。但是,如果您将在顶级的
对象
中生成lambda(不嵌套在其他类中,等等),那么您的闭包只能依赖于可序列化的东西(因为
对象
本身具有空构造函数并实现
可序列化
接口),因此,它本身是可序列化的

当涉及到
Props
和按名称参数时,同样的原则也适用。如果在顶级(或以其他方式保证可序列化)的伴生对象中使用by name param创建
Prop
,则闭包也将可序列化,使用也将是安全的。就像医生建议的那样

长话短说:

class ValueClassActor(arg: Argument) extends Actor {
  def receive = { case _ => () }
}

object ValueClassActor {
  def props(arg: Argument) = Props(new ValueClassActor(arg))
}

是安全的。

感谢您的澄清,但我仍然无法理解安全示例与我的第一个不安全示例中的props7之间的区别。这个解释更好吗?是的,非常感谢!这个例子的问题是Helper是case类,与某种特征相关,因此它使Props不可序列化,是吗?这个例子中的代码是可序列化的,所以我只能假设这是一个占位符,用于实际项目中不存在的内容。所以我们可能应该注释或标记相关的帖子?
Compiled from "foo.scala"
public class Foo {
  public static Foo hello();
    Code:
       0: getstatic     #16                 // Field Foo$.MODULE$:LFoo$;
       3: invokevirtual #18                 // Method Foo$.hello:()LFoo;
       6: areturn

  public Foo(scala.Function0<java.lang.String>);
    Code:
       0: aload_0
       1: invokespecial #25                 // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 3: 0
      line 1: 4
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       5     0  this   LFoo;
          0       5     1     s   Lscala/Function0;
}
Compiled from "foo.scala"
public final class Foo$ {
  public static final Foo$ MODULE$;

  public static {};
    Code:
       0: new           #2                  // class Foo$
       3: dup
       4: invokespecial #17                 // Method "<init>":()V
       7: putstatic     #19                 // Field MODULE$:LFoo$;
      10: return
    LineNumberTable:
      line 3: 0

  public Foo hello();
    Code:
       0: new           #23                 // class Foo
       3: dup
       4: invokedynamic #44,  0             // InvokeDynamic #0:apply:()Lscala/Function0;
       9: invokespecial #47                 // Method Foo."<init>":(Lscala/Function0;)V
      12: areturn
    LineNumberTable:
      line 4: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      13     0  this   LFoo$;

  public static final java.lang.String $anonfun$hello$1();
    Code:
       0: ldc           #50                 // String test
       2: areturn
    LineNumberTable:
      line 4: 0
}
trait Bar {

  object Baz {
    def hello: Foo = new Foo("test")  // "test" is by-name so it has closure
  }
}
class ValueClassActor(arg: Argument) extends Actor {
  def receive = { case _ => () }
}

object ValueClassActor {
  def props(arg: Argument) = Props(new ValueClassActor(arg))
}