generische Klasse für alle Maps (nicht Collections :-))

dermoritz

Bekanntes Mitglied
Ich versuche gerade eine Klasse zu bauen die für alle Collections funktioniert. Genaugenommen hat sie einen Constructor der eine Collection nimmt und eine Methode, welche etwas an diese Collection dranhängt (add) - (wer Google Web Toolkit kennt dem wird das bekannt vorkommen):
Java:
private class LoadMaps<E,T extends Collection> implements AsyncCallback<T> {
		private T target;
		
		protected LoadMaps( T whereTo) {
			target.add(whereTo);
		}

		@Override
		public void onFailure(Throwable caught) {
			Window.alert(caught.getMessage());

		}

		@Override
		public void onSuccess(T result) {
			target.addAll(result);
			fireEvent(success);
		}
	}

Nur leider funktioniert das nicht. Ein Aufruf des Cunstructors mit einer Map<String, List<String>> ergibt "The constructor AdminUnits.LoadMaps(TaskDoneEvent, Map<String,List<String>>) is undefined".
Was mach ich (schonwieder) falsch mit generics?
 
Zuletzt bearbeitet:
du glaubst nicht wie sehr mir die einfache Antwort hilft 🙂. Gleichzeitig versinke ich vor Scham im Boden - manchmal ist man halt dümmer als man denkt (ich dachte ich bin zu dumm für generics, aber ich war sogar zu dumm für Collections/Maps)
 
noch eine klitzekleine frage: nun bekomme ich die "raw type" Warnungen. Wie bekomme ich die weg? Irgendwie reicht
Code:
private class LoadMaps<T extends Map<?,?>> implements AsyncCallback<T>...
nicht. Damit funktioniert
Code:
target.putAll(result)
nicht mehr:

Code:
The method putAll(Map<? extends capture#3-of ?,? extends capture#4-of ?>) in the type Map<capture#3-of ?,capture#4-of ?> is not applicable for the arguments (T)
 
Map ist keine Collection.

Man könnte Map als eine Art Collection<Map.Entry> auffassen, die Frage ist, wie praktisch das ist, z.B. bezieht sich dann add, remove, contains u.s.w. immer auf dan gesamten Entry, also key und value zusammengenommen.

Ich habe mal einen Wrapper gebastelt (ungetestet):

Java:
import java.util.List;
import java.util.ArrayList;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import static java.util.Map.Entry;

public class MapCollection<K,V> implements Collection<Entry<K,V>> {

    private final Map<K,V> map;
    
    public MapCollection(Map<K,V> map) {
        this.map = map;
    }
    
    public int size() {
        return map.size();
    }

    public boolean isEmpty() {
        return map.isEmpty();
    }

    public boolean contains(Object o) {
        if(o instanceof Entry) {
            Entry entry = (Entry) o;
            return entry.getValue().equals(map.get(entry.getKey()));
        }
        return false;
    }

    public Iterator<Entry<K, V>> iterator() {
        return map.entrySet().iterator();
    }

    public Object[] toArray() {
        Object[] array = new Object[size()];
        int i = 0;
        for(Iterator it = iterator(); it.hasNext();) {
            array[i++] = it.next();
        }
        return array;
    }

    public <T> T[] toArray(T[] a) {
        T[] array = (T[]) (a.length <= size() ? a 
                : Array.newInstance(a.getClass().getComponentType(), size()));
        int i = 0;
        for(Iterator it = iterator(); it.hasNext();) {
            array[i++] = (T) it.next();
        }
        return array;
    }

    public boolean add(Entry<K, V> e) {
        if(contains(e)) {
            return false;
        } else {
            map.put(e.getKey(), e.getValue());
            return true; 
        }
    }

    public boolean remove(Object o) {
        if(contains(o)) {
            Entry entry = (Entry) o;
            map.remove(entry.getKey());
            return true;
        } else {
            return false;
        }
        
    }

    public boolean containsAll(Collection<?> c) {
        for(Object o : c) {
            if(! contains(c)) {
                return false;
            }
        }
        return true;
    }

    public boolean addAll(Collection<? extends Entry<K, V>> c) {
        boolean changed = false;
        for(Entry<K,V> entry : c) {
            changed |= add(entry);
        }
        return changed;
    }

    public boolean removeAll(Collection<?> c) {
        boolean changed = false;
        for(Object entry : c) {
            changed |= remove(entry);
        }
        return changed;
    }

    public boolean retainAll(Collection<?> c) {
        List<Entry<K,V>> deleteList = new ArrayList<Entry<K,V>>();
        for(Entry<K,V> entry : this) {
            if (! c.contains(entry)) {
                deleteList.add(entry);
            }
        }
        return removeAll(deleteList);
    }

    public void clear() {
        map.clear();
    }

    public Map<K,V> getMap() {
        return map; 
    }
}
 
vielen dank werde mir das mal "bookmarken". Im Moment reicht mir aber tatsächlich die Klasse für alle Maps. Aber wie gesagt ich krieg die Raw-Type Warnungen nicht weg.

Was kann ich da machen?
 
was willst du denn machen, was bedeutet T? ist T die Map oder nicht eher der Valuetype in der Map?
wie genau ist der Key der Map eingegrenzt, wie sieht denn deine add()-Methode im Moment aus,
was genau fügst du als Key und Value ein?

Informationen preiszugeben würde helfen wenn du auf den Erhalt von Informationen hoffst,

edit: wie wärs mit
Java:
    public static <A, B, T extends Map<A, B>>void putAll(T one, T two)
    {
        one.putAll(two);
    }
 
Zuletzt bearbeitet von einem Moderator:
In generischen Collections, deren generischer Typ durch eine Wildcard ([c]?[/c]) gekennzeichnet ist, kannst du keine Objekte einfügen, wenn ich mich nicht irre. Der Compiler kann nicht sicherstellen, dass du ein Objekt vom korrekten Typ hinzufügst, und untersagt das Hinzufügen daher komplett.
 
ich will weder die keys noch die values der map einschränken. die klasse soll auf allen maps funktionieren - da ich lediglich "putAll".

nach dem Fauxpas mit maps sind keine collections sieht die Klasse nun so aus:
Java:
private class LoadMaps<T extends Map> implements AsyncCallback<T>{
		private T target;
		
		protected LoadMaps(TaskDoneEvent ev, T whereTo) {
			target = whereTo;
		}

		@Override
		public void onFailure(Throwable caught) {
			Window.alert(caught.getMessage());

		}

		@Override
		public void onSuccess(T result) {
			target.clear();
			target.putAll(result);
		}
	}
im Moment bekomme ich 2 raw-type Warnungen die ich gerne loswerden würd: in der ersten Zeile bei Map und in der Zeile mit "putAll". Ersteres bekomme ich weg in dem ich Map durch Map<?,?> ersetze, aber dann gibts nen Error bei putAll.

funktional sollte es klar sein was geschieht: man übergibt im contructor eine map die bei aufruf von onSuccess den inhalt von result enthält.

EDIT: um mal auf deinen Vorschlag einzugehen
Java:
private class LoadMaps<A,B,T extends Map<A,B>> implements AsyncCallback<T>
slaterB der bringt eben nur bedingt was: die Warnungen in der Klasse sind dann weg, aber sie bleiben bei jedem Aufruf des Constructors. Aus irgendeinem Grund kann ich nrgendwo "T<A,B>" verwenden.
 
Zuletzt bearbeitet:
also bei mir funktionierts, wenn es auch nicht allzu schick aussieht, das gebe ich zu,
vielleicht habe ich aber manche Warnung ausgeschaltet,

wie immer: Informationen würden helfen, welcher Code, welche Fehlermeldung?

edit: ich habe noch eine zweite Variante eingebaut, wobei ich generische Parameter bei nicht-statischen Methoden eigentlich lieber vermeide

edit: und Variante 3
Java:
public class Test {
    public static void main(String[] args)  {
        Map<String, Integer> a = new HashMap<String, Integer>();

        LoadMaps<String, Integer, Map<String, Integer>> k = 
            new LoadMaps<String, Integer, Map<String, Integer>>();
        k.putAll(a, a);

        LoadMaps2<String, Integer> k2 = new LoadMaps2<String, Integer>();
        k2.putAll(a, a);
        
        LoadMaps3<String, Integer> k3 = new LoadMaps3<String, Integer>();
        k3.putAll(a, a);
    }
}

class LoadMaps<A, B, T extends Map<A, B>> {
    public void putAll(T one, T two){
        one.putAll(two);
    }
}

class LoadMaps2<A, B> {
    public <T extends Map<A, B>>void putAll(T one, T two) {
        one.putAll(two);
    }
}

class LoadMaps3<A, B> {
    public void putAll(Map<A, B> one, Map<A, B> two){
        one.putAll(two);
    }
}
 
Zuletzt bearbeitet von einem Moderator:
Danke!

der Fehler lag nur im Aufruf/ Instanziierung - und ja das sieht wirklich nicht schick aus. Aber besser gehts nicht oder - was meinen die anderen?

Es geht einfacher:
private class LoadMaps<K,V> implements AsyncCallback<Map<K,V>>...
!

da habe ich mich etwas im kreis gedreht - vielen Dank!
 
Zuletzt bearbeitet:
'siehe auch meine Edits' wollte ich mit der nächsten, also dieser, Antwort schreiben, die letzte Variante hast du ja jetzt auch 😉

unten noch ein Beispielprogramm, warum es nur mit Vorgabe der Gesamt-Map bzw. List in dem Beispiel nicht funktioniert,
man kann die Warnung zwar unterdrücken, aber bei falscher Bedienung sind dann Fehler möglich,

Problem ist ein halb allgemeiner Typ '? extends' für das T der Extra-Klasse Load,
wenn man dort das T einschränken könnte so dass nur List<Number> usw. statt List<? extendsNumber> gewählt werden könnte,
dann wäre die addAll()-Methode sicher,
mag sein dass es dafür eine Syntax gibt, ansonsten eine Begrenztheit in Java

Java:
public class Test {
    public static void main(String[] args)  {
        List<Integer> a = new ArrayList<Integer>();
        List<Double> b = new ArrayList<Double>();
        b.add(4.3);
        List<? extends Number> ca = a;
        List<? extends Number> cb = b;

        Load<List<? extends Number>> l = new Load<List<? extends Number>>();
        l.addAll(ca, cb);
        Integer k = a.get(0); // ClassCastException
    }
}

class Load<T extends List<?>> {
    public void addAll(T one, T two) {
        one.addAll((List)two);
    }
}
 

Zurück
Oben