(Down-)Casting Problem

temi

Top Contributor
Hallo zusammen, folgendes Problem:
Ich experimentiere gerade in Richtung "Entity Component System", dabei soll gelten, dass ein Entity nur eine ID ist und sonst nichts. In meinem Fall ist es eine UUID. Um den internen Typen der ID zu abstrahieren, habe ich eine Klasse "Entity" erstellt:
Java:
public class Entity
{
    private final UUID id;

    public Entity(final UUID id)
    {
        Objects.requireNonNull(id);

        this.id = id;
    }

    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Entity entity = (Entity) o;

        return id.equals(entity.id);
    }

    @Override
    public int hashCode()
    {
        return id.hashCode();
    }
}

Da alle Spiel-Objekte "Entities" sind kann es z.B. für ein Inventory zu solchem Code kommen:
Java:
Map<Entity, List<Entity>> inventoryForPlayer;

Das ist etwas undurchsichtig, darum hatte ich so etwas vor, hier exemplarisch für "Player":
Java:
public final class Player extends Entity
{
    public Player(final UUID id)
    {
        super(id);
    }
}

Eigentlich ist das nur ein anderer Name für den selben Inhalt. Schade das es in Java kein using Player = Entity; gibt, wie bei C++.

Damit würde das Beispiel von oben zu folgendem:
Java:
Map<Player, List<Item>> inventoryForPlayer;


Entities sollen von einem EntityManager erzeugt und verwaltet werden (hier noch sehr rudimentär) und hier kommt nun das Problem. Der EntityManager soll natürlich ein Objekt vom benötigten Entity-Typen erzeugen und nicht ein allgemeines Entity:
Java:
public final class EntityManager
{
    public <T extends Entity> T createEntity()
    {
        final T entity = (T) new Entity(UUID.randomUUID());

        return entity;
    }
}

Der Cast schlägt allerdings fehl, vermutlich weil es ein Downcast zur Childklasse ist.

Lässt sich das Problem irgendwie lösen?
 
Zuletzt bearbeitet:
Ok, ich habe eine funktionierende Lösung gefunden:
Java:
public final class EntityManager
{
    public <T extends Entity> T createEntity(Class<T> clazz)
    {
        try
        {
            final T entity = clazz.getDeclaredConstructor(UUID.class).newInstance(UUID.randomUUID());
            return entity;
        }
        catch (ReflectiveOperationException e)
        {
            System.err.println(e);
        }

        return null;
    }
}

Leider ist ein zusätzlicher Parameter für den Aufruf erforderlich, aber das sollte zu verschmerzen sein. Ich werde sehen, ob ich so weiter machen kann...
 
Könntest du das reflection-Gedöns nicht mit einer static-Method in der Entity-Klasse vermeiden?
Java:
public static Entity create() {
    return new Entity(UUID.randomUUID());
}
 
Könntest du das reflection-Gedöns nicht mit einer static-Method in der Entity-Klasse vermeiden?
Java:
public static Entity create() {
    return new Entity(UUID.randomUUID());
}
Nicht in der Entity-Klasse, denn dann würde ja nur der Basistyp erzeugt, aber mit einer statischen Methode in jeder abgeleiteten Klasse. Hm...

Was würde mir das bringen? Sicherheit? Geschwindigkeit?

Ernst gemeinte Fragen. Danke für den Vorschlag.
 
Um den Class-Parameter wirst du nicht drum rum kommen, wenn du alle mit dieser Methode erzeugen willst.

Anderer Weg wäre, das es für jeden Typ eine Factory gibt und diese sich beim Manager registrieren, dann braucht man keine Reflection mehr.
 
Anderer Weg wäre, das es für jeden Typ eine Factory gibt und diese sich beim Manager registrieren, dann braucht man keine Reflection mehr.
Ich werde mir die Möglichkeit mal im Hinterkopf behalten, es aber jetzt erst mal so lassen wie es ist. Danke für die Idee.

Aktuell bin ich noch nicht so sicher, ob mir die spezialisierten Entity-Klassen, die es ja "nur" für die Lesbarkeit gibt, nicht an anderer Stelle zusätzliche Probleme machen.
 

Zurück
Oben