Generics (Subtypen)

iamapo

Mitglied
Hallo,

ich habe ein Problem mit folgender Aufgabenstellung:

Schreiben Sie eine statische generische Methode copyTo, die die Inhalte der Map mtv in die Map mtu kopiert. Das erste Argument von copyTo ist mtv, das zweite mtu. Wenn der Schlüssel aus mtv bereits in mtu enthalten ist, dann darf nicht kopiert werden. Für die Schlüssel und Werte in mtv sollen auch Subtypen der Schlüssel und Werte in mtu möglich sein.


Den Kopiermechanismus habe ich schon implementiert:
Java:
public static <k, v> Map copyTo(Map mtv , Map mtu){
    	Map result =new HashMap(mtu);
    	Iterator iter = mtv.entrySet().iterator();
    	while (iter.hasNext()){
    		Map.Entry pairs = (Entry) iter.next();
    		if(!result.containsKey(pairs.getKey())){
    			result.put(pairs.getKey(), pairs.getValue());
    		}
    	}
    	return result;
    }


Das was ich jetzt nicht verstehe ist, wie ich die Subtypen der Schlüssel und Werte einbaue.
Kann mir da jemand helfen, bzw hat einen kleinen Tipp für mich?

Grüße
 
Die ganzen Map, Iterator usw. sind ja raw types. Füg' da erstmal die passenden Generic-Typparameter (Map<X,Y> ...) hinzu. (Und die NUR als einzelne GROSSbuchstaben!!!). Dann kann man mal weitersehen.
 
Ist das so richtig?

Java:
public static <K,V> Map<K, V> copyTo(Map mtv , Map mtu){
    	Map<K,V>result =new HashMap<K, V>(mtu);
    	Iterator<?> iter = mtv.entrySet().iterator();
    	while (iter.hasNext()){
    		Map.Entry<K,V> pairs =(Entry<K, V>) iter.next();
    			if(!result.containsKey(pairs.getKey())){
    			result.put(pairs.getKey(), pairs.getValue());
    		}
    	}
    	return result;
    }

Grüße
 
Anstatt dem Iterator und der while-Schleife kannst du auch die foreach-Schleife nehmen. Und den Iterator darfst du genauso wie die Parameter der Methode ebenfalls mit dem korrekten Typ parametisieren.

Java:
<K, V> Map<K, V> copyTo(final Map<K, V> mtv, final Map<K, V> mtu) {
	final Map<K, V> result = new HashMap<K, V>(mtu);
	final Iterator<Map.Entry<K, V>> iter = mtv.entrySet().iterator();
	while (iter.hasNext()) {
		final Map.Entry<K, V> pairs = iter.next();
		if (!result.containsKey(pairs.getKey())) {
			result.put(pairs.getKey(), pairs.getValue());
		}
	}
	return result;
}

Java:
<K, V> Map<K, V> copyTo(final Map<K, V> mtv, final Map<K, V> mtu) {
	final Map<K, V> result = new HashMap<K, V>(mtu);
	for (final Map.Entry<K, V> pair : mtv.entrySet()) {
		if (!result.containsKey(pair.getKey())) {
			result.put(pair.getKey(), pair.getValue());
		}
	}
	return result;
}
 
Ich sehe da immernur ein <K, V>... bisher kannt ich das nur mit konkreten generischen Typen für die Map.
z.B. Map<String, MyClass>
Wo ist jetzt der Clue bei dem <K, V> ? Was bedeutet das?
 
Der Trick ist, dass damit erst beim Aufruf entschieden wird, welcher Typ das ist
Java:
Map<Integer, String> s;
Map<Integer, String> t;
copyTo(s,t); // geht

Map<String, Float> s;
Map<String, Float> t;
copyTo(s,t); // geht auch

Und was jetzt noch gehen soll ist sowas wie
Java:
Map<Integer, String> s;
Map<Number, String> t;
copyTo(s,t); // soll gehen
 
Weiss zwar nicht wie die generischen Typen beim Kopieren passen sollen, aber man könnte ja die <K, V> Definition erweitern:

Java:
public static <K, V, A, B> Map<A, B> copyTo(Map<K, V> mtv, Map<A, B> mtu) {
		Map<A, B> result = new HashMap<A, B>(mtu);
		Iterator<Map.Entry<K, V>> iter = mtv.entrySet().iterator();
		while (iter.hasNext()) {
			Entry<K, V> pairs = iter.next();
			if (!result.containsKey(pairs.getKey())) {
				result.put((A)pairs.getKey(), (B)pairs.getValue());
			}
		}
		return result;
	}
 
Zuletzt bearbeitet:
Danke für die vielen Antworten.
Aber wie bekomm ich jetzt mein Problem mit den Subtypen hin?
Bzw was ist genau ein Subtyp?

Grüße und vielen Dank für die Antworten.
 
@NattleBet: So eben gerade nicht 😉

@iamapo: Ein Beispiel hatte ich schon beschrieben
Java:
Map<Integer, String> s;
Map<Number, String> t;
copyTo(s,t); // soll gehen

Man kann ja auch sowas machen wie
Java:
Map<Number, String> map = ...
Integer i = Integer.valueOf(123);
map.put(i, "Hallo");
Und das ganze jetzt eben für die copy-Methode erweitert.

Schau mal bei Angie vorbei 😉
 
Also ein Subtyp ist eine Abgeleitete Klasse von der Superklasse.
Meine Idee ist jetzt die Typen der Maps von einander abhängig zu machen mit extends.

Java:
	public static <K, V> Map<K, V> copyTo(Map<? extends K, ? extends V> mtv, Map<K, V> mtu) {
		Map<K, V> result = new HashMap<K, V>(mtu);
		Iterator<?> iter = mtv.entrySet().iterator();
		while (iter.hasNext()) {
			Map.Entry<? extends K, ? extends V> pairs = iter.next();
			if (!result.containsKey(pairs.getKey())) {
				result.put(pairs.getKey(), pairs.getValue());
			}
		}
		return result;
	}

Bin jetzt soweit, aber irgendwie mag mich der iter.next() noch nicht... ;(
Ist das der richtige Ansatz? ???:L Hab ja bisher noch kein cast benutzt :applaus:
 
Ist doch gar nicht dein Thread?! ???:L
Wie auch immer - man kann auch sowas schreiben wie
public static <K, V, L extends K> Map<K, V> ...
um den Typ zu kennen. Abgesehen davon stand nirgendwo, dass dort eine Map zurückgegeben werden soll, sondern dass von der einen in die andere kopiert werden soll.
 
Ja das ist nicht mein Thread, deswegen interessiert mich trotzdem die Lösung.
Der Rückgabewert von der Methode ist mir auch egal, dass hat der Ersteller so vorgegeben und spielt für mich keine Rolle. Will nur wissen, wie das mit den generics geht!

Was eben noch falsch ist ist die eine Zeile:

Java:
Map.Entry<? extends K, ? extends V> pairs = iter.next();
 
Habs jetzt gelöst... hoff ich

Java:
public class Test2 {
	public static void main(String[] args) {
		Map<Integer, String> s1 = new HashMap<Integer, String>();
		s1.put(1, "EINS");
		s1.put(2, "EINS");
		Map<Integer, String> t1 = new HashMap<Integer, String>();
		t1 = copyTo(s1, t1); // geht

		Map<String, Float> s2 = new HashMap<String, Float>();
		s2.put("00100", (float) 0.1);
		Map<String, Float> t2 = new HashMap<String, Float>();
		t2 = copyTo(s2, t2); // geht auch

		Map<Integer, String> s3 = new HashMap<Integer, String>();
		s3.put(1, "EINS");
		s3.put(2, "ZWEI");
		Map<Number, String> t3 = new HashMap<Number, String>();
		t3 = copyTo(s3, t3); // soll gehen
	}

	public static <K, V, K2 extends K, V2 extends V> Map<K, V> copyTo(Map<K2, V2> mtv, Map<K, V> mtu) {
		Map<K, V> result = new HashMap<K, V>(mtu);
		Iterator<Entry<K2, V2>> iter = mtv.entrySet().iterator();
		while (iter.hasNext()) {
			Map.Entry<K2, V2> pairs = iter.next();
			if (!result.containsKey(pairs.getKey())) {
				result.put(pairs.getKey(), pairs.getValue());
			}
		}
		return result;
	}
}
 
Das sollte auch gehen:
Java:
    public static <K, V> Map<K, V> copyTo(Map<? extends K, ? extends V> mtv, Map<K, V> mtu) {
        Map<K, V> result = new HashMap<K, V>(mtu);
        Iterator<? extends Map.Entry<? extends K, ? extends V>> iter = mtv.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<? extends K, ? extends V> pairs = iter.next();
            if (!result.containsKey(pairs.getKey())) {
                result.put(pairs.getKey(), pairs.getValue());
            }
        }
        return result;
    }
 
Ja, ich finde aber, ab einer gewissen Verschachtelungstiefe haben die <? extends X<? extends Y, ? extends Z>> Generics die Tendenz abgefuckt unübersichtlich zu werden 😉

Jetzt noch mit foreach, dann hat der TO endlich die Lösung
Java:
    public static <K, V, L extends K> Map<K, V> copyTo(Map<L, V> mtv, Map<K, V> mtu) 
    {
        Map<K, V> result = new HashMap<K, V>(mtu);
        for (Map.Entry<L, V> entry : mtv.entrySet())
        {
            if (!result.containsKey(entry.getKey())) 
            {
                result.put(entry.getKey(), entry.getValue());
            }
        }
        return result;
    }
 

Zurück
Oben