MudGuard: Generischer Vektor in abstrakter Klasse ...

Hi,

In einer abstrakten Klasse habe ich folgendes Member samt setter:

class AbstractVectorHolder
{
  protected Vector<?> allData;

public void setData(Vector<?> data)
  {
    allData = data;
  }

//... der Rest, der für's Problem irrelevant ist.
}

Dazu eine Instanz einer abgeleiteten Klasse, diese soll eine Methode zum Hinzufügen eines Elements zum Vektor haben:

class OneVectorHolder extends AbstractVectorHolder
{
//  public void addRow(WasAuchImmer obj)
//  {
//    allData.add(obj);
//  }

//hier noch mehr kram, der für's Problem irrelevant ist
}

Irgendwo (wo spielt keine Rolle für's Problem) wird dann

OneVectorHolder instanz = new OneVectorHolder();
Vector<WasAuchImmer> data = getVectorOfWasAuchImmer();
instanz.setData(data);

gemacht. Klappt einwandfrei.

Leider kommt jedoch, wenn ich die addRow-Methode reinnehme, die Fehlermeldung
The method add(capture-of ?) in the type Vector<capture-of ?> is not applicable for the arguments (WasAuchImmer)
in der Zeile mit dem allData.add(obj).

Ich hab's mit diversen Casts probiert (z.B: allData.add((Object) obj); ), leider bekomme ich das nicht übersetzt.

Das einzige, was funktioniert, ist
((Vector) allData).add(object);
also ein Cast des Vektors.

Vector<?> sollte doch eigentlich ein Vektor beliebigier Objekte sein, insofern verstehe ich nicht, wieso
allData.add(obj); oder allData.add((Object) obj); nicht funktionieren.

Kann mir das jemand erklären?

Gibt es eine Variante, bei der nur das obj gecastet wird, nicht der ganze Vector?
Oder gar eine Variante ganz ohne Cast?

WasAuchImmer ist dabei irgendeine Klasse, spielt keine Rolle.

cu,
Andreas

--
Warum nennt sich Andreas hier MudGuard?
O o ostern ...
Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
  1. Hallo,

    vorweg: Ich bin kein Generics-Experte, sondern habe Generics nur das
    eine oder andere Mal in einfachen Fällen angewendet.

    Dein Problem läßt sich wohl auf Type-Erasure zurückführen. Eigentlich
    macht deine Implementierung keinen so wirklich großen Sinn, wenn du
    mich fragst.

    class AbstractVectorHolder
    {
      protected Vector<?> allData;

    public void setData(Vector<?> data)
      {
        allData = data;
      }

    //... der Rest, der für's Problem irrelevant ist.
    }

    Hier sagst du, daß die Klasse einen typisierten Vector mit unspezifiziertem
    Typ aufnehmen kann. Blöderweise ist der Typ zur Compile-Zeit nicht
    bekannt. Ergo könntest du hier jetzt z.B. keine get-Methode zur
    Klasse hinzufügen, die ein Element aus dem Vector zurückliefert, einfach
    weil der Typ nicht bekannt ist.

    Zur Laufzeit wäre er das natürlich. Aber Generics sind nun mal eben
    genau dafür da, daß sie zur Compile-Zeit sicher sind. Durch einen
    Cast, wie du ihn unten probierst, umgehst du diese Sicherheit natürlich
    wieder. Bei einem Cast verlässt sich der Compiler aber schlichtweg
    auf deine Weisheit und fragt nicht weiter nach. Mit einem Cast geht
    alles. Das aber nur am Rande.

    Mit deiner Klasse hast du die Information über den Typ zur Compile-Zeit
    jedenfalls weggeworfen und wirst ihn auch nicht mehr erhalten können.

    class OneVectorHolder extends AbstractVectorHolder
    {
    //  public void addRow(WasAuchImmer obj)
    //  {
    //    allData.add(obj);
    //  }

    //hier noch mehr kram, der für's Problem irrelevant ist
    }

    Wenn du hier jetzt versuchst ein WasAuchImmer-Objekt in den Vector zu
    schreiben, geht das im Grunde deshalb nicht, weil der Vector keinen
    konkreten Typ hat. Er hat einen unbekannten Typ. Deshalb kannst du auf
    diese Weise auch kein einziges(!) Objekt in den Vector ablegen. Denn
    damit würdest du mit der Typsicherheit zur Compile-Zeit brechen. Da
    der Compiler nicht weiß, welchen Typ der Vector hat, könntest du alles
    mögliche reinstecken, wenn es keinen Compile-Fehler geben würde. Und
    bei der nächsten Abfrage (von außerhalb des AbstractVectorHolder-
    Objekts, s.u.) würde bei falschem Typ (z.B. String) eine ClassCastException
    geworfen werden. Aber das wäre genau das Gegenteil von Compile-Zeit-
    Sicherheit. :)

    Irgendwo (wo spielt keine Rolle für's Problem) wird dann

    OneVectorHolder instanz = new OneVectorHolder();
    Vector<WasAuchImmer> data = getVectorOfWasAuchImmer();
    instanz.setData(data);

    gemacht. Klappt einwandfrei.

    Das geht. Du kannst auch prima sowas wie data.add(new WasAuchImmer())
    aufrufen. In diesem Scope ist nämlich zur Compile-Zeit(!) bekannt, daß
    "data" vom Typ Vector<WasAuchImmer> ist. Im Scope von OneVectorHolder
    wurde der Type durch ? gelöscht und ist (zur Compile-Zeit) nicht mehr
    bekannt.

    Ich hab's mit diversen Casts probiert (z.B: allData.add((Object) obj); ), leider bekomme ich das nicht übersetzt.

    Das einzige, was funktioniert, ist
    ((Vector) allData).add(object);
    also ein Cast des Vektors.

    Ja, weil du damit aus dem Objekt quasi ein Vector<Object>-Objekt machst.
    Da kannst du natürlich alles reinstecken, was von Object abgeleitet ist.
    (Also alles.) Wenn du allerdings z.B. ein new Object() reinstecken
    würdest, würde ein nachfolgender Aufruf wie "allData.get(5)" zu einer
    ClassCastException führen, weil in der (typisierten) get-Methoden eben
    intern einen Type-Cast auf "WasAuchImmer" macht.

    Vector<?> sollte doch eigentlich ein Vektor beliebigier Objekte sein, insofern verstehe ich nicht, wieso
    allData.add(obj); oder allData.add((Object) obj); nicht funktionieren.

    Nein, ein Vector<Object> wäre ein Vector beliebiger Objekte. Blöderweise
    kriegst du dann bei z.B. get() allerdings auch nur einen Object-Typ
    zurück, hast also nur auf die Methoden Zugriff, die in Object definiert
    sind.

    Gibt es eine Variante, bei der nur das obj gecastet wird, nicht der ganze Vector?
    Oder gar eine Variante ganz ohne Cast?

    Was willst du eigentlich genau erreichen?

    Lies dir mal den Generics-Abschnitt aus 'Java ist auch eine Insel' durch.
    Da ist das eigentlich recht gut erklärt. (Zumindest um einen groben
    Eindruck zu erhalten.)

    Würd mich aber tatsächlich interessieren, was du hier vorhattest.
    Vielleicht läßt sich ja gemeinsam eine Lösung finden.

    Gruß
    Slyh

    1. Hi,

      vorweg: Ich bin kein Generics-Experte, sondern habe Generics nur das
      eine oder andere Mal in einfachen Fällen angewendet.

      Dein Problem läßt sich wohl auf Type-Erasure zurückführen. Eigentlich
      macht deine Implementierung keinen so wirklich großen Sinn, wenn du
      mich fragst.

      Naja ...

      Was ich eigentlich mache, sind z.B. abstrakte Klassen, die z.B. von AbstractTableModel oder ähnlichem abgeleitet sind und diese um gemeinsame Funktionalität erweitern. Eine dieser Erweiterungen ist eben, daß sie den Vektor mit den Daten enthält und darauf einige Operationen zur Verfügung stellt.

      Andere Funktionalitäten wie eben das Hinzufügen _eines_ konkreten Datenobjekts (im Beispiel war es WasAuchImmer) zum Vektor, also mein addRow(WasAuchImmmer was), die ich nicht überall brauche (die meisten der Tabellen bekommen komplett neue Daten und bleiben von der Datenmenge her unverändert, bis die Daten komplett ersetzt werden, dort werden nie einzelne Datensätze hinzugefügt), wollte ich jetzt in der instanzierbaren Klasse hinzufügen.

      Lies dir mal den Generics-Abschnitt aus 'Java ist auch eine Insel' durch.

      Werde ich machen.

      Würd mich aber tatsächlich interessieren, was du hier vorhattest.
      Vielleicht läßt sich ja gemeinsam eine Lösung finden.

      Eine der Lösungen wäre vermutlich, den Vektor in der abstrakten Klasse nicht zu typisieren. Oder auf Vector<Object> zu gehen.
      Mal sehen, was ich da mache.

      cu,
      Andreas

      --
      Warum nennt sich Andreas hier MudGuard?
      O o ostern ...
      Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.