Java中的通用类(类型安全)相互依赖

时间:2009-03-26 11:42:49

标签: java generics types

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

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

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

不好:

“A extends 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>

5 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:3)

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

答案 2 :(得分:1)

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

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

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

答案 3 :(得分:0)

在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

答案 4 :(得分:0)

谢谢你,“Tom Hawtin - tackline”,我收录了你的回答, 它适合。另外,下面的代码演示了(有点奇怪) 必须改变List类型参数(如果需要原子列表)

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

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