Problem mit generics -> ClassCastException und ka wieso

Status
Nicht offen für weitere Antworten.

SebiB90

Top Contributor
also ich hab eine Klasse Task mit generics in der die methode excute() gibt
Code:
public abstract class Task<E> {
  //...

  public abstract boolean excute(E... arguments);

  //...
}
dann habe ich eine unterklasse Open in der ich dann das Generic setze(oder wie das heißt)
Code:
public class Open extends Task<File> { // <--- Zeile 12
  //...
  public boolean excute(File... openFile) {
    //...
  }
  //...
}
also habe ich jetzt eine methode mit excute(File...)
wenn ich jetzt die methode aufrufe in einer anderen methode, parameter task ist ein Object von Open:
Code:
public void excuteTaskInBackground(final Task task, final Object... arguments) {
        //aufruf von excute
        if (pTask.excute())
        //oder so, fehler kommt bei beiden versionen
        if(pTask.excute(arguments))
    }
bekomm ich eine ClassCastException:
Code:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object;
	at icqhistoryviewer.tasks.Open.excute(Open.java:12)
	at icqhistoryviewer.tasks.TaskManager.excuteTaskInBackground(TaskManager.java:143)
	at icqhistoryviewer.tasks.TaskManager.excuteTaskInBackground(TaskManager.java:129)
	at icqhistoryviewer.JICQHistoryViewer.main(JICQHistoryViewer.java:67)
aber ich hab gar keine ahnung wieso. es ist auch egal ob ich die methode mit oder ohne argumenten aufrufe.
und das komisch die zeile 12(oben gekennzeichnet im code) ist die klassendefinition und nicht die methode excute()
 
... ist jetzt n schnellschuss desswegen sorry falls es nicht stimmen sollte... aber ich meine mich daran zu erinnern dass man Generics nicht in vererbungshierarchien verwenden kann?!
 
motschow hat gesagt.:
wofür verwendest du die Klasse Open?
also in der excute methode von Open, wird eine datei geöffnet, entweder per JFileChooser wenn kein argument übergen wurde, wenn ein File übergeben wurde dann diese datei. aber für was musst du das wissen?
 
Wenn man schon Generics benutzt, dann gleich überall:
Code:
public <T> void excuteTaskInBackground(final Task<T> task, final T... arguments) {

Und wieso es bei dir nicht läuft? Weil du ein Object[] einem File[] übergeben willst, was halt nicht passt.
 
ja aber excuteTaskInBackground in der Klasse TaskManager und ich wollte nur einen für alle haben und ich hab noch andere tasks die z.b. von Task<ICQUser> erben, also mit verschiedenen generics.
gibt es dafür auch ne lösung?

und was soll eigentlich public <T> void excuteTaskInBackground sein? wenn <T> schon der Typ ist wieso dann noch void? oder ist <T> hier was anderes?
 
Na rat mal: "class Bla<T>" <- hier ist das <T> ein Generic. Was könnte das <T> wohl bei "public <T> void.." sein? :wink:

P.S. es fängt mit "G" an, und hört mit "eneric" auf.

Das T wird bei jedem Methodenaufruf "neu gesetzt", also funktioniert auch mit Task<Hubusch> und Task<Quak> ohne Probleme.
 
ist klar das <T> generic ist aber was soll es da zu suchen haben? den der rückgabe typ ist void also keine und zwischen public und rückgabe typ steht doch nur noch static final etc.

und wieso wird der generic neu gesetzt? und wo geschieht das?

sorry,das ich jetzt vllt so blöd frage, aber ich raff es einfach nicht.
 
Die beiden Argumente von "excuteTaskInBackground" benötigen Generics (ansonsten macht eine Klasse "Task<E>" keinen Sinn - wenn mans nicht anwendet, kann mans gleich lassen).

So ein Generic kann man auf Klassenebene, oder auf Methodenebene definieren. Bei der Klasse Task<E> ist E auf Klassenebene definiert. Ich habe jetzt die Variante für Methodenebene aufgeschrieben.

Die Generics auf Methodenebene hängen von den Argumenten ab, welche man ihnen übergibt. Du kannst "excuteTaskInBackground" also z.B. so aufrufen:
Code:
excuteTaskInBackground( new Open(), new File[]{ new File( "a.txt" )});
oder
Code:
excuteTaskInBackground( new StringTask(), new String[]{ "aba", "bab" })
.... und zwar unabhängig von den anderen Generics die in der Klasse rumschwirren, welche "excuteTaskInBackground" anbietet.

Achja... da fällt mir noch ein: die "..." werden auch bei Generics wohl immer in ein Object[] abgebildet, hm, das könnte Probleme bereiten. Vielleicht wäre nur ein einzelnes Argument (welches halt auch ein Array sein kann) besser.
 
So, mal getestet: mein erster Vorschlag führt definitiv ins Verderben. Aber sowas ist möglich:
Code:
public abstract class Test<E>{
    public static void main( String[] args ) {
        test( new Test<String>(){
            @Override
            public String work( String arg ) {
                StringBuilder builder = new StringBuilder( arg );
                builder.reverse();
                return builder.toString();
            }
        }, "Blupp" );
        
        test( new Test<String[]>(){
            @Override
            public String work( String[] array ) {
                StringBuilder builder = new StringBuilder();
                for( String s : array )
                    builder.append( s );
                return builder.toString();
            }
        }, new String[]{ "a", "b", "c" });
    } 
    
    // Das Task-execute
    public abstract String work( E e );

    // Irgendwas mit so einem Task machen
    public static <T> void test( Test<T> task, T arg ){
        System.out.println( task.work( arg ));
    }
}

Die Begründung wieso das andere nicht funktioniert: deine Klasse "Open" definiert ausdrücklich, dass sie ein "File[]" bekommt (ein "File..." ist ja fast dasselbe). Aber der Aufruf von "execute" erfolgt immer mit einem "Object[]". Auch die Generics nützen nichts, da sie vom Compiler in Object übersetzt werden (Open ist davon ausgenommen, denn das "File" steht explizit im Code - nicht als Generic).
 
Danke 😀 wusst nicht das man nur auf methode auch generics beziehen kann ich dachte immer nur auf ganze Klassen

ich habe jetzt nur ein kleines Problem noch, ich speicher in einer HashMap häufige tasks ab damit ich die per string key ansprechen kann und dazu habe ich dann diese methode

Code:
public void excuteTaskInBackground(String taskName, Object... arguments) {
    Task task = getTask(taskName);

    if (task == null) {
      throw new NullPointerException("Kein Task mit dem Name: " + taskName);
    }

    excuteTaskInBackground(task, arguments);
  }
nur wie krieg ich da jetzt den generic mit rein?
also ich habe mir gedacht so:
Code:
public <T> void excuteTaskInBackground(String taskName, T... arguments) {
    Task task = getTask(taskName);

    if (task == null) {
      throw new NullPointerException("Kein Task mit dem Name: " + taskName);
    }

    excuteTaskInBackground(task, arguments);
  }
nur das problem, wenn ich kein argument übergebe, ist T wieder Object und weiter oben habe ich geschrieben das Open sowohl mit als auch ohne parameter aufgerufen werden soll(von FileChooser öffnen oder übergebene Datei).
wie krieg ich also hier das generic noch rein?
 
Zwar kann eine Map auch Elemente mit Generics haben, aber das funktioniert dann wie mit den Klassen: wenn eine Map "Integer" und "String" speichert, ist ihr generischer Parameter leider Object, denn ansonsten passen nicht beiden Typen rein.

Wenn du die Map verwenden willst führt IMHO kein Weg an einem Cast vorbei (z.B. von "Task<Object>" auf "Task<File>").

Eine Lösung wäre mehrere Maps zu verwenden, für jeden Typ Task (abhängig von dem generischen Parameter) eine Map.

Falls dus überlesen hast, "T...arguments" wird nicht klappen. Arrays + Generics = Kadabum.
 
@beni.

ne ?!

Code:
public class Test<T> {
	public void excuteTaskInBackground(final Test< T > task, final T... arguments) {
		System.out.println(task.toString());
		System.out.println(arguments[0]);
	}

	public static void main(String[] args) {
		Test<String> s = new Test<String>();
		s.excuteTaskInBackground(s, Integer.valueOf(2));
	}
}
das lässt sich nicht kompilieren, da die falschen Typen bei executeTaskxxx vorhanden ist.
Code:
s.excuteTaskInBackground(s, "2");
funktioniert.

das <T> bei void ist nicht nötig - eclipse gibt sogar eine Warnung aus "The typparameter T is hiding the type T" (und klar, wenn man statt <T> z.b. <S> schreibt ist die Warnung weg - zeigt aber die unwichtigkeit des ganzen !!)
Meines Wissens ist das <T> nicht nötig, da es sich hierbei um eine Instanzmethode handelt, bei statischen Methoden ist das was anderes.

Beispiel aus der API:
Code:
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
//....
 public int size() {
	return size;
    }


    public boolean isEmpty() {
	return size == 0;
    }


    public boolean contains(Object elem) {
	return indexOf(elem) >= 0;
    }
//...

also das <T> bei der Methode ist unnötig
 
@dbac

Die Methoden-Generics können schon Sinn machen (und funktionieren auch wenn kein static da ist), ein kleines Beispiel:
Code:
public class Test<A> {
   public [B] void excuteTaskInBackground(final Test[B] task, final B argument) {
      System.out.println(task.toString());
      System.out.println(argument);
   }

   public static void main(String[] args) {
      Test<Integer> s = new Test<Integer>();

      // hier wichtig: s hat A = Integer, aber die Methode kann B=String aufgerufen werden.
      s.excuteTaskInBackground( new Test<String>(){ ... }, "blupp" ); 
   }
}
... nur hat halt hier der Parameter A nichts mit B zu tun. Vielleicht sagt das A, wie die Ausgabe von irgendwas ist, und das B wie etwas anderes verarbeitet wird.
 
ja klar in dem Fall schon.

Aber wenn man nur ein Typparameter für die Klasse hat ist es nicht nötig
 
Die Generics direkt bei einer Methode sind halt etwas flexibler, weil sie bei jedem Aufruf unterschiedliche Werte (wie doof sich das liest, natürlich existieren sie zur Laufzeit nicht mehr - aber wenn man so tut als ob, ist es leichter verständlich) annehmen können. Die Generics für Klassen bleiben immer gleich, wenn das Objekt erstmals erstellt wurde.

Ich denke mal, Sebi hat irgendeinen Controller denn er gerne mit unterschiedlichen Tasks (mit unterschiedlichen Generics) füttern möchte. Da ist was flexibles passender.

Naja, ich geh jetzt trotzdem schlafen, gute Nacht.
 
danke euch schonmal für die hilfe

mir ist grad ne idee gekommen, wenn ich jetzt meine methode nehme wie ich die hab also das ich das mit T... arguments mache und wenn argumente vorhanden, das ich dann excuteTaskInBackground(Task<T> task, T... arguments) mit dem task und den argumenten aufrufe, das hat ja auch schon funktioniert. wenn jetzt aber keine argumente da sind dann könnte ich eigentlich nur den task übergeben, der hat ja in sich den generic gespeichert und übergibt den dann auch richtig und das array arguments übergebe ich dann nicht weil es object ist und es könnte dann mit task nicht überein stimmen wenn z.b. File das generic ist und wenn dann excuteTaskInBackground(Task<T> task, T... arguments) ohne arguments aufrufe bekomm ich ja ein leeres array mit passenden typ der zu dem task passt.

So müsste es doch gehen oder?
Hm.. probieren geht über studieren^^ sobald ich wieder am notebook sitze wo das prog drauf ist probier ich es einfach aus.
 
wisst ihr auch nicht wie man das lösen kann?
bin momentan ratlos, denn auch mit mehreren maps ist das blöd. Jedenfalls wenn ich das dynamisch machen will, also nicht vorher festlege was für typen(generic klassen) es gibt.
Das muss doch noch irgendwie gehen auch mit nur einer map...
 
Nur für alle die es interessiert.
Ich hab jetzt bei sun in Forum gefragt und habe gleich nach ner Stunde schon ne antwort bekommen, die auch funktioniert.
die Lösung:
Code:
taskManager.<java.io.File>excuteTaskInBackground("Open");
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Zurück
Oben