【问题标题】:Is there a "SELF" type in scala that represents the current type?scala中是否有代表当前类型的“SELF”类型?
【发布时间】:2012-01-31 18:13:58
【问题描述】:

我正在学习 Scala,但关于该语言有一点我不知道:

前段时间我在 Lisaac 中编程很舒服,在 Lisaac 中我可以编写一个带有插槽 list:ARRAY[SELF] 的类 PERSON,这相当于拥有 list:ARRAY[PERSON],因为 SELF 是该插槽所在的对象。

但是通过使用SELF,如果我编写一个继承自PERSON 的第二个类STUDENT,那么STUDENT 将继承将SELF 更改为STUDENT 的槽,所以STUDENT 将有一个STUDENT 的列表,而不是 PERSON

这可以在 Scala 中完成吗?我对此一无所知。

谢谢!

【问题讨论】:

    标签: inheritance scala


    【解决方案1】:

    有一个习惯用法,它在集合框架中被广泛使用(在所有 *Like 类中,例如 TraversableLike)。 您需要将 self-type 添加为超类的类型参数(就像在 C++ 中使用 CRTP 一样):

    trait Person[+Self] {
      this: Self => //Declare that any concrete subclass must implement Self; therefore, this can be used with type Self.
      //val list: Array[Self] //Not sure that this will work so easily, for the same reason new T[] does not work in Java.
      val list = Seq[Self]() //No problem here; Array is really special.
    }
    

    定义完这个类后,我们可以尝试在解释器中定义子类:

    scala> class Student extends Person[Student]
    defined class Student
    scala> (new Student).list
    res0: Seq[Student] = List() //Note the result type
    scala> class Student2 extends Person[Student] //Note the mistake
    <console>:9: error: illegal inheritance;
     self-type Student2 does not conform to Person[Student]'s selftype Person[Student] with Student
           class Student2 extends Person[Student]
    

    一个没有被阻止的错误是有这个定义,其中 Self 没有被重新定义:

    scala> class SpecStudent extends Student
    defined class SpecStudent
    

    感谢Self前面的+,这使它成为协变类型参数(我没有解释它是什么),但这至少是可能的:

    scala> class SpecStudentCorrect extends Student with Person[SpecStudentCorrect]
    
    scala> (new SpecStudentCorrect).list
    (new SpecStudentCorrect).list
    res1: Seq[SpecStudentCorrect] = List()
    

    【讨论】:

    • 您确定这个超过两年的问题没有被接受的答案充分涵盖吗?
    • 如果您阅读了该答案的 cmets,您会同意我的看法。
    • 此外,您为什么认为我没有检查,甚至没有阅读接受的答案?现在,您提供的链接确实提供了比接受的链接更好的答案,但我确实发现您的答案文本令人困惑。但是,我给出的解决方案是最常用的一种,也因为它比该链接中描述的更好:当Type抽象类型成员this: Type =>这样的自类型注释>属于同一类,但前提是它位于外部类中。
    • 例如,trait Person { this: Self =&gt; type Self }trait FooBase { type Bar }; trait FooDer extends FooBase { this: Bar =&gt; } 都不起作用,因为找不到 Bar
    • 当您嵌套需要这些自身类型的多种类型时,这尤其有用。
    【解决方案2】:

    我不确定这是否真的对你有用,但我能想到的最接近的是this.type。例如:

    scala> class A { val l: List[this.type] = Nil }  
    defined class A
    
    scala> new A().l
    res3: List[A] = List()
    
    scala> class B extends A
    defined class B
    
    scala> new B().l
    res4: List[B] = List()
    

    【讨论】:

    • 我不认为“this.type”是这里需要的。 "this.type" 是单例类型;它只是“this”实例的类型。尽你所能,“List[this.type]”可以包含的有效元素是“this”。
    • 是的,你是对的。我试图准确地记住它是如何工作的。
    【解决方案3】:

    Scala 中的this 关键字或多或少是等价的。

    在开发可扩展软件时,明确声明值this 的类型有时会很方便:

    Scala 中的显式类型自引用
    http://www.scala-lang.org/node/124

    【讨论】:

    • this 关键字不表示类型。
    • > Scala 中的 this 关键字或多或少是等价的。你指的是什么?如果它不等同于 Lisaac 的 SELF(它是一种类型),那么您的意思是什么? OP 要求一种表示类型的方法,因为他想编写类似 'Array[Self]' 的东西。并且有一个常用的模式来实现这一点,即使它有局限性(见我的回答)。
    • 我提供的链接肯定会澄清。
    • 我看过了;再看一遍,我意识到它显示了另一种解决方案,其中Self 被声明为抽象成员而不是参数。
    【解决方案4】:

    单例类型和 ETSR 不能解决问题。我自己也在 Scala 中寻找相同的功能,但显然它缺少所谓的自类型注释。

    在某些情况下,此类自类型注释可能非常有用。考虑一个例子(改编自Circular type parameters question example):

    // we want a container that can store elements
    trait Container[E <: Element[E]] {
      def elements: Seq[E]
      def add(elem: E): Unit
    }
    
    // we want elements be aware of their enclosing container
    trait Element[E <: Element[E]] {
      def container: Container[E]
    }
    

    假设您将其放入库中。图书馆使用者应该做到以下几点:

    object PersonContainer extends Container[Person] {
      // actual implementation is not important
      def elements = Nil
      def add(p: Person) = {}
    }
    
    class Person extends Element[Person] {             // {1}
      def container = PersonContainer
    }
    

    没关系,一切都按预期进行。唯一需要担心的是库使用者应该使用自绑定类型参数(代码中的#1)。但这还不是全部。现在假设您有某种 ActiveRecord 模式,并且您想将方法 save 添加到 Element,它只是委托给它的容器的 add 方法。令人惊讶的是,这并不容易:

    trait Element[E <: Element[E]] {
      def container: Container[E]
      def save() = container.add(this)   // won't compile
    }
    
    found   : Element[E]
    required: E
    

    直观地说,我们有几个选择:

    • 使add 方法接受Element[E] 而不是E
    • this 转换为Element[E]

    这些选项都不令人满意,只是因为EElement[E] 不一样(实现不强制使用自绑定类型参数)。我认为解决这个问题的唯一方法是在 Scala 中使用 self-type 概念(假设我们使用我们最喜欢的语言):

    trait Container[E <: Element] {
      def elements: Seq[E]
      def add(elem: E): Unit
    }
    
    trait Element {  // the type parameter would be redundant ...
      def save() = container.add(this)  // ... and this would be possible, too, ...
      def container: Container[this]  // ... if only we could do this
    }
    

    如果编译器可以将this(或者可能是另一个关键字),当它在方括号内使用时,作为实际实现的类型(即与obj.getClass的结果相同的类型),那么问题会消失。

    附:有人可以考虑将这些东西包含在 Scala 愿望清单中吗?不幸的是,我不知道实现这样的逻辑有多难,因为臭名昭著的 JVM 擦除可能会出现问题。

    附言或者也许还有其他我不知道的 Scala 方式?

    【讨论】:

    • 你需要在 trait 元素中添加一个自类型注解:trait Element[E &lt;: Element[E]] { this: E =&gt; ... } 一旦你有了,你可以省略类型绑定。
    猜你喜欢
    • 1970-01-01
    • 2013-04-04
    • 1970-01-01
    • 1970-01-01
    • 2020-11-06
    • 2015-10-08
    • 1970-01-01
    • 1970-01-01
    • 2015-07-09
    相关资源
    最近更新 更多