How can I access a Java constructor in a generic way? - java

I have a static builder method for a class "Model" that takes a JSON string and returns an ArrayList of Models. I would like it refer to the Model's constructer generically so that subclasses can inherit the builder method.
public class Model
{
protected int id;
public Model(String json) throws JSONException
{
JSONObject jsonObject = new JSONObject(json);
this.id = jsonObject.getInt("id");
}
public static <T extends Model> ArrayList<T> build(String json) throws JSONException
{
JSONArray jsonArray = new JSONArray(json);
ArrayList<T> models = new ArrayList<T>(jsonArray.length());
for(int i = 0; i < jsonArray.length(); i++)
models.add( new T(jsonArray.get(i)) )
return models;
}
}
This is a simplified implementation of the class, the relevant line being
models.add( new T(jsonArray.get(i)) )
I know this isn't possible, but I would like to write something that calls the constructor of whatever type T happens to be. I have tried to use this(), which obviously doesn't work because the method "build" is static and I've tried to use reflection to determine the class of T but have been at a loss to figure out how to get it to work. Any help is greatly appreciated.
Thanks,
Roy

The workaround for "dynamic instantiation" with generics is to pass a hint to the class or the method:
public class Model<T> {
Class<T> hint;
public Model(Class<T> hint) {this.hint = hint;}
public T getObjectAsGenericType(Object input, Class<T> hint) throws Exception {
return hint.cast(input);
}
public T createInstanceOfGenericType(Class<T> hint) throws Exception {
T result = hint.newInstance();
result.setValue(/* your JSON object here */);
return result;
}
}
I'm happy to provide more help/ideas but I'm not sure what you want to achieve with your technical solution.
(Note: Example has some over-simplified Exception handling)

The way it is written now, I can't see that the T type parameter in build() is of any use whatsoever. Can't you just drop it and use Model in its place? If so, that would solve your construction problem.

Related

How can I pull values from a deserialized feed when its class isn't known at runtime?

I'm using Jackson to deserialize feeds and pull specific values from it. I'm trying to do this without know what feeds will be selected at runtime. I attempted to make a generic class that contains the method for deserializing the feed, and it works oddly enough. The problem is I can't pull any information from it without explicitly stating which class I am pulling from.
public class Motor<T> {
final ObjectMapper jsonMapper = new ObjectMapper();
public Object pullJson( String url, Object blah ) throws IOException{
#SuppressWarnings("unchecked")
T mapped = jsonMapper.readValue(new URL(url), (Class<T>) blah);
return mapped;
}
//Some other code
}
public class Root{
public static String value;
public String getValue(){
return value;
}
}
public class Propeller {
public static void main(String[] args) throws IOException{
Motor<?> motor = new Motor();
List<Class<?>> classes = new ArrayList<Class<?>>();
classes.add(Root.class)
String url = "blah.com/blah.json"
Object blah = classes.get(0);
blah = motor.pullJson(url, blah);
System.out.println(blah.value);
}
}
The only way I could think to pass a class into the method without know what the class will be was to set the class parameter as an Object and then pass a generic class onto it.
If I we're to ask for Root.value, it'll give me value's value, but I won't know what class the method will act on at runtime. And trying to act on the class declared as blah will tell me value cannot be resolved. I have a feeling my thought process here is extremely flawed in trying to pass a class as an object and reading the values that way.

Is Reflection needed to apply the correct generic adapter to my object dynamically

I am currently working on a serialization routine which uses a library of generically typed adapters. If the object being serialized is an instance of one of the specific adapters I have, then I need to call that adapter on the object prior to performing my other serialization procedures.
The following code works:
private final static String serialize(Object obj, Map<Class<?>,
XmlAdapter<?,?>> classToAdapterMap) throws JAXBException
{
Object adaptedObj = null;
for (Class<?> clazz : classToAdapterMap.keySet()) {
if (clazz.isInstance(obj)) {
XmlAdapter<?,?> adapter = classToAdapterMap.get(clazz);
Class<?>[] argTypes = new Class[] {clazz};
try {
Method method = adapter.getClass().getMethod("marshal", argTypes);
adaptedObj = method.invoke(adapter, obj);
break;
} catch (Exception e) {
// handle method retrieval and invocation related exceptions
}
}
}
// serialize
}
However, I had originally thought that I would be able to do this more simply, for example with code like:
/* DOES NOT WORK */
private final static String serialize(Object obj, Map<Class<?>,
XmlAdapter<?,?>> classToAdapterMap) throws JAXBException
{
Object adaptedObj = null;
for (Class<?> clazz : classToAdapterMap.keySet()) {
if (clazz.isInstance(obj)) {
XmlAdapter<?,?> adapter = classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(clazz.cast(obj));
break;
}
}
// serialize
}
Clearly the problem is that the wildcard generically typed adapter isn't guaranteed to handle an object of type clazz. However, I can't indicate that these two are the same by changing the method signature—as I might otherwise do—to private final static <T> String serialize(Object obj, Map<Class<T>, XmlAdapter<?,T>> classToAdapterMap), because the map needs to hold adapters of all different types.
What would be a better way to do this? Or should I stick with the Reflection based solution?
Thanks in advance,
-Dan
There are several solutions to circumvent this problem.
Most likely, the easiest one is using raw types: don't specify the type parameters for the adapter, and the compiler will happily accept the marshall call (with a raw type warning of course):
XmlAdapter adapter = classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(obj);
(This is actually roughly the same solution as Bastian's, without the intermediate type)
If you don't like raw types, you may choose the unchecked cast to an Object-parameterized adapter. It's not really better, but it also works (by tricking the compiler…):
XmlAdapter<?, Object> adapter = (XmlAdapter<?, Object>) classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(obj);
My last solution is to use a type parameter at the method level. This time, what you do is semantically correct (as long as the map itself is correct), and the unchecked cast really means “I know what I am doing here”:
private final static <T> String serialize(T obj, Map<Class<?>,
XmlAdapter<?,?>> classToAdapterMap) throws JAXBException
{
Object adaptedObj = null;
for (Class<?> clazz : classToAdapterMap.keySet()) {
if (clazz.isInstance(obj)) {
try {
XmlAdapter<?, ? super T> adapter = (XmlAdapter<?, ? super T>) classToAdapterMap.get(clazz);
adaptedObj = adapter.marshal(obj);
break;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// serialize
}
The semantic correctness comes from the following:
you may consider T to be the actual class of obj since T is a method-bound parameter, not used elsewhere in the signature;
clazz is a super type of the type of T since we checked clazz.isInstance(obj);
adapter can handle instances of clazz or a super-type of it since it is how the map was built;
by consequent, adapter can handle all instances of an (unknown) super type of T, hence the ? super T declaration.
There is a simpler and safer way without using reflection:
At first, we need a small specialization of the XmlAdapter as it allows us to ask the adapter for the type it can handle.
public abstract class TalkingXmlAdapter<ValueType, BoundType> extends XmlAdapter<ValueType, BoundType> {
public abstract Class<BoundType> getBoundType();
}
My custom adapters now need to extend TalkingXmlAdapter:
public class AppleXmlAdapter extends TalkingXmlAdapter<String, Apple> {
#Override
public Class<Apple> getBoundType() {
return Apple.class;
}
#Override
public Apple unmarshal(String v) throws Exception {
System.out.println("Unmarshalling Apple");
return new Apple();
}
#Override
public String marshal(Apple v) throws Exception {
System.out.println("Marshalling Apple");
return "Apple";
}
}
public class BananaXmlAdapter extends TalkingXmlAdapter<String, Banana> {
#Override
public Class<Banana> getBoundType() {
return Banana.class;
}
#Override
public Banana unmarshal(String v) throws Exception {
System.out.println("Unmarshalling Banana");
return new Banana();
}
#Override
public String marshal(Banana v) throws Exception {
System.out.println("Marshalling Banana");
return "Banana";
}
}
That allows us to write a simplified serialization method:
public class SimpleSerializer {
public static final String serialize(Object obj, List<TalkingXmlAdapter> allAdapters) throws Exception {
Object adaptedObj = null;
for (TalkingXmlAdapter adapter : allAdapters) {
if (adapter.getBoundType().isInstance(obj)) {
adaptedObj = adapter.marshal(obj);
break;
}
}
// serialize
System.out.println("Simple serializing for " + obj.toString());
return "Simply serialized " + obj.toString();
}
}
Using the code e.g. like in the subsequent listing shows the behavior you want:
List<TalkingXmlAdapter> allAdapters = new ArrayList<>();
allAdapters.add(new AppleXmlAdapter());
allAdapters.add(new BananaXmlAdapter());
SimpleSerializer.serialize(new Banana(), allAdapters);
SimpleSerializer.serialize("Lemmon", allAdapters);
SimpleSerializer.serialize(new Apple(), allAdapters);
Output:
Marshalling Banana
Simple serializing for generic.adapter.Banana#659e0bfd
Simple serializing for Lemmon
Marshalling Apple
Simple serializing for generic.adapter.Apple#2a139a55
To sum this up, the solution gives you following advantages:
You don't need reflection which simplifies your code.
You need fewer generic programming in your serialization routine which simplifies your code.
The solution is more safe. Note that no type cast is needed. Every adapter accepts the type Object. However by using the generic method getBoundType() you can ensure the specific runtime type is the correct one. When building your map as in the reflection solution, a wrongly mapped class results in a runtime exception. In the proposed solution the super class TalkingXmlAdapter enforces each adapter to state their correct type by using generics.
The price you pay is:
Introduction of a new super type.
Requires small adaptions for your custom adapters.
Hope that helps!

Generic type invocation using string class names

Hope you can help me with this:
I have ...
a string list of class names called classNameList
a generic class Geography<T>
a static generic method <T> void read(Class<T> cl, Geography<T> geo)
I want to loop through the string class name list and call the generic method for each of these classes.
What I tried but obviously did not work:
for (int i = 0; i < classNameList.length; i++) {
Class<?> myClass = Class.forName(classNameList[i].getName());
Geography<myClass.newInstance()> geo;
read(myClass, geo);
}
Error: myClass.newInstance cannot be resolved to a type
My code runs perfectly for a single call of the generic function:
Geography<ExampleClass> ExampleGeo;
read(ExampleClass.class, ExampleGeo);
Any ideas how I could do this?
UPDATE:
Thanks for the helpful input, still it's hard for me to adopt it to my real code.
So this is the non simplyfied problem:
I do ready in shapefile-Data with a shapefileLoader, for each feature of the Shapefile a class (GuadAgent) is initialized with a predifined class (PlantWind). I have shapefiles in my input-directory with the names of the Classes their features do represent. I want Java to read in the shapefiles and create the respective agent class. (the agents are also placed in a context and a geography..)
Used classes are: ShapefileLoader, Geography, the other classes can be find at the same website
This part is in the main-method:
Geography<GuadAgent> guadGeography = GeographyFactoryFinder.createGeographyFactory(null).createGeography("guadGeography", context, new GeographyParameters<GuadAgent>());
Context<GuadAgent> context = new DefaultContext<GuadAgent>();
FileFilter filter = new FileFilter() {
#Override
public boolean accept(File file) {
return file.getName().endsWith(".shp"); // return .shp files
}
};
String shapefileDir = System.getProperty("user.dir")+"\\input\\shp\\";
File folder = new File(shapefileDir);
File[] listOfFiles = folder.listFiles(filter);
for (File classFile : listOfFiles) {
try {
readForName(classFile,context,guadGeography);
} catch (ClassNotFoundException | MalformedURLException
| FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The static Method that reads in the names:
static <T> void readForName(File classFile, Context<GuadAgent> context,Geography<GuadAgent> guadGeography) throws ClassNotFoundException, MalformedURLException, FileNotFoundException {
String shapefileDir = System.getProperty("user.dir")+"\\input\\shp\\";
String className = classFile.getName().split("\\.(?=[^\\.]+$)")[0];
File shapefile = null;
shapefile = new File(shapefileDir+classFile.getName());
if (!shapefile.exists()) {
throw new FileNotFoundException("Could not find the given shapefile: " + shapefile.getAbsolutePath());
}
switch (className) {
case "PlantWind":
ShapefileLoader<PlantWind> PlantWindLoader = new ShapefileLoader<PlantWind>(PlantWind.class,shapefile.toURI().toURL() , guadGeography, context);
PlantWindLoader.load();
PlantWindLoader.close();
System.out.println(context.getObjects(PlantWind.class).size());
break;
// Todo Add other Agent types
default:
break;
}
How can I get rid of the switch? Although their number is finit, there are very many different agents...
Unfortunately, there's no syntax close to your intention (nice idea though).
The basic problem is that Class.forName() returns an unknown Class<?>, so you need a cast somewhere. It's just a mater of where you put it.
I suggest this approach (which compiles) that bundles up doing a read() based on a class name:
static <T> void readForName(String className) throws ClassNotFoundException {
Class<T> myClass = (Class<T>) Class.forName(className);
Geography<T> geo = new Geography<T>(); // No code shown. Adjust as required
read(myClass, geo);
}
May I also suggest using the foreach loop syntax, for tidier code:
for (String className : classNameList) {
readForName(className.getName());
}
Creating instances from Generic Types at Runtime
I am not entirely clear on what you are trying to accomplish, but at first look it looks like the simplest solution is the best solution.
It could be solved with using a scripting environment ( Groovy, JavaScript, JRuby, Jython ) that could dynamically evaluate and execute arbitrary code to create the objects, but that got extremely convoluted and overly complex, just to create an object.
But unfortunately I think it has a very pedestrian solution.
As long as there is a predefined set of supported types, you can use a Factory pattern. Here I just leverage the Provider<>T interface from the javax.inject/com.google.inject package.
Q26289147_ProviderPattern.java
public class Q26289147_ProviderPattern
{
private static final List<String> CLASS_NAMES = ImmutableList.of("String", "Integer", "Boolean");
private static final Map<String, Provider<StrawManParameterizedClass>> PROVIDERS;
static
{
final ImmutableMap.Builder<String, Provider<StrawManParameterizedClass>> imb = ImmutableMap.builder();
for (final String cn : CLASS_NAMES)
{
switch (cn)
{
case "String":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<String> get() { return new StrawManParameterizedClass<String>() {}; }
});
break;
case "Integer":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
case "Boolean":
imb.put(cn, new Provider<StrawManParameterizedClass>()
{
#Override
public StrawManParameterizedClass<Integer> get() { return new StrawManParameterizedClass<Integer>() {}; }
});
break;
default:
throw new IllegalArgumentException(String.format("%s is not a supported type %s", cn, Joiner.on(",").join(CLASS_NAMES)));
}
}
PROVIDERS = imb.build();
}
static <T> void read(#Nonnull final StrawManParameterizedClass<T> smpc) { System.out.println(smpc.type.toString()); }
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
#Override
public String toString() { return type.getRawType().getCanonicalName(); }
}
public static void main(final String[] args)
{
for (final String cn : CLASS_NAMES)
{
read(PROVIDERS.get(cn).get());
}
}
}
Disclaimer:
This is just a proof of concept example, I would never use a switch
statement like that in production code I would use a Strategy
Pattern or Chain of Responsibility Pattern to encapsulate the logic
of what type to create based on the ClassName key.
This initially looked like a generics problem, it isn't, it is a creation problem.
That said, you don't need to pass around instances of Class<?> you can get Generic Type information off of Parameterized classes at runtime with TypeToken from Guava.
You can even create instances of any generic type at runtime with TypeToken from the Guava library.
The main problem is this syntax isn't supported: Geography<myClass.newInstance()> geo; and I can't think of anyway to fake it other than the Provider implementation above.
Here is a straw man example of how to use TypeToken so that your
parameterized classes will always know their types!
Q26289147.java
import com.google.common.reflect.TypeToken;
public class Q26289147
{
public static void main(final String[] args) throws IllegalAccessException, InstantiationException
{
final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
final String string = (String) smpc.type.getRawType().newInstance();
System.out.format("string = \"%s\"",string);
}
static abstract class StrawManParameterizedClass<T>
{
final TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
}
Notes:
Works great for classes that have a default no arg constructor.
Works better than using straight reflection if there are no default no arg constructors.
Should play well with Guice allowing you to use the ".getRawType()generatedClassto pass togetInstance()` of an Injector. have not tried this yet, I just thought of it!
You can use Class<T>.cast() to do casting that doesn't need #SuppressWarning("unchecked") all over the place.`
You can create a static factory method in Geography (or in any other class):
public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
return new Geography<T>(cls.newInstance());
}
I made a guess at the Geography class's constructor. If I guessed wrong, edit your question to include the constructor(s) in Geography.
You can create a static factory method in Geography (or in any other class):
public static <T> Geography<T> newInstance(Class<T> cls)
throws ReflectiveOperationException {
return new Geography<T>(cls.newInstance());
}
I made a guess at the Geography class's constructor. If I guessed wrong, edit your question to include the constructor(s) in Geography.
Update: I'm not sure what the Geography class is meant to do. If it needs a generically typed object, it might look like this:
public class Geography<T> {
private final T data;
public Geography(T data) {
this.data = Objects.requireNonNull(data);
}
}
If it needs a class, the constructor might look like this:
public class Geography<T> {
private final Class<T> dataClass;
public Geography(Class<T> cls) {
this.dataClass = Objects.requireNonNull(cls);
}
}

De-serializing nested, generic class with gson

Using Gson, I'm trying to de-serialize a a nested, generic class. The class structure looks like the following:
Wrapper object, simplified, but normally holds other properties such as statusMessage, which are returned along with the data-field from the server:
public class Response<T> {
private List<T> data = null;
public List<T> getData() { return this.data; }
}
Simple class, the expected output from data-field above (though as an array):
public class Language {
public String alias;
public String label;
}
Usage:
Type type = new TypeToken<Response<Language>>() {}.getType();
Response<Language> response = new Gson().fromJson(json, type);
List<Language> languages = response.getData();
Language l = languages.get(0);
System.out.println(l.alias); // Error occurs here
Where the json-variable is something like this.
However, when doing this, I recieve the following exception (on line 3, last code example):
ClassCastException: com.google.gson.internal.StringMap cannot be cast to book.Language
The exception ONLY occurs when storing the data from getData() into a variable (or when used as one).
Any help would be highly appreciated.
The problem you're actually having is not directly due to Gson, it's because of how arrays and Generics play together.
You'll find that you can't actually do new T[10] in a class like yours. see: How to create a generic array in Java?
You basically have two options:
Write a custom deserializer and construct the T[] array there as shown in the SO question I linked above
Use a List<T> instead, then it will simply work. If you really need to return an array, you can always just call List.toArray() in your method.
Edited from comments below:
This is a fully working example:
public class App
{
public static void main( String[] args )
{
String json = "{\"data\": [{\"alias\": \"be\",\"label\": \"vitryska\"},{\"alias\": \"vi\",\"label\": \"vietnamesiska\"},{\"alias\": \"hu\",\"label\": \"ungerska\"},{\"alias\": \"uk\",\"label\": \"ukrainska\"}]}";
Type type = new TypeToken<Response<Language>>(){}.getType();
Response<Language> resp = new Gson().fromJson(json, type);
Language l = resp.getData().get(0);
System.out.println(l.alias);
}
}
class Response<T> {
private List<T> data = null;
public List<T> getData() { return this.data; }
}
class Language {
public String alias;
public String label;
}
Output:
be

Using Jackson ObjectMapper with Generics to POJO instead of LinkedHashMap

Using Jersey I'm defining a service like:
#Path("/studentIds")
public void writeList(JsonArray<Long> studentIds){
//iterate over studentIds and save them
}
Where JsonArray is:
public class JsonArray<T> extends ArrayList<T> {
public JsonArray(String v) throws IOException {
ObjectMapper objectMapper = new ObjectMapper(new MappingJsonFactory());
TypeReference<ArrayList<T>> typeRef = new TypeReference<ArrayList<T>>() {};
ArrayList<T> list = objectMapper.readValue(v, typeRef);
for (T x : list) {
this.add((T) x);
}
}
}
This works just fine, but when I do something more complicated:
#Path("/studentIds")
public void writeList(JsonArray<TypeIdentifier> studentIds){
//iterate over studentIds and save them by type
}
Where the Bean is a simple POJO such as
public class TypeIdentifier {
private String type;
private Long id;
//getters/setters
}
The whole thing breaks horribly. It converts everything to LinkedHashMap instead of the actual object. I can get it to work if I manually create a class like:
public class JsonArrayTypeIdentifier extends ArrayList<TypeIdentifier> {
public JsonArrayTypeIdentifier(String v) throws IOException {
ObjectMapper objectMapper = new ObjectMapper(new MappingJsonFactory());
TypeReference<ArrayList<TypeIdentifier>> typeRef = new TypeReference<ArrayList<TypeIdentifier>>(){};
ArrayList<TypeIdentifier> list = objectMapper.readValue(v, typeRef);
for(TypeIdentifier x : list){
this.add((TypeIdentifier) x);
}
}
}
But I'm trying to keep this nice and generic without adding extra classes all over. Any leads on why this is happening with the generic version only?
First of all, it works with Longs because that is sort of native type, and as such default binding for JSON integral numbers.
But as to why generic type information is not properly passed: this is most likely due to problems with the way JAX-RS API passes type to MessageBodyReaders and MessageBodyWriters -- passing java.lang.reflect.Type is not (unfortunately!) enough to pass actual generic declarations (for more info on this, read this blog entry).
One easy work-around is to create helper types like:
class MyTypeIdentifierArray extends JsonArray<TypeIdentifier> { }
and use that type -- things will "just work", since super-type generic information is always retained.

Categories

Resources