Aus Klassen Objekte instanziieren

takidoso

Erfahrenes Mitglied
Hallo und Halli,
irgendwie stehe ich ein wenig dumm auf dem Schlauch.
Ich wollte eine relativ einfache Factory, die bestimmte Klassen in einer Map halten würde, Objekte instaziiert.
Klingt doch eigetnlich normal aber Nun weiß ich nicht so richtig warum ich keine Methode finde, die aus einer Klasse ein Objekt holt.
Sicher es gibt die forName Methode, aber die verlangt wieder einen Namen.

folgendes habe ich ausprobiert was aber irgendwie schon strukturell falsch zu sein scheint.
Java:
public class PacsToMT103ConvFactory
{
	static private Map <String, String> converterClassMap = new HashMap<String, String>(); 
	

	static public PacsToSwiftConverter newInstance(String identifier)
	{
		return (PacsToSwiftConverter)Class.forName(converterClassMap.get(identifier)); 
	}

	static public void register(String key, String convClassName)
	{
		converterClassMap.put(key, convClassName);
	}
}
PacsToSwiftConverter ist eine abstrakte Klasse und ich hatte gedacht, dass die Map durch z.B. den Static-Block in den konkreten Unterklassen mit Leben (Elementen) gefüllt wird, was offenbar zumindest kompilierbar ist:
Java:
public class Pacs04ToMT103Converter extends PacsToSwiftConverter implements Pacs04Tagging
{
	static 
	{
		PacsToMT103ConvFactory.register(FT04, Pacs04ToMT103Converter.class.getName());
	}
	
	public Pacs04ToMT103Converter()
	{
	}
...
}
FT04 ist hier eine Textkonstante aus der Interface Pacs04Tagging

drolligerweise im oberen Listing hat er schon compile Probleme in der Methode static public PacsToSwiftConverter newInstance(String identifier), die da lauten:
Cannot cast from Class<capture#1-of ?> to PacsToSwiftConverter
Irgendwie verstehe ich nicht so ganz warum, bzw, was ich da anders machen muss.

Für hilfreiche Ideen sehr dankbar

Takidoso
 
Registriercode ausführen über Annotations

Ich denke ich habe es weitest gehend nun hinbekommen, indem ich bei der Instanziierung über ein algemeines Objekt gegangen bin. Ich weiß zwar nicht ob dies noch State of the Art ist, aber mir fiel nix anderes ein. Außerdem, auch wenn es mit dem eigetnlichen Problem nix zu tun hatte, habe ich ein Interface anstelle der abstrakten Klasse verwendet.
Die factory sieht nun so aus:
Java:
import java.util.HashMap;
import java.util.Map;

public class PacsToMT103ConvFactory
{
	static private Map <String, String> converterClassMap = new HashMap<String, String>(); 
	

	static public PacsToSwiftConverting newInstance(String identifier)
	{
		PacsToSwiftConverting conv;
		
		try
		{
			Class convClass = Class.forName(converterClassMap.get(identifier));
			conv = (PacsToSwiftConverting)convClass.newInstance();
			
		}
		catch (NullPointerException nEx)
		{
			String msg = "No Converter found for '"+identifier+"'";
			msg += "\n suppoting";
			for (String ident : converterClassMap.keySet())
			{
				msg += "\n  '"+ident+"'";
			}
			msg += "\n only!";
			
			throw new IllegalArgumentException(msg);
		}
		catch (Exception ex)
		{
			throw new RuntimeException(ex);
		}
		return   conv;
	}

	static public void register(String key, String convClassName)
	{
		converterClassMap.put(key, convClassName);
	}
}

Nun funktioniert jedoch leider das registrieren über den static Block nicht.
Vermutlich weil der erst zur allersten Instanzierungszeit angezogen wird.

Ich bin mir nicht sicher, aber könnte man hier es elegant mit Annotations machen? Wenn ja wie sähe das dann aus, habe leider noch kein Beispiel durch Internet-Recherche bezogen auf meine Situation finden können.

Für weiterhelfende Links und Hinweise dankbar

Takidoso
 
Zuletzt bearbeitet:
Wenn du für einen Identifier Klassen registrieren willst, die ein bestimmtes Interface implementieren, solltest du das auch so hinschreiben (nebenbei, die Reihenfolge ist public static und nicht umgekehrt):

Java:
public static register(String identifier, Class<? extends PacsToSwiftConverting> clazz)

Gleiches für die Map. Dann sollte auch das casten für newInstance wegfallen und man kann keine "falschen" Klassen registrieren.

Die NullPointerException zu fangen ist übrigens sehr unschöner Code. Prüfe doch vorher mit containyKey auf der Map ob es einen Converter gibt.

Das mit dem Registrieren musst du wohl außerhalb machen. Ist in dem Context auch so üblich.
 
Hi, Zeja,
Mensch, Dank dir. bezogen auf die Generics war das der Durchbruch, da ich bisher nur die simplen Situationen verstand.
Ich habe das nun entsprechend geändert und es funktioklappt nun auch so wie ich es mir ursprünglich vorgestellt hatte.

Bei der Reihnfolge static public ist das bei Java eigentlich recht liberal. Ich weiß, viele mögen lieber public static u.ä.. Da aber public, protected private häfiger vor kommt als static oder gar final, habe ich mir angewöhnt das seltenste zuerst hinzuschreiben, da it es mir besser in meine trüben Augen springt :-)

Di NullpointerException abzufangen, da hatte ich mit mir auch gehadert, da es eher unüblich ist. Da aber ich sowieso einen Try/catch Block hatte und nur sehr wenig code dazwischen, fand ich es unnötig und vom Schriftbild etwas störend da noch eine null-Abfrage reinzubauen um dann sowieso eine Exceptio vom Stapel zu lassen. Und es bei einer nackedixen NullPointer-Exception für einProblem, dass ganz genau fachlich in einer Fehelrmeldung benannt werden kann, zu belassen fand ich dann auch nicht so prickelnd.
(nicht schön aber selten ;))

Takidoso

hier nochmal die neuere Fassung
Java:
import java.util.HashMap;
import java.util.Map;

import de.cmk.util.ClassCollector;

public class PacsToMT103ConvFactory
{
	
	static private Map <String, Class<? extends PacsToSwiftConverting>> converterClassMap = new HashMap<String, Class<? extends PacsToSwiftConverting>>(); 
	
	static public PacsToSwiftConverting newInstance(String identifier)
	{
		PacsToSwiftConverting conv;
		
		try
		{
			Class<? extends PacsToSwiftConverting> convClass = converterClassMap.get(identifier);
			
			if (convClass!=null)
			{
				conv = (PacsToSwiftConverting)convClass.newInstance();
			}
			else
			{
				String msg = "No Converter found for '"+identifier+"'";
				msg += "\n supporting";
				for (String ident : converterClassMap.keySet())
				{
					msg += "\n  '"+ident+"'";
				}
				msg += "\n only!";
				
				throw new IllegalArgumentException(msg);
			}
			
		}
		catch (Exception ex)
		{
			throw new RuntimeException(ex);
		}
		return   conv;
	}

	static public void register(String key, Class<? extends PacsToSwiftConverting> convClass)
	{
		converterClassMap.put(key, convClass);
	}
	
}
 
Zuletzt bearbeitet:

Neue Beiträge

Zurück