2011-01-08 5 views
1

Предположим, у меня есть два класса A и B, причем B - подтип A. Это, очевидно, только часть более богатой иерархии типов, но я не думаю, что это актуально. Предположим, что A является корнем иерархии. Существует класс коллекции C, который отслеживает список A. Тем не менее, я хочу сделать C generic, так что можно создать экземпляр, который поддерживает только B и не будет принимать A.Дженерики в двунаправленной ассоциации

class A(val c: C[A]) { 
    c.addEntry(this) 
} 
class B(c: C[A]) extends A(c) 
class C[T <: A]{ 
    val entries = new ArrayBuffer[T]() 
    def addEntry(e: T) { entries += e } 
} 
object Generic { 
    def main(args : Array[String]) { 
     val c = new C[B]() 
     new B(c) 
    } 
} 

Код выше, очевидно, дают ошибку 'несоответствие типов: найдено: С [B] требуется C [A]' на new B(c) линии.

Я не уверен, как это можно исправить. Невозможно сделать C ковариант в T (например, C[+T <: A]), потому что ArrayBuffer невообразимо типизирован в T. Невозможно заставить конструктор B потребовать C [B], потому что C не может быть ковариантным.

Я здесь лаяю неправильное дерево? Я полный новичок Scala, поэтому любые идеи и советы могут быть полезными. Спасибо!

EDIT: В принципе, то, что я хотел бы иметь в том, что компилятор принимает и

val c = new C[B]() 
new B(c) 

и

val c = new C[A]() 
new B(c) 

но отвергнут

val c = new C[B]() 
new A(c) 

Это, вероятно, можно чтобы расслабить типизацию ArrayBuffer в C, чтобы быть A вместо T, и, следовательно, в addEntry, если это помогает.

ответ

1

Это не возможно сделать C коварианты в T (например, C[+T <: A]), потому что ArrayBuffer не является variantly набран в T

не только из-за этого. Тип addEntry достаточно, чтобы запретить его:

val a: A = ... 
val b: B = ... 

val cb: C[B] = ... 

cb.addEntry(b) // works 
cb.addEntry(a) // doesn't and shouldn't 
0

Hacky, но, кажется, работает:

class A(val c: C[A]) { 
    c.addEntry(this.asInstanceOf[c.X]) 
} 

class B(c: C[B]) extends A(c) 

class C[+T <: A] { 
    type X <: T 
    val entries = new ArrayBuffer[X]() 
    def addEntry(e: X) { entries += e } 
} 

object Generic { 
    def main(args : Array[String]) { 
     val c = new C(){ type T = B } 
     new B(c) 
    } 
} 

Конечно, я был бы заинтересован в правильном решении, а также ...

0

Если вы хотите отслеживать экземпляры A, вам необходимо передать экземпляр C [A] в конструктор B, поскольку каждый B также является A:

def main(args : Array[String]) { 
    val c = new C[A]() 
    new B(c) 
} 

Если вы хотите отслеживать B, то вы не можете делегировать это до A, так как A ничего не знает о B.

В целом, у меня есть ощущение, что ваша проблема несколько плохо поставлена.

0

Предположим, что это было возможно. Тогда вы сможете это сделать:

class A(val c: C[A]) { 
    c.addEntry(this) 
} 
class B(c: C[A]) extends A(c) 
class C[+T <: A]{ 
    val entries: ArrayBuffer[T] @uncheckedVariance = new ArrayBuffer[T]() 
    def addEntry(e: T @uncheckedVariance) { entries += e } 
} 
object Generic { 
    def main(args : Array[String]) { 
     // Everything's fine so far... 
     val c = new C[B]() 
     c.addEntry(new B(c)) 
     // but, suddenly... 
     val ca: C[A] = c 
     ca.addEntry(new A(ca)) 
     // a problem appears! 
     c.entries forall { 
      case thing: B => true // ok 
      case otherThing => false // not ok -- c now contains an A! 
     } 
    } 
} 

Попытка запуска этого кода приведет к исключению класса.

Редактировать

Вы добавили это требование:

val c = new C[B]() 
new B(c) 

и

val c = new C[A]() 
new B(c) 

но отвергнут

val c = new C[B]() 
new A(c) 

Однако, если B инициализируется C[B], и при условии, что B расширяет A, то B будет инициализировать A с C[B], нарушив тем самым последнее требование.

+0

Это действительно не так, потому что вы объясняете здесь причины, если C является ковариантным, но это не является строго требованием. Я не думаю, что есть необходимость сделать C [B] подтипом C [A]. – Verhoevenv

+0

@Verhoevenv Если 'C [B]' не был подтипом 'C [A]', то как вы ожидаете, что 'new B (c)' будет работать, если 'c' типа' C [B] ' ? Сейчас я смотрю ваш пересмотр, и вы просите что-то невозможное. –

Смежные вопросы