探索Scala(2)-- Traits

本文记录我对Scala语言Traits的一些理解。

trait >= interface

Scala语言没有接口(Interface)的概念,取而代之的,是功能更加强大的Trait。因此,interface在Scala语言里并不是关键字,我们可以自由的使用它,如下面这段代码所示:


但是要注意,上面的代码虽然是合法的Scala代码,能编译出ScalaObject.class。但是如果想在Java里正常使用这个class的话,就会遇到问题。

没有具体方法的Trait会被编译成接口

如果一个Trait没有定义任何有具体实现的方法,那么它和接口是等价的。换句话说,如果一个Trait的所有方法(如果有的话)全都是抽象的,那么Scala会把它编译成Java接口。比如下面这个没有任何方法的TraitA

会被编译成TraitA.class,分析class文件可以知道,它其实等价于下面的Marker接口:

public interface TraitA {

}
再比如下面这个有两个抽象方法的 TraitB

会被编译为:

public interface TraitB {
    public int m1();
    public int m2(int arg);
}

有具体方法的Trait会被编译成两个class文件

以下面这个TraitC为例:


编译之后得到两个class文件:TraitC.classTraitC$class.class。分析class文件可以知道,TraitC.class实际上是一个接口,如下所示:

public interface TraitC {
    public int m3(int arg);
}
方法实现在TraitC$class.class里,如下所示:

public abstract class TraitC$class {
    public static int m3(TraitC t, int arg) {
        return 1;
    }
    public static void $init$() {
        //return;
    }
}
由此可知:

  1. Trait会被编译为等价的接口
  2. 如果Trait有具体方法,则这些方法会被复制到相应的$class类里,并且有下面两处变动:
    1. 方法变为static
    2. Trait实例被插入到参数列表的最开始

字段(Fields)

如果Trait定义了字段呢?比如下面这个TraitD

编译之后,仍然会得到两个class文件,如下所示:

public interface TraitD {
    public int f1();
    public void f1_$eq(int i);
}
public abstract class TraitD$class {
    public static void $init$(TraitD t) {
        t.f1_$eq(1);
    }
}
由此可知:

  1. Trait仍然被编译成了等价的接口,但var字段被替换成了一对儿getter/setter方法。需要注意的是,这对getter/setter方法并没有按照JavaBean风格来命名
  2. 相应的$class类里只有一个$init$方法

Mix in Traits

下面通过一个类来观察一下mix in上面提到的Traits之后,会发生什么:


下面是反编译之后的MyClass(Java)代码:

public class MixedIn implements TraitA, TraitB, TraitC, TraitD {

    private int f1;
    
    public MixedIn() {
        TraitC$class.$init$(this);
        TraitD$class.$init$(this);
    }

    public int f1() {
        return this.f1;
    }
    public void f1_$eq(int val) {
        this.f1 = val;
    }

    public int m1() {
        return 0;
    }
    public int m2(int arg) {
        return arg;
    }

    public int m3(int arg) {
        return TraitC$class.m3(this, arg);
    }

}

分析如下:

  • TraitA没有定义任何方法,所以只是继承了接口
  • TraitB有两个抽象方法(m1、m2),所以我们必须自己实现这两个方法
  • TraitC有一个具体方法(m3),被Scala编译器实现
  • TraitD定义了一个字段,也被编译器实现
  • 编译器还生成了一个构造函数,调用了TraitC和TraitD的$init$方法



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值