2016-01-31 2 views
2

Я уверен, что на это ответили 100 раз, но я не уверен, что искать. Я хочу создать интерфейс с абстрактным методом, который применяет параметр self-type класса, который его реализует.Как создать интерфейс с абстрактными методами, которые ссылаются на self-type в java

Мусор, это глоток.

Например, у меня есть интерфейс под названием Collider, с методом isColliding. У меня есть класс под названием Player, который реализует Collider. В моем случае использования только объекты одного и того же подтипа Collider должны будут проверить, столкнулись ли они друг с другом. Я хочу, чтобы Player реализовал метод isColliding, но я хочу, чтобы прототип функции выполнялся как isColliding (Player p), а не isColliding (Collider c)

Мне удалось реализовать то, что я считаю работой -around, объявив коллайдер, как

public interface Collider<T extends Collider<T>> { 
    public abstract boolean isColliding(T other); 
} 

Но когда я объявляю свой класс, который реализует коллайдер, класс прототип выглядит следующим образом

public class Player implements Collider<Player> 

Это кажется уродливым и не обязательно, так как Типобезопасный, как я d нравится. Похоже, должен быть способ сделать это <. Игрок > подразумевается. Моя цель состоит в том, чтобы прототип функции Overridden для дочернего класса выглядел следующим образом:

public boolean isColliding(Player other) 

Заранее благодарен!

редактировать:

Чтобы дать немного больше фона:

У меня есть одноэлементный класс под названием Collision, которая регистрирует мои объекты, которые могут конфликтовать друг с другом. Внутри Столкновение у меня есть HashMap объявлен

HashMap<Class, ArrayList<Collider>> colliders 

Это моя структура данных для хранения объектов, которые могут конфликтовать друг с другом. Они отображаются по классу, потому что мой дизайн требует, чтобы каждый класс проверял, сталкивается ли он с самим собой. У Collision есть своя функция isColliding, которая вызывается из моих объектов, которые реализуют Collider. Похоже, что этот

public <T extends Collider> boolean isColliding(T c) throws ColliderNotPopulatedException { 
    ArrayList<T> cList = (ArrayList<T>)colliders.get(c.getClass()); 

    if (cList == null) { 
     throw new ColliderNotPopulatedException(); 
    } 

    for (T otherC : cList) { 
     if (c != otherC && c.isColliding(otherC)) { 
      return true; 
     } 
    } 
    return false; 
} 

я получаю ошибки NoSuchMethod, как я пытаюсь вызвать isColliding в этом методе, и я подозреваю, что это потому, что сам-typeness не реализован. Должен ли я передумать мой дизайн? Есть ли узоры, которые сделают это чище?

редактировать 2:

мне удалось пройти ошибку мой кастинг 'otherC' как тип (T) в вызове isColliding. Похоже, эта реализация будет работать для меня. Спасибо всем за вашу помощь!

+0

Классический пример чего-то вроде java.lang.Comparable. Вы считали, что копируете его структуру? –

+0

Я думаю, что это то, что у меня уже есть. Я надеялся, что есть еще одна особенность Java, которую я не замечаю для себя, для себя, вместо того, чтобы я явно предоставлял ее в каждом унаследованном классе. – Michael

+0

Это неточно и избыточно. Вот почему я думаю, что это, вероятно, не лучшее решение. Это моя работа для обеспечения самонастройки в методе isColliding. Это не совсем то, что я хочу, но это более конкретный, чем разрешить любой общий тип. – Michael

ответ

2

Кажется, вы пытаетесь создать какую-то игру или игровой движок, не так ли? в этом случае я также предполагаю, что в какой-то момент вам понравится обнаруживать столкновения не только между людьми, но и между другими вещами (например, Person, Monster, Vehicle и т. д.).

Возможно, вам нужно немного подумать о своем дизайне и не входить в самостоятельные типы.

Сначала я предлагаю вам создать все объекты, которые могут сталкиваться с подклассами общего типа, который предоставляет свойства, которые вы можете использовать для обнаружения столкновения (это может быть положение, многоугольник и т. Д.). Логика обнаружения

public interface GameObejct { 

     int getProperty1(); 
     int getProperty2(); 
    } 

public class Person implements GameObejct { 
    @Override 
    public int getProperty1() { 
     return 0; 
    } 

    @Override 
    public int getProperty2() { 
     return 0; 
    } 
} 

public class Monster implements GameObejct{ 
    @Override 
    public int getProperty1() { 
     return 0; 
    } 

    @Override 
    public int getProperty2() { 
     return 0; 
    } 
} 

Столкновение не относится к объектам (Person, монстр и т.д.), а на другой части объекта program.The оно само не нужно знать, что он может столкнуться с чем-то.

Поскольку вы выразили желание использовать различные методы обнаружения столкновений, вы можете использовать политику обнаружения столкновений и создавать различные экземпляры, которые вы можете использовать.

public interface CollidingDetectionPolicy <T extends GameObejct> { 

    boolean objectsColliding(T object1,T object2); 

} 

public class SimpleCollisionDetector implements CollidingDetectionPolicy<GameObejct> { 
    @Override 
    public boolean objectsColliding(GameObejct object1, GameObejct object2) { 
     // detect collision using a simple method... 
     return false; 
    } 
} 

public class ComplexCollisionDetector implements CollidingDetectionPolicy<GameObejct> { 
    @Override 
    public boolean objectsColliding(GameObejct object1, GameObejct object2) { 
     // detect collision using some other more complex method 
     return false; 
    } 
} 

Главный двигатель вашей игры должен проверять на наличие столкновений, если это необходимо.

public class GameEngine { 

    public void detectCollisions() { 

     /// ... 
     /// ... 
     // need to know if there is some collision 

     // get all the objects on screen and the current collision detection policy (see note) 
     List<GameObejct> currentlyVisibleObjects = getObjectsOnScreen(); 
     CollidingDetectionPolicy collisionDetector = getCollisionDetectionLogic(); 

     // naive implementation . don't traverse your list this way!Think about complexity! 
     for (int i = 0; i < currentlyVisibleObjects.size() - 1; i++) { 
      GameObejct object1 = currentlyVisibleObjects.get(i); 

      for (int j = i + 1; j < currentlyVisibleObjects.size(); j++) { 
       GameObejct object2 = currentlyVisibleObjects.get(j); 
       if (collisionDetector.objectsColliding(object1, object2)) { 
        // object colliding ...do something 
       } 
      } 
     } 

    } 

    public List<GameObejct> getObjectsOnScreen() { 
     ... // return the list of game objects 
    } 

    public CollidingDetectionPolicy getCollisionDetectionLogic() { 
     ... /// return the detection implementation 
    } 
} 

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

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

public CollidingDetectionPolicy getDetectionPolicy(); 

, и мы должны были проверить эти два объекта

Person p = ...; 
Monster m = ..; 

затем

p.getDetectionPolicy().objectsColliding(p, m) 

может иметь другой результат, чем

m.getDetectionPolicy().objectsColliding(p, m) 

который бы странно, мягко говоря.

+1

Благодарим вас за тщательную демонстрацию написания механизма обнаружения столкновений. Я фактически создаю симуляцию, и мои варианты использования намного проще, чем обычная видеоигра. Здесь много интересной информации, и я обязательно вернусь к ней за шаблонами, которые вы упомянули, если мне когда-нибудь понадобится более надежный механизм столкновения. – Michael

1

Я думаю, что лучший подход является тот, который вы показали

Однако другой способ может быть, чтобы определить это в интерфейсе коллайдер

public boolean isColliding(Object object); 

, а затем реализовать метод таким образом

class Player implements Collider{ 

    public boolean isColliding(Object object){ 
     if(object instanceof Player){ 
      // some stuff 
      return true; 
     } 
     return false; 
    } 

} 

Я предпочитаю использовать шаблон, как вы это сделали, поэтому метод имеет фиксированный тип.

+0

Это решение, возможно, но вряд ли хорошее. В нем отсутствуют какие-либо гарантии безопасности типа, которые общий тип поможет программисту достичь. В общем случае использование 'instanceof' в типах, которые вы создаете самостоятельно (или можете изменять), указывает на то, что может возникнуть проблема. – scottb

+0

Как я написал, мне не нравится это решение, потому что мне нравится использование шаблона ... Я предоставил возможное решение .. Все, использование универсального типа не является проблемой, и это не подчеркивается использованием " instanceof ", который является ключевым словом, реализованным на Java по этой причине ... Извините, но я не согласен с вами. –

+0

Вы можете не соглашаться, однако лучшие практики - это то, что они по какой-то причине. Код, который вы предложили, не является безопасным по типу. Он также не может быть устойчивым или надежным, как это могло бы быть, если бы использовались лучшие практики. Например, вы переместили обнаружение ошибок из времени компиляции (как и с общим типом) во время выполнения, что может затруднить идентификацию ошибок. Для Java-учащихся я бы просто сказал, что вышеизложенное, которое может быть вполне разумной внутренней реализацией, - это не стиль, который нужно эмулировать. – scottb

3

Эта конструкция выглядит странно: достаточно объявить Collider с одним T типа:

interface Collider<T> { 
    boolean isColliding(T other); 
} 

В этом случае классы реализации проверки столкновения с объектами одного и того же типа:

class Person implements Collider<Person> { 
    @Override 
    public boolean isColliding(Person otherPerson) { 
     ... 
    } 
} 

Этот работает так же, как и интерфейс Comparable.

Декларация <T extends Collider<T>> сильнее и гарантирует, что класс реализации может указывать только коллайдер подтипы, как общий тип:

class Person implements Collider<Person> - this will work 
class Person implements Collider<String> - this will not work 

Он по-прежнему не требует, чтобы использовать один и тот же тип - class Person1 implements Collider<Person2> - юридическое

0

Может быть, я но я предполагаю, что метод сравнения одинаковый для каждого типа объектов, так как вы не хотите переопределять метод для каждого класса, и на самом деле имеет значение только то, что сравниваются только объекты одного типа. Тогда buildng на ответ Андреа вы могли возможно использовать:

class Collider{ 

    public boolean isColliding(Object o){ 
     if (this.getClass() == o.getClass()){ 
      //Do some stuff 
      return true; 
     } 
     return false; 
    } 

} 

Однако я думаю, что ваш общий ответ одобрен стиль, Afterall это шаблон, который Java использует для Сопоставимого.

+0

Я хочу переопределить метод для каждого класса. Я намереваюсь, чтобы каждый из моих подклассов реализовал свои собственные алгоритмы обнаружения столкновений. Некоторые будут сферическими, некоторые будут многоугольными. Многие из них имеют интересные формы и должны иметь свои собственные решения. – Michael

+0

Хорошо, тогда я определенно думаю, что ваш оригинальный дизайн - лучший! – PNS

0

Интерфейс Comparable может быть руководством. Как упоминал в своем ответе AdamSkywalker,

public interface Collider<T> 

достаточно. Эта привязка не дает вам никакой пользы.

В любом методе или классе, где обобщается на тип коллайдера, вы можете использовать связанный

<T extends Collider<? super T>> 

, чтобы гарантировать, что этот тип может проверять столкновения с самим собой.

Для вашего Map, нет вообще никакого пути в Java, чтобы объявить Map переменные, которая сопоставляет Class к элементам этого класса в пути типа проверяется (независимо от того, есть ли у вас ограничение на классы разрешены или нет) , потому что методы Map имеют типы параметров, которые относятся только к аргументам типа в целом Map, поэтому они должны быть одинаковыми для всех элементов в Map.

Однако, вы можете написать класс-оболочка которого API применяет эти отношения, что-то подобное,

class ClassToColliderList { 
    public <T extends Collider<? extends T>> void put(Class<T> clazz, List<T> list); 
    public <T extends Collider<? extends T>> List<T> get(Class<T> clazz); 
} 

Такой класс все равно пришлось бы хранить переменную некоторого типа, как Map<Class<?>, List<?>> внутренне и сделать непроверенные слепки внутри, но все это содержится в частных деталях реализации класса, которые можно проверить, чтобы гарантировать его безопасность.

Смежные вопросы