2

I'm sure this has been answered 100 times but I'm not sure what to search for. I want to create an interface with an abstract method that enforces a parameter of self-type of the class that implements it.

Whew, that's a mouthful.

For example, I have an interface called Collider, with a method called isColliding. I have a class called Player that implements Collider. In my use case, only objects of the same sub-type of Collider will need to check if they're colliding with each other. I want Player to implement the method isColliding, but I want the function prototype to be enforced as isColliding(Player p), as opposed to isColliding(Collider c)

I've managed to implement what I'd consider a work-around, by declaring Collider as

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

But when I declare my class that implements collider, the class prototype looks like this

public class Player implements Collider<Player>

This seems ugly and not necessarily as type-safe as I'd like. It seems like there should be a way to make that <Player> implied. My goal is to have the child class's Overridden function prototype look like this

public boolean isColliding(Player other)

Thanks in advance!

edit:

To give some more background:

I have a singleton class called Collision, which registers my objects that may collide with each other. Inside Collision I have a Hashmap declared as

HashMap<Class, ArrayList<Collider>> colliders

This is my data structure for storing the objects that may collide with each other. They are mapped by class, because my design only requires each class to check if it is colliding with itself. Collision has its own isColliding function to be called from within my objects that implement Collider. It looks like this

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

I'm getting NoSuchMethod errors as I try to call isColliding in this method, and I suspect it's because the self-typeness isn't implemented. Should I re-think my design? Are there patterns that would make this cleaner?

edit 2:

I managed to get past the error my casting 'otherC' as type (T) in the call to isColliding. It seems that this implementation will work for me. Thanks everyone for your help!

Michael
  • 318
  • 3
  • 14
  • The classic example of something like this is java.lang.Comparable. Have you considered copying its structure? – Patricia Shanahan Jan 31 '16 at 18:47
  • I think that's what I already have. I was hoping there was another feature of Java I'm overlooking to figure out the self-type for me, instead of myself explicitly providing it in every inherited class – Michael Jan 31 '16 at 18:50
  • It's imprecise and redundant. That's why I think it's probably not the best solution. It's my work-around for enforcing the self-type in the isColliding method. It's not exactly what I want but it's more specific than allowing any generic type. – Michael Jan 31 '16 at 18:59
  • Self-typing in generic types can become a source of difficulty. If it's what you really need, then that's that, but sometimes there are elegant solutions that avoid self-typing. – scottb Jan 31 '16 at 19:03
  • What would that entail? What are the details of its difficulty? – Michael Jan 31 '16 at 19:08
  • can you add a method that populates the map? – AdamSkywalker Jan 31 '16 at 19:37
  • If you use generics for this you won't get any actual "enforcement" that the `isColliding()` method is being called with the right type. The compiler can't tell that you're invoking the right method on the right object since you are invoking from 'Collision', and it won't know until runtime what type it has. And you can't check at runtime because by then your generics will have been erased. You need a runtime type check, as much as it pains me to say it. – Erick G. Hagstrom Jan 31 '16 at 21:50

5 Answers5

3

This design looks strange: it is enough to declare Collider with a single T type:

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

In this case implementation classes check collision with objects of the same type:

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

This works much the same as Comparable interface, for example.

Declaration <T extends Collider<T>> is stronger and ensures that implementation class can specify only Collider subtypes as generic type:

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

It still does not demand to use the same type - class Person1 implements Collider<Person2> - legal

AdamSkywalker
  • 11,408
  • 3
  • 38
  • 76
3

It seems like you are trying to create some kind of game or game engine, right? in this case I also guess that at some point you will like to detect collisions not only between Persons but also between other things (eg Person,Monster,Vehicle etc).

You possibly need to re-think your design a bit and not go into the self-types.

First I would suggest you to make all the objects that may collide subclasses of a common type which exposes the properties which you can use to detect collision (it may be position,polygon etc).

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

The collision detection logic does not belong to the objects (Person,Monster etc) but on another part of the program.The object it self does not need to know that it can collide with something.

Since you expressed the desire to use different collision detection methods you can use a collision detection policy and create various instances which you can use.

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

the main engine of your game should check for collisions when appropriate.

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

NOTE: I don't find it wise the object itself to provide the detection policy because in the case 2 objects provide different policy the comparison semantics will be a bit weird as in the following case:

lets assume that the object itself could provide us its preferred detection policy using method

public CollidingDetectionPolicy getDetectionPolicy();

and we had to check these two objects

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

then

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

could have different result than

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

which would be weird to say the least.

ntsakonas
  • 46
  • 1
  • 1
    Thank you for the thorough demonstration of writing a collision detection engine. I'm actually creating a simulation, and my use cases are much simpler than a typical video game. There is a lot of great info in here and I will definitely be referring back to it for the patterns you mentioned if I ever do need a more robust collision engine. – Michael Feb 01 '16 at 16:24
1

I think the best approach is the one that you showed

However the other way may be to define this in the Collider interface

public boolean isColliding(Object object);

and then implement the method in this way

class Player implements Collider{

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

}

I prefer the use of template as you've done, so the method has a fixed type.

Andrea Catania
  • 1,361
  • 3
  • 21
  • 37
  • This is a solution, perhaps, but hardly a good one. It lacks any of the type safety guarantees that a generic type would help the programmer to achieve. In general, the use of `instanceof` in types that you author yourself (or can modify) indicates that there may be a problem. – scottb Jan 31 '16 at 19:04
  • As i've written I don't like this solution because I like the use of template... I have provided a possible solution.. All away the use of generic type is not a problem and it's not underline by the use "instanceof" that is a keyword implemented in Java for this reason... Sorry but I'm not agree with you – Andrea Catania Jan 31 '16 at 19:16
  • You are free to disagree, however best practices are what they are for a reason. The code you have suggested is not type safe. Nor is it as maintainable or robust as it could be if best practices were employed. For example, you have moved error discovery from compile-time (as it would have been with a generic type) to run-time, which can make errors more difficult to identify. To Java learners I would simply say that the above, which may be a perfectly reasonable in-house implementation, is not a style to be emulated. – scottb Jan 31 '16 at 20:35
  • Actually, the "other way" described here provides runtime type safety that can't be achieved with a generics-only approach. And generics don't help at compile time _in this particular case_ because `Collision` doesn't know who it's invoking at compile time. – Erick G. Hagstrom Jan 31 '16 at 22:01
0

Maybe I'm misunderstanding, but I assume the method of comparison is the same for each type of object since you don't want to override the method for each class and what actually matters is that only objects of the same type are compared. Then buildng on Andrea's answer you could perhaps use:

class Collider{

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

}

However I think your generic answer is ok style, afterall it is the pattern that java uses for Comparable.

PNS
  • 750
  • 1
  • 5
  • 19
  • I do want to override the method for each class. I intend to have each of my subclasses implement their own collision detection algorithms. Some will be spherical, some will be polygonal. Many have interesting shapes and will have to have their own solutions. – Michael Jan 31 '16 at 19:27
  • Ok, then I definitely think your original design is the best! – PNS Jan 31 '16 at 19:43
0

The interface Comparable can be a guide. As AdamSkywalker mentioned in his answer,

public interface Collider<T>

is good enough. The bound provides you no benefit here.

In any method or class where you generalize on collider type, you can use the bound

<T extends Collider<? super T>>

to ensure that that type can check for collision with itself.

For your Map, there is in general no way in Java to declare a Map variable that maps a Class to elements of that class in a type-checked way (regardless of whether you have restrictions on the classes allowed or not), because Map's methods have parameter types that are only related to the type arguments on the whole Map, so they must be the same for all elements in the Map.

However, you can write a wrapper class whose API enforces this relationship, something like,

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

Such a class would still have to store a variable of some type like Map<Class<?>, List<?>> internally and do unchecked casts internally, but that is all contained to the private implementation details of the class, which can be checked to guarantee it's safe.

newacct
  • 119,665
  • 29
  • 163
  • 224