双向关联中的泛型

发布于 2024-10-11 03:34:53 字数 977 浏览 9 评论 0原文

假设我有两个类 A 和 B,其中 B 是 A 的子类型。显然,这只是更丰富的类型层次结构的一部分,但我认为这不相关。假设 A 是层次结构的根。有一个集合类 C,它跟踪 A 的列表。但是,我想让 C 变得通用,这样就可以创建一个只保留 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)
    }
}

上面的代码显然在 new B(c) 行上给出了错误“类型不匹配:找到 C[B],需要 C[A]”。

我不知道如何解决这个问题。不可能使 C 在 T 中协变(如 C[+T <: A]),因为 ArrayBuffer 在 T 中是非变体类型。不可能使 B 的构造函数需要 a C[B] 因为 C 不能协变。

我是不是找错树了?我是一个十足的 Scala 新手,所以任何想法和技巧可能都会有帮助。谢谢你!

编辑: 基本上,我想要的是编译器接受两者

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

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

但会拒绝

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

它可能可以将 C 中 ArrayBuffer 的类型放宽为 A 而不是 T,因此在 addEntry 方法中也是如此,如果这有帮助的话。

Let's say I have two classes A and B, with B a subtype of A. This is only part of a richer type hierarchy, obviously, but I don't think that's relevant. Assume A is the root of the hierarchy. There is a collection class C that keeps track of a list of A's. However, I want to make C generic, so that it is possible to make an instance that only keeps B's and won't accept A's.

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)
    }
}

The code above obviously give the error 'type mismatch: found C[B], required C[A]' on the new B(c) line.

I'm not sure how this can be fixed. It's not possible to make C covariant in T (like C[+T <: A]) because the ArrayBuffer is non-variantly typed in T. It's not possible to make the constructor of B require a C[B] because C can't be covariant.

Am I barking up the wrong tree here? I'm a complete Scala newbie, so any ideas and tips might be helpful. Thank you!

EDIT:
Basically, what I'd like to have is that the compiler accepts both

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

and

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

but would reject

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

It's probably possible to relax the typing of the ArrayBuffer in C to be A instead of T, and thus in the addEntry method as well, if that helps.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

情栀口红 2024-10-18 03:34:54

如果你想跟踪 A 的实例,则必须将 C[A] 的实例传递给 B 的构造函数,因为每个 B 也是 A:

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

但是,如果你想跟踪 B,那么你可以'不要将其委托给 A,因为 A 对 B 一无所知。

总的来说,我感觉你的问题有点不适。

If you want to keep track of the instances of A, you have to pass an instance of C[A] to the constructor of B since every B is also an A:

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

If however you want to keep track of the Bs then you can't delegate this down to A since A doesn't know anything about B.

Overall, I have the feeling your problem is somewhat ill posed.

风筝有风,海豚有海 2024-10-18 03:34:54

假设这是可能的。然后您就可以执行以下操作:

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]()
新B(c)

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

但会拒绝

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

但是,如果 B 使用 C[B] 初始化,并且考虑到 B 扩展 A ,那么B将使用C[B]初始化A,从而违反了最后一个要求。

Let's say it was possible. Then you'd be able to do this:

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!
        }
    }
}

Trying to run this code will result in a class cast exception.

Edit

You added this requirement:

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

and

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

but would reject

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

However, if B is initialized with C[B], and given that B extends A, then B will initialize A with C[B], thereby violating the last requirement.

↘紸啶 2024-10-18 03:34:53

不可能在 T 中使 C 协变(例如 C[+T <: A]),因为 ArrayBuffer 在 T 中是非变体类型

不仅因为这个。 addEntry 的类型足以禁止它:

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

val cb: C[B] = ...

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

It's not possible to make C covariant in T (like C[+T <: A]) because the ArrayBuffer is non-variantly typed in T

Not only because of this. The type of addEntry is enough to disallow it:

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

val cb: C[B] = ...

cb.addEntry(b) // works
cb.addEntry(a) // doesn't and shouldn't
满身野味 2024-10-18 03:34:53

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)
    }
}

当然我也对适当的解决方案感兴趣......

Hacky, but seems to work:

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)
    }
}

Of course I would be interested in a proper solution as well...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文