Java Generics

lumo

Top Contributor
Hallo,

ich habe ein problem mit einer meiner klassen.
und zwar folgendes:
Java:
public class AdditionalEntry<T> {

	private static final long serialVersionUID = 7817815482047475858L;
	
	protected Integer index; // iff null then invalid
	protected T value;

	public AdditionalEntry(Integer anIndex, T aValue) {
		super(true);
		index = anIndex;
		value = aValue;
	}
	
	public Class<?> getClazz() {
		return value.getClass();
	}

	public T getValue() {
		return value;
	}

	void setValue(T aValue) {
		value = aValue;
	}

	public boolean validate() {
		if (index == null || index < 0) {
			return false;
		}
		return true;
	}
}
die klasse tut eigentlich nichts besonderes.
nun habe ich allerdings noch eine weitere klasse, die folgendes tun soll:
eine map halten, für gewisse key-strings (zahl, text, string, double, boolean) und einer liste.
wenn ich zb eine liste an integer werten brauche, solll ich diese über map.get("integer") bekommen.
jetzt brauche ich noch uperationen auf diese liste: get, set, trim;
get: gib mir ein element aus der liste an position index.
set: schreibe ein element in die liste an position index (wenn die liste zu klein ist, hänge so lange null elemente an bis sie lange genug ist damit der gewünschte index verfügbar ist)
trim: alle null elemente am ENDE der liste löschen

nun habe ich generische funktionen, die mir, get, set und trim übernehmen, allerdings das problem, dass ich nicht weiss, wie ich diese verwenden kann/muss/soll, damit ich keinen error bekomme

Java:
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AdditionalList {

	private Map<String, List<AdditionalEntry<?>>> map = new HashMap<String, List<AdditionalEntry<?>>>();

	private <T> AdditionalEntry<T> get(int index, List<AdditionalEntry<T>> list) {
		if (index < list.size()) {
			return list.get(index);
		}
		return null;
	}

	private <T> List<AdditionalEntry<T>> trim(List<AdditionalEntry<T>> toTrim) {
		while (toTrim.get(toTrim.size()) == null) {
			toTrim.remove(toTrim.size());
		}
		return toTrim;
	}

	private <T> void set(List<AdditionalEntry<T>> toCheck, int index, T value) {
		while (index > toCheck.size() + 1) {
			toCheck.add(null);
		}
		toCheck.add(new AdditionalEntry<T>(index, value));
	}

	///////// TODO IMPLEMENT THIS FOR ALL OTHER CLASSES TOO!!!
	@Override
	public Integer getInteger(int index) {
		List<AdditionalEntry<Integer>> list = map.get(Integer.class.getSimpleName());
		return (Integer) get(index, list).getValue();
	}

	@Override
	public void setInteger(int index, Integer value) {
		set(map.get(Integer.class), index, value);
	}
}
kann man mein problem so nicht lösen oder hab ich einen denkfehler?
 
kann das so stimmen? (bekomme keine errors)
allerdings ist nun alles mit raw types...

Java:
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import at.biooffice.data.structure.def.IAdditional;
import at.biooffice.data.structure.lookup.AbstractLookup;

public class AdditionalList /*implements IAdditional */{

	private Map<String, List<AdditionalEntry>> map = new HashMap<String, List<AdditionalEntry>>();

	private AdditionalEntry get(int index, List<AdditionalEntry> list) {
		if (index < list.size()) {
			return list.get(index);
		}
		return null;
	}

	private List<AdditionalEntry> trim(List<AdditionalEntry> toTrim) {
		while (toTrim.get(toTrim.size()) == null) {
			toTrim.remove(toTrim.size());
		}
		return toTrim;
	}

	private <T> void set(List<AdditionalEntry> toCheck, int index, T value) {
		while (index > toCheck.size() + 1) {
			toCheck.add(null);
		}
		toCheck.add(new AdditionalEntry<T>(index, value));
	}
	
	private void trim() {
		for (String key : map.keySet()) {
			trim(map.get(key));
		}
	}


	///////// TODO IMPLEMENT THIS FOR ALL OTHER CLASSES TOO!!!
	public Integer getInteger(int index) {
		List<AdditionalEntry> list = map.get(Integer.class.getSimpleName());
		return (Integer) get(index, list).getValue();
	}

	public void setInteger(int index, Integer value) {
		set(map.get(Integer.class), index, value);
	}
}
 
Meinst du sowas?
Java:
class AdditionalList<T> {
 
    private Map<String, List<AdditionalEntry<T>>> map = new HashMap<String, List<AdditionalEntry<T>>>();
 
    private AdditionalEntry<T> get(int index, List<AdditionalEntry<T>> list) {
        if (index < list.size()) {
            return list.get(index);
        }
        return null;
    }
 
    private List<AdditionalEntry<T>> trim(List<AdditionalEntry<T>> toTrim) {
        while (toTrim.get(toTrim.size()) == null) {
            toTrim.remove(toTrim.size());
        }
        return toTrim;
    }
 
    private void set(List<AdditionalEntry<T>> toCheck, int index, T value) {
        while (index > toCheck.size() + 1) {
            toCheck.add(null);
        }
        toCheck.add(new AdditionalEntry<T>(index, value));
    }
 
    ///////// TODO IMPLEMENT THIS FOR ALL OTHER CLASSES TOO!!!
    public T getInteger(int index) {
        List<AdditionalEntry<T>> list = map.get(Integer.class.getSimpleName());
        return get(index, list).getValue();
    }
 
    public void set(int index, T value) {
        set(map.get(Integer.class), index, value);
    }
}
 
war jetzt leider länger krank...

dachte zuerst es wäre die richtige lösung.
aber...
du definierst additional als generic <T> somit muss ich beim instantiieren angeben welchen typ sie hat.. aber die klasse hält ja mehrere typen...
deren listen auf einer hashmap gehalten werden...
 
Zuletzt bearbeitet:
du musst schon genauer beschreiben was passieren soll,
wie viele generische Typen, am Anfang festgelegt oder beliebig, wie sehen die Aufrufe an die Klasse aus usw.
im Moment deutet es eher wie du es weitgehend schon hast auf eine allgemeine Map Klasse -> Liste hin, die Klasse als Parameter für set+get,

dann nur noch Methoden die allgemein passend casten, in der Hoffnung dass intern sauber abgelegt wurde,
mit dem eigentlichen Prinzip von Generics hat das nicht so viel zu tun, eher eine Cast-Vereinfachung

------

gegen 'raw type'-Fehlermeldung hilft evtl. List<AdditionalEntry<?>> usw.,
diese Fehlermeldung halte ich aber generell für unnütz, habe ich bei mir abgeschaltet
 
naja, es geht um die generischen funktionen
sowas in der art...
Java:
@Override
    public TgetValue(int index) {
        List<AdditionalEntry<T>> list = map.get(T.class.getSimpleName());
        return (Integer) get(index, list).getValue();
    }
 
    @Override
    public void setValue(int index, T value) {
        set(map.get(T.class), index, value);
    }

PS: habs inzwischen unschön implementiert...
 
in welcher form... bsp:

Java:
public T getValue(String type, int index) {
        List<AdditionalEntry<T>> list = map.get(type);
        return (T) get(index, list).getValue();
    }

das "problem" ist dass ich nicht nur von java unterstützte datentypen habe sondern auch 'Text' - was in java auf String abgebildet wird, aber NICHT in der hashmap unter string gespeichert sein darf (hat gründe die ich hier nicht erklären möchte - trägt auch nicht zum probem/lösung bei)

dass der typ der funktion mit dem erwarteten wert übereinstimmt wird im anderen teil des codes behandelt.

oder meinst du etwas anderes mit form..?
 
Du willst den Typ also als String übergeben? Und wie soll der Aufruf aussehen, und das konkrete Objekt, auf dem die Methode aufgerufen wird?

Java:
AdditionalList a = new AdditionalList(); // ???
String s0 = a.get("String", 0);
String s1 = a.get("Text", 0);
Integer i0 = a.get("Integer",0); 
Integer i1 = a.get("Ineger",1); // oops...

So wird das nicht funktionieren. Entweder muss man casten, oder den Typ in irgendeiner Form übergeben. Diese Form könnte dann auch relativ versteckt sein. Also sowas wie
Java:
AdditionalList a = new AdditionalList(); // ???
String s0 = a.get(STRING, 0);
String s1 = a.get(TEXT, 0);
Integer i0 = a.get(INTEGER, 0); 
Integer i1 = a.get(INTEGER, 1);
könnte gehen...
 
*seufz* ... ist schon zäh ... 🙁

Java:
import java.util.*;

class Type<T>
{
    public static Type<Integer> INTEGER = new Type<Integer>();
    public static Type<String> TEXT = new Type<String>();
    public static Type<String> STRING = new Type<String>();
}

public class TypedLists
{
    private Map<Type<?>, List<?>> map = new HashMap<Type<?>, List<?>>();
    
    public static void main(String args[])
    {
        TypedLists t = new TypedLists();
        
        t.add(Type.INTEGER, Integer.valueOf(123));
        t.add(Type.INTEGER, Integer.valueOf(234));
        t.add(Type.STRING, "Some string");
        t.add(Type.TEXT, "Some text");
        
        Integer i0 = t.get(Type.INTEGER, 0);
        Integer i1 = t.get(Type.INTEGER, 1);
        String s0 = t.get(Type.STRING, 0);
        String s1 = t.get(Type.TEXT, 0);
        System.out.println(i0);
        System.out.println(i1);
        System.out.println(s0);
        System.out.println(s1);
    }
    
    @SuppressWarnings("unchecked")
    public <T> void add(Type<T> type, T object)
    {
        List<T> list = (List<T>) map.get(type);
        if (list == null)
        {
            list = new ArrayList<T>();
            map.put(type, list);
        }
        list.add(object);
    }

    @SuppressWarnings("unchecked")
    public <T> T get(Type<T> type, int index)
    {
        List<T> list = (List<T>) map.get(type);
        if (list == null)
        {
            return null;
        }
        return list.get(index);
    }
}
 
@Marco: Wieso verwendest du nicht einfach Class<T> anstatt Type<T>? Würde sich doch anbieten:

Java:
private Map<Class<?>, List<?>> map = new HashMap<Class<?>, List<?>>();
//u.s.w.
 
Jein... abgesehen davon, dass das nur ein "im-Nebel-Stocher-Beispiel" sein sollte, könnte so ein "Type" je nach Anwendungsfall noch weitere Methoden haben, oder die möglichen Typen könnten eingeschränkt sein durch
Code:
[b]final[/b] class Type<T>
{
    public static final Type<Integer> INTEGER = new Type<Integer>();
    ...

    [b]private Type() {}[/b]
}
Aber es stimmt natürlich: Im konkreten Beispiel würde es Class genauso tun...

EDIT: ... und wäre vielleicht näher an dem, was ursprünglich vom TO so an Code gepostet wurde ... aber wenn dann von Enums und Vereinfachungen die Rede ist, weiß man ja nicht, wie belastbar das gepostete ist...
 
also erst nochmal ein großes danke dass ihr euch das mit mir gebt 🙂
ich denke so weit ab von dem was Marco13 geschrieben hat ist mein jetziger code nicht...
vielleicht ist es einfacher, wenn ich meine klassen offen lege...
(fehlende includes sind hier nicht wichtig - könnte man auch rauslassen)

mein enum für die verschiedenen typen
Java:
/**
 * 
 * @author lumo
 * @version 1.0
 */
public enum AdditionalType {
	STRING(String.class), TEXT(String.class), INTEGER(Integer.class),
	BOOLEAN(Boolean.class), DOUBLE(Double.class), CALENDAR(Calendar.class),
	LOOKUP(AbstractLookup.class);
	
	private Class<?> clazz = null;
	
	AdditionalType(Class<?> aClazz) {
		clazz = aClazz;
	}
	
	public Class<?> getClazz() {
		return clazz;
	}
}

dann meine klasse die einen eintrag hält. (GuidItem hält nur zwei variablen und getter/setter für die db, validateble spricht für sich selbst)
Java:
/**
 * 
 * @author lumo
 * @version 1.0
 * @param <T>
 */
public class AdditionalEntry<T> extends GuidItem implements Validateable {

	private static final long serialVersionUID = 7817815482047475858L;

	protected Integer index; // iff null then invalid
	protected T value;

	/**
	 * 
	 * @param anIndex
	 * @param aValue
	 */
	public AdditionalEntry(Integer anIndex, T aValue) {
		super(true); // do not generate a guid
		index = anIndex;
		value = aValue;
	}

	public Class<?> getClazz() {
		return value.getClass();
	}

	public T getValue() {
		return value;
	}

	void setValue(T aValue) {
		value = aValue;
	}

	public boolean validate() {
		if (index == null || index < 0) {
			return false;
		}
		return true;
	}

	@Override
	public Object save() {
		// TODO IMPLEMENT ME
		System.err.println(getClass().getSimpleName()
				+ ".save() is not implemented yet");
		return null;
	}
}

und zu guter letzt noch die Listen klasse
Java:
public abstract class AdditionalList extends GUIDImpl /* implements IAdditional */{

	protected static final long serialVersionUID = -6917704708648208251L;
	protected Map<AdditionalType, List<AdditionalEntry<?>>> map = new HashMap<AdditionalType, List<AdditionalEntry<?>>>();

	protected AdditionalEntry<?> get(AdditionalEntry<?> type, int index) {
		List<AdditionalEntry<?>> list = (List<AdditionalEntry<?>>) map.get(type);
		return get(index, list);
	}

	/**
	 * get an entry off the list
	 * 
	 * @param index
	 * @param list
	 * @return
	 */
	protected AdditionalEntry<?> get(int index, List<AdditionalEntry<?>> list) {
		if (list != null && index < list.size()) {
			return list.get(index);
		}
		return null;
	}

	/**
	 * set an entry in the list.<br>
	 * iff the index is larger than the existint list append null elements until
	 * the list is large enough to add the element!
	 * 
	 * @param <T>
	 * @param toCheck
	 * @param index
	 * @param value
	 */
	protected <T> void set(List<AdditionalEntry<?>> toCheck, int index, T value) {
		while (index > toCheck.size() + 1) {
			toCheck.add(null);
		}
		toCheck.add(new AdditionalEntry<T>(index, value));
	}

	/**
	 * trim all available lists<br>
	 * obsolete
	 */
	@Deprecated
	protected void trim() {
		for (AdditionalType key : map.keySet()) {
			trim(map.get(key));
		}
	}

	/**
	 * trim the given list<br>
	 * obsolete
	 */
	@Deprecated
	protected List<AdditionalEntry<?>> trim(List<AdditionalEntry<?>> toTrim) {
		while (toTrim.get(toTrim.size()) == null) {
			toTrim.remove(toTrim.size());
		}
		return toTrim;
	}
}
die klasse ist nicht generisch sondern per <?> gelöst, wie ihr seht

@marco13 - wie kann ich nun deine funktion so anpassen dass das bei mir noch klappt
Java:
public <T> T get(Type<T> type, int index)
    {
        List<T> list = (List<T>) map.get(type);
        if (list == null)
        {
            return null;
        }
        return list.get(index);
    }
ich seh grad vor lauter spitzklammern nicht mehr wie das funktionieren soll :shock:
 
Joa, ich hatte schon beim vorletzen Beitrag so dieses dumpfe Gefühl, dass du vielleicht sowas suchst. Das "Problem" dabei ist, dass Enums keine Typparameter haben können. Deswegen (und das ist das entscheidende) können diese <T>s nicht zur Compilezeit überprüft werden. Man kann sich zwar nicht getClazz() die Klasse holen und "manuell" Tests machen, aber der Compiler sieht nicht, ob das passt. Die oben angedeutete "Type"-Klasse wäre so gesehen eine Art Ersatz für eine Enum, nur eben als normale Klasse, damit man Typparameter angeben kann. Vielleicht gibt es schönere oder für deinen Fall geeignetere Lösungen, aber ich denke, das kommt schon recht nahe an das, was du mit der Enum vorhattest.
 
also muss ich meine tollen enums 😉 gegen eine klasse eintauschen und dort statische finale instanzen ansiedeln damit das geht...
werd ich mir baldmöglichst vornehmen
 
EUREKA!
nun funktionierts (mit weniger code) genau so wie es sollte! :applaus:
vielen dank @Marco13

Java:
public class AdditionalType<T> {
	public static final AdditionalType<String> STRING = new AdditionalType<String>();
	public static final AdditionalType<String> TEXT = new AdditionalType<String>();
	public static final AdditionalType<Integer> INTEGER = new AdditionalType<Integer>();
	public static final AdditionalType<Boolean> BOOLEAN = new AdditionalType<Boolean>();
	public static final AdditionalType<Double> DOUBLE = new AdditionalType<Double>();
	public static final AdditionalType<Calendar> CALENDAR = new AdditionalType<Calendar>();
	public static final AdditionalType<AbstractLookup> LOOKUP = new AdditionalType<AbstractLookup>();
}
Java:
public class AdditionalList extends GUIDImpl /* implements IAdditional */{

	protected static final long serialVersionUID = -6917704708648208251L;
	protected Map<AdditionalType<?>, List<?>> map = new HashMap<AdditionalType<?>, List<?>>();

	@SuppressWarnings("unchecked")
	public <T> T get(AdditionalType<T> type, int index) {
		if (index > getSize(type) - 1) {
			return null;
		}
		List<T> list = (List<T>) map.get(type);
		if (list == null) {
			return null;
		}
		return list.get(index);
	}

	@SuppressWarnings("unchecked")
	public <T> int getSize(AdditionalType<T> type) {
		return ((List<T>) map.get(type)).size();
	}

	/**
	 * set an entry in the list.<br>
	 * iff the index is larger than the existint list append null elements until
	 * the list is large enough to add the element!
	 * 
	 * @param <T>
	 * @param toCheck
	 * @param index
	 * @param value
	 */
	@SuppressWarnings("unchecked")
	public <T> void set(AdditionalType<T> type, int index, T object) {
		List<T> list = (List<T>) map.get(type);
		if (list == null) {
			list = new ArrayList<T>();
		}
		System.out.println("size: " + list.size() + " > index " + index);
		while (index > list.size() - 1) {
			list.add(null);
		}
		list.set(index, object);
		map.put(type, list);
	}

	public static void main(String[] args) {
		AdditionalList t = new AdditionalList();

		t.set(AdditionalType.INTEGER, 1, Integer.valueOf(123));
		t.set(AdditionalType.INTEGER, 0, Integer.valueOf(234));
		t.set(AdditionalType.STRING, 1, "Some string");
		t.set(AdditionalType.TEXT, 1, "Some text");

		Integer i0 = t.get(AdditionalType.INTEGER, 0);
		Integer i1 = t.get(AdditionalType.INTEGER, 1);
		String s0 = t.get(AdditionalType.STRING, 0);
		String s1 = t.get(AdditionalType.TEXT, 1);

		System.out.println(t.getSize(AdditionalType.INTEGER));

		System.out.println(i0);
		System.out.println(i1);
		System.out.println(s0);
		System.out.println(s1);

	}
}
 
und jetzt noch eine frage dazu...
firePropertyChange(new PropertyChangeEvent(
wenn ich sowas dranhängen muss, dann in der set funktion der klasse additionallist - richtig?? *blush*
 
Zuletzt bearbeitet:
observer hab ich in meiner anwendung schon implementiert aber
da die daten aber mit swt angezeigt werden will ich beans haben, da diese einfach gebunden werden können 😉
also warum umständlich selbst observer schreiben, wenns 'einfach' geht
habs mir noch einmal überlegt und wenn ich einfach beim setzen des eintrages einer liste fireevent mache, müsste das kappen

das sollte stimmen:
Java:
@SuppressWarnings("unchecked")
	public <T> void set(AdditionalType<T> type, int index, T object) {
		List<T> list = (List<T>) map.get(type);
		
		if (list == null) {
			list = new ArrayList<T>();
		}
		System.out.println("size: " + list.size() + " > index " + index);
		while (index > list.size() - 1) {
			list.add(null);
		}
		// temp the old value
		T oldValue = list.get(index);
		// set the new value
		list.set(index, object);
		// fire property change event!
		firePropertyChange(new PropertyChangeEvent(this, "add"+AdditionalType.getString(type)+index,
				oldValue, oldValue = object));
		map.put(type, list);
	}

Java:
public static String getString(AdditionalType<?> type) {
		if (type.equals(STRING)) {
			return "String";
		} else if(type.equals(TEXT)){
			return "Text";
		} else if(type.equals(INTEGER)){
			return "Integer";
		} else if(type.equals(BOOLEAN)){
			return "Boolean";
		} else if(type.equals(DOUBLE)){
			return "Double";
		} else if(type.equals(CALENDAR)){
			return "Calendar";
		} else if(type.equals(LOOKUP)){
			return "Lookup";
		}
		return "undefined";
	}
 
Zuletzt bearbeitet:

Zurück
Oben