Общие классы в Java (типобезопасно) в зависимости друг от друга - PullRequest
1 голос
/ 26 марта 2009

Я хочу создать абстрактный класс коллекции (называемый Space) и класс абстрактных элементов (называемый Atom). Экземпляры обоих должны знать друг друга (точно набранные). В этом проблема.

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

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

Не хорошо:

«А расширяет атом» означает любой атом, но не строго типизированный

«S расширяет пробел» означает любой пробел, но не строго типизированный

Я не могу достичь полной безопасности типов при следующих попытках:

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 ]

4 голосов
/ 26 марта 2009

Это работает для меня, хотя я действительно запутался во всех этих общих ограничениях. Это означает, что я не могу гарантировать, что он делает то, что должен:

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);
        }
}
3 голосов
/ 26 марта 2009
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>> {}
1 голос
/ 26 марта 2009

Я бы не стал пытаться решить эту проблему с помощью дженериков. Вместо этого предоставьте Фабрику, которая всегда создает соответствующие объекты.

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

Затем вы можете выполнить проверку типа в ваших реализациях, чтобы убедиться, что используются только соответствующие объекты.

0 голосов
/ 26 марта 2009

Спасибо, "Том Хотин - 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);
    }
}

Идея, лежащая в основе всего этого: Мне нужны элементы-элементы, которые знают свой тип контейнера безопасно и контейнерные объекты, которые знают свои элементы тип-безопасно.

0 голосов
/ 26 марта 2009

Единственный способ заставить параметризованные типы в 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

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