【问题标题】:Generic classes in Java (type-safely) depending on each otherJava 中的泛型类(类型安全)相互依赖
【发布时间】:2009-03-26 11:42:48
【问题描述】:

我想创建一个抽象集合类(称为 Space)和一个 抽象元素类(称为 Atom)。两者的实例必须相互了解(准确键入)。 这就是问题所在。

abstract class Space<A extends Atom>{
  // ...
}

abstract class Atom<S extends Space>{
  // ...
}

不好:

“A extends Atom”是指任何 Atom,但不是强类型的 Atom

“S extends Space”是指任何空间,但不是强类型空间

通过以下尝试我也无法达到完全的类型安全:

abstract class Space<A extends Atom<? extends Space>>
abstract class Atom<S extends Space<? extends Atom>>

abstract class Space<S, A extends Atom<S extends Space<A>>>
abstract class Atom<A, S extends Space<A extends Atom<S>>>

and so on ...

记住,这两个类是抽象的,我希望任何可能的 两个子类是根据彼此类型分型的。 这意味着,以下示例中的 SomeSpace 和 SomeAtom 类 必须对彼此有很强的“类型知识”:

class SomeSpace extends Space<SomeAtom>
class SomeAtom extends Atom<SomeSpace>

【问题讨论】:

  • 好吧,我会咬人的……你为什么要这样做? :) 这是你的实际抽象还是只是问题的一个例子?
  • 真实代码示例请看我自己的answer

标签: java generics types


【解决方案1】:

这对我有用,尽管我对所有这些通用约束感到非常困惑。这意味着我不能保证它会做它应该做的事情:

interface ISpace<S extends ISpace<S, A>, A extends IAtom<S, A>> {
        List<? extends IAtom<S, A>> getList(); //// CHANGED
}

interface IAtom<S extends ISpace<S, A>, A extends IAtom<S, A>> {
        S getSpace();
}

abstract class Space<S extends Space<S, A>, A extends Atom<S, A>>
                implements ISpace<S, A> {

        private final List<Atom<S, A>> atoms = new LinkedList<Atom<S, A>>(); ////CHANGED

        public Space() {
        }
        public Space<S, A> getSpace() {
                return this;
        }
        @Override
        public List<Atom<S, A>> getList() {  //// CHANGED
                return atoms;
        }
}

abstract class Atom<S extends Space<S, A>, A extends Atom<S, A>>
                implements IAtom<S, A> {

        private final S space;

        public Atom(S someSpace) {
                this.space = someSpace;
                space.getList().add(this);
        }

        @Override
        public S getSpace() {
                return space;
        }

        public Atom<S, A> getAtom() {
                return this;
        }
}

class Space1 extends Space<Space1, Atom1> {
        public Space1() {
        }
}

class Atom1 extends Atom<Space1, Atom1> {
        public Atom1(Space1 someSpace) {
                super(someSpace);
        }
}

【讨论】:

  • 是的,这也有效,谢谢!所以“秘密”是用比接口定义的更具体的类型覆盖 getList()。
【解决方案2】:
abstract class Space<S extends Space<S,A>, A extends Atom <A, S>> {}
abstract class Atom <A extends Atom <A,S>, S extends Space<S, A>> {}

或者,我猜,如果你愿意的话

abstract class Space<S extends Space<S,A>, A extends Atom<S, A>> {}
abstract class Atom <S extends Space<S,A>, A extends Atom<S, A>> {}

【讨论】:

  • 我倾向于使参数的顺序一致。
  • Tom,你的建议有效,但是一个很特别的东西不起作用(很奇怪),请在我的“答案”中查看整个代码,希望您能告诉我问题是什么?谢谢
【解决方案3】:

我不会尝试用泛型来解决这个问题。相反,提供一个始终创建匹配对象的工厂。

class UniverseFactory {
    public Space getSpace();
    public Atom getAtom();
}

然后您可以在您的实现中执行类型检查以确保仅使用匹配的对象。

【讨论】:

    【解决方案4】:

    让 Java 中的参数化类型完全了解其实际具体类型的唯一方法是在构造时传入类型参数:

    public Foo<T>(class<T> klass, // other parameters
    

    元答案是循环类型依赖是有问题的,并且表明需要重新审视您的设计。一般来说,更好的解决方案是对第三种类型进行分层依赖:

    public class SomeAtom extends Atom<Something>
    public class SomeSpace extends Space<Something>
    

    元元答案是“类型安全”有其局限性,无论是什么语言:http://www.kdgregory.com/index.php?page=java.generics.cpp

    【讨论】:

      【解决方案5】:

      谢谢你,“Tom Hawtin - tackline”,我已经采纳了你的答案, 它适合。此外,以下代码演示了(有点奇怪) 需要更改列表类型参数(如果您需要原子列表)

      interface ISpace<S extends ISpace<S, A>, A extends IAtom<S, A>> {
          List<IAtom<? extends S, ? extends A>> getList();
      }
      interface IAtom<S extends ISpace<S, A>, A extends IAtom<S, A>> {
          S getSpace();
      }
      
      abstract class Space<S extends Space<S, A>, A extends Atom<S, A>>
          implements ISpace<S, A> {
      
          private final List<IAtom<? extends S, ? extends A>> atoms =
                  new LinkedList<IAtom<? extends S, ? extends A>>();
      
          public Space() {
          }
      
          @Override
          public List<IAtom<? extends S, ? extends A>> getList() {
              return atoms;
          }
      }
      
      abstract class Atom<S extends Space<S, A>, A extends Atom<S, A>>
          implements IAtom<S, A> {
      
          private final S space;
      
          public Atom(S someSpace) {
              this.space = someSpace;
      
      /// THIS WILL NOT WORK WITHOUT THOSE STRANGE LIST TYPE PARAMETERS
              space.getList().add(this);
          }
      
          @Override
          public S getSpace() {
              return space;
          }
      }
      
      class Space1 extends Space<Space1, Atom1> {
          public Space1() {
          }
      }
      
      class Atom1 extends Atom<Space1, Atom1> {
          public Atom1(Space1 someSpace) {
              super(someSpace);
          }
      }
      

      整个事情背后的想法是: 我需要安全地知道其容器类型的元素对象 和容器对象,它们知道它们的元素类型安全。

      【讨论】:

      • 您可能还需要向 Atom 添加一个方法: abstract A getThis();并使用它而不是原始的 this。
      • 正如我所说,没有? extends 是行不通的。而且,A getThis(){ return this; } 也不起作用 :(
      • 是的!找到了解决方案:List&lt;IAtom&lt;? extends S, ? extends A&gt;&gt;
      猜你喜欢
      • 1970-01-01
      • 2023-02-10
      • 2013-05-17
      • 2017-05-09
      • 2021-03-27
      • 1970-01-01
      • 2013-03-26
      • 1970-01-01
      • 2012-04-20
      相关资源
      最近更新 更多