加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 有什么优势来确定一个特征的def的val?

发布时间:2020-12-16 19:01:49 所属栏目:安全 来源:网络整理
导读:在 Scala中,val可以覆盖def,但def不能覆盖val. 那么,宣布一个特征是有优势的吗?喜欢这个: trait Resource { val id: String} 而不是这个 trait Resource { def id: String} 后续的问题是:编译程序在实践中如何处理调用vals和defs,并且它实际上与val做什么
在 Scala中,val可以覆盖def,但def不能覆盖val.

那么,宣布一个特征是有优势的吗?喜欢这个:

trait Resource {
  val id: String
}

而不是这个

trait Resource {
  def id: String
}

后续的问题是:编译程序在实践中如何处理调用vals和defs,并且它实际上与val做什么样的优化?编译器坚持认为vals是稳定的 – 编译器在实践中是什么意思?假设子类实际上是使用val来实现id.在特征中指定为def的是否有惩罚?

如果我的代码本身不需要id成员的稳定性,那么在这些情况下总是使用defs,并且只有在这里确定性能瓶颈时才能切换到val,那么这可能是不可能的?

解决方法

简答:

据我所知,这些值始终通过访问器方法访问.使用def定义一个简单的方法,它返回值.使用val定义一个private [*] final字段和一个访问器方法.所以在访问方面,两者之间的差异很小.差异是概念性的,def每次都被重新评估,val只被评估一次.这显然会对性能产生影响.

[*] Java私有

长答案:

我们来看下面的例子:

trait ResourceDef {
  def id: String = "5"
}

trait ResourceVal {
  val id: String = "5"
}

ResourceDef& ResourceVal产生相同的代码,忽略初始化器:

public interface ResourceVal extends ScalaObject {
    volatile void foo$ResourceVal$_setter_$id_$eq(String s);
    String id();
}

public interface ResourceDef extends ScalaObject {
    String id();
}

对于生成的子类(其中包含方法的实现),ResourceDef产生的就是你所期望的,注意到该方法是静态的:

public abstract class ResourceDef$class {
    public static String id(ResourceDef $this) {
        return "5";
    }

    public static void $init$(ResourceDef resourcedef) {}
}

对于val,我们简单地在包含类中调用initializer

public abstract class ResourceVal$class {
    public static void $init$(ResourceVal $this) {
        $this.foo$ResourceVal$_setter_$id_$eq("5");
    }
}

当我们开始扩展:

class ResourceDefClass extends ResourceDef {
  override def id: String = "6"
}

class ResourceValClass extends ResourceVal {
  override val id: String = "6"
  def foobar() = id
}

class ResourceNoneClass extends ResourceDef

我们在哪里重写,我们得到一个方法,在类中只是做你期望的. def是简单的方法:

public class ResourceDefClass implements ResourceDef,ScalaObject {
    public String id() {
        return "6";
    }
}

而val定义了一个私有域和访问器方法:

public class ResourceValClass implements ResourceVal,ScalaObject {
    public String id() {
        return id;
    }

    private final String id = "6";

    public String foobar() {
        return id();
    }
}

请注意,即使foobar()也不使用字段id,而是使用访问器方法.

最后,如果我们不覆盖,那么我们得到一个在trait辅助类中调用static方法的方法:

public class ResourceNoneClass implements ResourceDef,ScalaObject {
    public volatile String id() {
        return ResourceDef$class.id(this);
    }
}

我在这些例子中删除了构造函数.

所以,始终使用访问器方法.我认为这是为了避免在扩展可以实现相同方法的多个特征时的并发症.它真的很复杂.

更长的答案:

Josh Suereth在Binary Resilience at Scala Days 2012举行了一个非常有趣的演讲,介绍了这个问题的背景.这个摘要是:

This talk focuses on binary compatibility on the JVM and what it means
to be binary compatible. An outline of the machinations of binary
incompatibility in Scala are described in depth,followed by a set of rules and guidelines that will help developers ensure their own
library releases are both binary compatible and binary resilient.

In particular,this talk looks at:

  • Traits and binary compatibility
  • Java Serialization and anonymous classes
  • The hidden creations of lazy vals
  • Developing code that is binary resilient

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读