What is the difference between self-types and trai

2018-12-31 10:06发布

A self-type for a trait A:

trait B
trait A { this: B => }

says that "A cannot be mixed into a concrete class that does not also extend B".

On the other hand, the following:

trait B
trait A extends B

says that "any (concrete or abstract) class mixing in A will also be mixing in B".

Don't these two statements mean the same thing? The self-type seems to serve only to create the possibility of a simple compile-time error.

What am I missing?

11条回答
人气声优
2楼-- · 2018-12-31 11:01
trait A { def x = 1 }
trait B extends A { override def x = super.x * 5 }
trait C1 extends B { override def x = 2 }
trait C2 extends A { this: B => override def x = 2}

// 1.
println((new C1 with B).x) // 2
println((new C2 with B).x) // 10

// 2.
trait X {
  type SomeA <: A
  trait Inner1 { this: SomeA => } // compiles ok
  trait Inner2 extends SomeA {} // doesn't compile
}
查看更多
只靠听说
3楼-- · 2018-12-31 11:03

A self type lets you specify what types are allowed to mixin a trait. For example, if you have a trait with a self type Closeable, then that trait knows that the only things that are allowed to mix it in, must implement the Closeable interface.

查看更多
梦寄多情
4楼-- · 2018-12-31 11:03

Update: A principal difference is that self-types can depend on multiple classes (I admit that's a bit corner case). For example, you can have

class Person {
  //...
  def name: String = "...";
}

class Expense {
  def cost: Int = 123;
}

trait Employee {
  this: Person with Expense =>
  // ...

  def roomNo: Int;

  def officeLabel: String = name + "/" + roomNo;
}

This allows to add the Employee mixin just to anything that is a subclass of Person and Expense. Of course, this is only meaningful if Expense extends Person or vice versa. The point is that using self-types Employee can be independent of the hierarchy of the classes it depends on. It doesn't care of what extends what - If you switch the hierarchy of Expense vs Person, you don't have to modify Employee.

查看更多
流年柔荑漫光年
5楼-- · 2018-12-31 11:08

Section 2.3 "Selftype Annotations" of Martin Odersky's original Scala paper Scalable Component Abstractions actually explains the purpose of selftype beyond mixin composition very well: provide an alternative way of associating a class with an abstract type.

The example given in the paper was like the following, and it doesn't seem to have an elegant subclass correspondent:

abstract class Graph {
  type Node <: BaseNode;
  class BaseNode {
    self: Node =>
    def connectWith(n: Node): Edge =
      new Edge(self, n);
  }
  class Edge(from: Node, to: Node) {
    def source() = from;
    def target() = to;
  }
}

class LabeledGraph extends Graph {
  class Node(label: String) extends BaseNode {
    def getLabel: String = label;
    def self: Node = this;
  }
}
查看更多
谁念西风独自凉
6楼-- · 2018-12-31 11:09

One additional difference is that self-types can specify non-class types. For instance

trait Foo{
   this: { def close:Unit} => 
   ...
}

The self type here is a structural type. The effect is to say that anything that mixes in Foo must implement a no-arg "close" method returning unit. This allows for safe mixins for duck-typing.

查看更多
登录 后发表回答