Java (обработка 1.5.1): свободный интерфейс @ многоуровневое наследование через дженерики

Я пытаюсь реализовать свободный интерфейс в своем движке 2D-игры.

Упрощенный пример моей реализации:

public class Sprite<T> {

    protected float x = 0.0;
    protected float y = 0.0;

    public T setPosition(float x, float y) {
        this.x = x;
        this.y = y;
        return (T)this;
    }

}

public class Living<T extends Living> extends Sprite<Living> {

    protected boolean alive = false;

    public T setAlive(boolean alive) {
        this.alive = alive;
        return (T)this;
    }

}

public class Entity<T extends Entity> extends Living<Entity> {

    protected String name = null;

    public T setName(String name) {
        this.name = name;
        return (T)this;
    }

}


Entity entity = new Entity().setPosition(100, 200).setAlive(true).setName("Zombie");

Я продолжаю получать сообщение об ошибке: "Функция setAlive(boolean) не существует."

Я знаю, используя мои методы наоборот (в более логическом порядке) работает:

Entity entity = new Entity().setName("Zombie").setAlive(true).setPosition(100, 200);

И я знаю, что сработает перезапись любых родительских сеттер-функций в каждом дочернем классе:

public class Entity extends Living {

    protected String name = null;

    public Entity setPosition(float x, float y) {
        return (Entity)super.setPosition(x, y);
    }

    public Entity setAlive(boolean alive) {
        return (Entity)super.setAlive(alive);
    }

    public Entity setName(String name) {
        return (Entity)super.setName(name);
    }

}

Но я хочу, чтобы интерфейс был максимально бесплатным/несложным для "конечного пользователя", а код был максимально компактен. и чистить по мере необходимости.

Я не знаю, то ли я просто испортил дженерики, то ли мой подход к дырам совершенно неверен. Я надеюсь, что вы можете помочь. Я открыт для любых советов. (Извините за мой плохой английский.)

Изменить: я уже тестировал следующий подход, и он работает для класса Entity.

public class Sprite<T> {
...
}

public class Living<T> extends Sprite<T> {
...
}

public class Entity extends Living<Entity> {
...
}

Я забыл упомянуть, что мне также нужно создать экземпляр Sprite & Living. Например:

Living living = new Living().setPosition(50, 50).setAlive(false);

person Poersch    schedule 11.12.2012    source источник


Ответы (2)


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

public class Sprite<T> {
...
}

public class Living<T> extends Sprite<T> {
...
}

public class Entity extends Living<Entity> {
...
}
person hoaz    schedule 11.12.2012
comment
Спасибо за быстрый ответ. Работает для организации. Но я должен был упомянуть, что мне также нужно создать экземпляр Sprite & Living. Если я реализую ваш подход, я получаю ту же ошибку, что и выше, при создании экземпляра Living следующим образом: Living living = new Living().setPosition(50, 50).setAlive(false); - person Poersch; 11.12.2012
comment
ты можешь сделать new Living<Living>(); - person hoaz; 11.12.2012
comment
Если я проверю это: Living living = new Living<Living>().setPosition(100, 200).setAlive(true);, я получу: невозможно преобразовать из объекта в жизнь - person Poersch; 11.12.2012
comment
Я думаю, вам нужно иметь отдельный класс для конкретного типа Living, например ConcreteLiving extends Living<Living> для каждого типа, который вы собираетесь создавать. - person hoaz; 11.12.2012
comment
Не то, что я хотел услышать, но, безусловно, правильный ответ. Большое тебе спасибо. :) - person Poersch; 11.12.2012

Это доблестная попытка использовать странно повторяющийся шаблон шаблона в Java. Проблема в том, что вы смешиваете дженерики и необработанные типы, что означает, что вы не «замыкаете цикл» шаблона. Например, ваше объявление Living:

public class Living<T extends Living> extends Sprite<Living>

Должно быть действительно:

public class Living<T extends Living<T>> extends Sprite<T>

В какой-то момент вам нужно будет объявить класс-лист, который разрешает T, иначе вы не сможете создавать экземпляры и объявлять переменные этих типов, не прибегая к необработанным типам или подстановочным знакам (что противоречит цели шаблона). Например:

public final class ConcreteEntity extends Entity<ConcreteEntity>

См. мой ответ здесь для получения более подробной информации о реализации этого шаблона.

person Paul Bellora    schedule 11.12.2012
comment
Спасибо за ответ. Я уже пробовал этот подход. Но я забыл упомянуть, что мне также нужно создать экземпляр Sprite & Living. - person Poersch; 11.12.2012
comment
Это невозможно. Вам нужно создать дерево наследования, в котором можно создавать экземпляры только листьев. Хотя это должно быть тривиально. Это то, что я сделал в решении, которое я упомянул в своем связанном ответе, - где обычно я бы использовал базовый класс напрямую, вместо этого я создал тривиальный ответвленный класс, чтобы разрешить параметр рекурсивного типа. - person Paul Bellora; 11.12.2012
comment
Похоже, мне нужно добавить еще несколько классов. Большое тебе спасибо. :) - person Poersch; 11.12.2012