scala中的trait

特质是一些字段和行为的集合,可以扩展或混入(mixin)你的类中。

1 trait Car {
2   val brand: String
3 }
4 
5 trait Shiny {
6   val shineRefraction: Int
7 }
1 class BMW extends Car {
2   val brand = "BMW"
3 }

通过with关键字,一个类可以扩展多个特质:

1 class BMW extends Car with Shiny {
2   val brand = "BMW"
3   val shineRefraction = 12
4 }

什么时候应该使用特质而不是抽象类? 如果你想定义一个类似接口的类型,你可能会在特质和抽象类之间难以取舍。这两种形式都可以让你定义一个类型的一些行为,并要求继承者定义一些其他行为。一些经验法则:

  • 优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
  • 如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行。例如,你不能说trait t(i: Int) {},参数i是非法的。

你不是问这个问题的第一人。可以查看更全面的答案: stackoverflow: Scala特质 vs 抽象类 , 抽象类和特质的区别, and Scala编程: 用特质,还是不用特质?

 

在Scala中有一个trait类型,它可以被继承,而且支持多重继承,其实它更像我们熟悉的接口(interface),但它与接口又有不同之处是: 

 1 //声明一个 trait
 2 trait TraitBase{
 3   def add(x:Int,y:Int): Int ={
 4     return x+y
 5   }
 6 }
 7 //TraitTest 继承自 TraitBase
 8 class TraitTest extends TraitBase{
 9 //重写父类的方法
10   override def add(x:Int,y:Int): Int ={
11     return x+y*10
12   }
13 }
14 
15 //如果需要调用父类的方法,使用super访问父类
16 class TraitTest extends TraitBase{
17   override def add(x:Int,y:Int): Int ={
18     return super.add(x,y)*10
19   }
20 }
21 
22 //使用
23     val test = new TraitTest
24     println(test.add(4,5))

对于多重继承,我们使用 with 关键字

 1 trait A{
 2   def FA(): Unit ={
 3     println("FA")
 4   }
 5 }
 6 trait B{
 7   def FB(): Unit ={
 8     println("FB")
 9   }
10 }
11 class C{
12   val content=null
13 }
14 //多重继承
15 class D extends C with A with B{
16 //这里不需要必须实现trait中的方法
17 }

看完了使用,我们看看编译器把trait编译成什么样的java对象了。编译后有两个文件(TraitBase$class.class和TraitBase.class) 
先来看看TraitBase.class

1 /**很熟悉吧,interface,看来trait还是跟interface有共性*/
2 public abstract interface TraitBase
3 {
4   public abstract int add(int paramInt1, int paramInt2);
5 }

再看看TraitBase$class.class

 1 /**抽象类
 2 它内部都是static的静态方法,但是大家注意
 3 每个方法都带有一个参数,TraitBase 对象,通过这个对象,可以访问实际对象中定义的变量和方法
 4 */
 5 public abstract class TraitBase$class
 6 {
 7   public static int add(TraitBase $this, int x, int n)
 8   {
 9     return x + n;
10   }
11 
12   public static void $init$(TraitBase $this)
13   {
14   }
15 }

对于继承自trait的TraitTest,反编译结果如下

 1 /**中规中矩的接口继承*/
 2 public class TraitTest  implements TraitBase
 3 {
 4   public int add(int x, int n)
 5   {
 6   //调用的是TraitBase$class类中的add方法,并传递了自己这个实例对象
 7     return TraitBase.class.add(this, x, n); 
 8   } 
 9   //初始化
10   public TraitTest() 
11   { 
12       TraitBase.class.$init$(this); 
13   }
14 
15 }

属性的访问,假如有下面的Scala代码

1 trait TraitBase{
2   val content = "this is trait test"
3 }
4 
5 class TraitTest extends TraitBase{
6   def Foo(): Unit ={
7     println(content)
8   }
9 }

反编译结果 
TraitBase.class

1 public abstract interface TraitBase
2 {
3 /**编译器自动增加了两个接口,对content属性赋值和取值*/
4 
5   //写接口,编译器自动命名,就是我们常用的stter
6   public abstract void scala$test$TraitBase$_setter_$content_$eq(String paramString);
7   //读接口,就是我们常用的getter
8   public abstract String content();
9 }

TraitBase$class.class

1 public abstract class TraitBase$class
2 {
3 //初始化,初始化变量
4   public static void $init$(TraitBase $this)
5   {
6     $this.scala$test$TraitBase$_setter_$content_$eq("this is trait test");
7   }
8 }

看来trait最终编译为interface和abstract class的两个文件

posted on 2016-01-04 14:27  重八  阅读(1484)  评论(0编辑  收藏  举报

导航