2010-10-07 4 views
4

Update: Уточнен и расширен, так как исходный вопрос был упрощен слишкомScala рекурсивных дженерик: Родительский [Ребенок] и ребенок [Родитель]

мне нужна пара признаков, каждый из которых относится к другой, что родительский и дочерний классы должны относиться друг к другу.

trait Parent [C <: Child] { 
    def foo(c: C) 
} 

trait Child [P <: Parent] { 
    def parent: P = ... 
    def bar = parent.foo(this) 
} 

Такое, что реализация классов должны прийти в парах:

class ActualParent extends Parent [ActualChild] { 
    def foo(c: ActualChild) = ... 
} 

class ActualChild extends Child [ActualParent] { 
} 

К сожалению, компилятор не любит эти черты, потому что общие типы не являются полными. Вместо C <: Child необходимо сказать C <: Child[что-то]. Оставляя их неопределенные не работает либо:

trait Parent [C <: Child[_]] { 
    def foo(c: C) 
} 

trait Child [P <: Parent[_]] { 
    def parent: P = ... 
    def bar = parent.foo(this) 
} 

Теперь жалуется на parent.foo(this) линии, потому что он не знает, что this имеет правильный тип. Тип parent должен быть Parent[this.type] для звонка foo, чтобы иметь нужные типы.

Я думаю, что должен быть способ обращения к собственному типу объекта? Или к типу, который требуется самому?


Update: В дополнение к @ ответ Даниила, я попытался с помощью абстрактного элемента типа внутри ребенка изложить общие типы родительского типа, как это:

trait Parent [C <: Child] { 
    def foo(c: C) 
} 

trait Child { 
    type P <: Parent[this.type] 

    def parent: P = ... 
    def bar = parent.foo(this) 
} 

Это ISN не работает, когда я пытаюсь его реализовать:

class ActualParent extends Parent [ActualChild] { 
    def foo(c: ActualChild) = ... 
} 

class ActualChild extends Child { 
    type P = ActualParent 
} 

Ошибка

overriding type Parent in trait Child with bounds >: Nothing <: Parent[ActualChild.this.type] 
type Parent has incompatible type 

Что это значит?

ответ

5

Вы можете использовать подход, приведенный в http://programming-scala.labs.oreilly.com/ch13.html:

abstract class ParentChildPair { 
    type C <: Child 
    type P <: Parent 

    trait Child {self: C => 
    def parent: P 
    } 

    trait Parent {self: P => 
    def child: C 
    } 
} 

class ActualParentChildPair1 { 
    type C = Child1 
    type P = Parent1 

    class Child1 extends Child {...} 

    class Parent1 extends Parent {...} 
} 
+0

Это выглядит правильно. Теперь, как бы вы расширили это, чтобы включить цепочки родительского -> child -> child? –

+0

Этот подход успешный, но я не очень доволен этим. Я не уверен, что могу объяснить, почему. –

1

Вы можете написать C <: Child[_].

+1

Я думаю, что будет работать, но я думаю, что ОП после ярлыка для Parent [C Synesso

+0

Как сказал Сынессо, Ребенок [_] не будет принуждать, чтобы родители и дети были сопоставлены друг с другом. –

+0

Почему вы хотите использовать дженерики? Почему бы не использовать параметры: class Parent (c: Child); class Child (p: Parent) – sschaef

3

Это может быть сделано с элементами абстрактного типа.

class Parent { 
    type C <: Child 
    def child: C = null.asInstanceOf[C] 
} 

class Child { 
    type P <: Parent 
    def parent: P = null.asInstanceOf[P] 
} 
+0

Это заставляет меня пропустить предупреждение компилятора, но не гарантирует, что родительский и дочерний классы должны быть связаны друг с другом. –

+0

См. Мой ответ на основе членов абстрактного типа. –

3

В дополнении к @ ответу Дэниела, я могу использовать абстрактный элемент типа внутри ребенка изложить общие типы родительского типа, как это :

trait Parent [C <: Child] { 
    def foo(c: C) 
} 

trait Child { 
    type P <: Parent[this.type] 

    def parent: P = ... 
    def bar = parent.foo(this) 
} 

this.type не может использоваться непосредственно в дженериков, но в качестве параметра, кажется, будет хорошо. Этот подход намного менее длинный, чем окружающий абстрактный класс, и позволяет использовать более гибкие приложения, такие как дети, которые также являются родителями.

1

Несмотря на то, что это не удалось, я запишу эту аллею в качестве ответа.

Использование абстрактных членов типа, государственные границы для типов ссылающихся назад this.type:

trait Parent { 
    type C <: Child { type P <: this.type } 
    def foo(c: C) 
} 

trait Child { 
    type P <: Parent { type C <: this.type } 
    def parent: P 
    def bar = parent.foo(this) 
} 

class ActualParent extends Parent { 
    type C = ActualChild 
    def foo(c: ActualChild) = println("Hello") 
} 

class ActualChild extends Child { 
    type P = ActualParent 
    def parent = new ActualParent 
} 

Проблема здесь заключается в том, что компилятор не связывает this с типом ребенка родителя, поэтому вызов parent.foo(this) результатов в:

type mismatch 
found : Child.this.type (with underlying type Child) 
required: _3.C where val _3: Child.this.P 
Смежные вопросы