Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java doing different things based on generic type

Tags:

java

generics

As of now I have an object that looks similar to the below.

public class DataField implements JSONable
{
    public final String name;
    public final Class<?> typeClass;
    private Object data;

    public <T> DataField(String name, Class<T> typeClass)
    {
        this.name = name;
        this.typeClass = typeClass;

        data = null;
    }
    public <T> T getData(Class<T> passClass)
    {
        if(!typeClass.equals(passClass))
            throw new TypeMismatchException("Type mismatch");
        return passClass.cast(data);
    }
    public <T> T setData(T obj, Class<T> passClass)
    {
        if(!typeClass.equals(passClass))
            throw new TypeMismatchException("Type mismatch");
        data = (Object) obj;
    }
    public String toJSON()
    {
        //The part that bugs me
        StringBuilder sb = new StringBuilder();
        switch(type)
        {
        case "myPackage.DataField":
            if(isArray)
                return TakesADataObjOrCollectionOfDataObjs(sb,Arrays.asList(get(DataObj[].class)));
            else
                return TakesADataObjOrCollectionOfDataObjs(sb,get(DataObj.class));
        break;
        default:
            if(isArray)
                TakesAnObjectOrCollection(sb,Arrays.asList(get(Object[].class)));
            else
               TakesAnObjectOrCollection(sb,get(Object.class));
        break;
        }
        return sb.toString();
    }
}
//I did not include code for DataObj but it has a HashMap<String,DataField>
//fields on it and two functions for getting and setting field data 
//without actually divulging the field objects themselves.

Background: The problem is with this last toJSON function. I have two different functions (listed here as TakesADataObjOrCollectionOfDataObjs and TakesAnObjectOrCollection) which do different things based on if the object implements JSONable or not. Now I cannot simplify these all down to one function and let the compiler choose because with type erasure, I cannot have a function that takes both Collection<?> and Collection<? extends JSONable> as they have the same function signature.
It was easy when I only had to differentiate between just Object and DataObj types (it's a pretty simple check and all I have to do is pass in the Class<?> of the type we're working with) but now I need to start storing List<?> and List<? extends JSONable>.

Current Solution: I do have a bit of code for this solution but it is not the same as what's pictured above. The current solution is to just include the type of the generic that I'm using as well like having typeClass and a genericTypeClass. The problem with this is it starts becoming really messy because:

  • All appropriate methods that take just typeClass would need two methods now, one that takes both typeClass and genericTypeClass and one that takes typeClass but sets genericTypeClass to null.
  • I'd having much more trouble performing type checks (for get and set) if I could even do it at all for some instances.
  • The toJSON switch statement would be filled with more cases, one for each supported generic, and then under each of those, checking the genericTypeClass to see if it's a DataObj or not.


Question: Is there any other way that I could create this class to do what I want (store differing data yet still be able to output different JSON based on the type the DataField holds)?

Hopefully just explaining the situation and not asking 3 or 4 other related questions will stop me from falling prey to the XY Problem by just asking this.

like image 971
Cobertos Avatar asked Nov 02 '22 03:11

Cobertos


1 Answers

I would recommend you to use Jackson to convert your objects into JSON Strings.

It is really easy to get to work (seriously) and it could save you a lot of time and potential errors... It also supports some features that you could use, I'm thinking about the serialization of Lists for example :)

Here is a link to an interesting blog to get you going with Jackson Json.
And here is a link to the download page for Jackson Json.


Below is a little example I just made to show you how to work with it:

public class Main {
    public final String mainStr = "Hello";
    
    public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
        final ObjectMapper om = new ObjectMapper();
        System.out.println("Serialized Main: " + om.writeValueAsString(new Main()));
        System.out.println("Serialized A: " + om.writeValueAsString(new A()));
        System.out.println("Serialized AA: " + om.writeValueAsString(new AA()));
        System.out.println("Serialized B: " + om.writeValueAsString(new B()));
        System.out.println("Serialized List of A: " + om.writeValueAsString(Collections.singletonList(new A())));
    }
    
    static class A extends Main {
        public final String aStr = "World";
        public final int aInt = 42;
    }
    
    static class AA extends A {
        public final String aaStr = "Foo";
    }

    static class B {
        public final String bStr = "Bar";
        public final boolean bBool = true;
    }
}

Ouptut:

Serialized Main: {"mainStr":"Hello"}
Serialized A: {"mainStr":"Hello","aStr":"World","aInt":42}
Serialized AA: {"mainStr":"Hello","aStr":"World","aInt":42,"aaStr":"Foo"}
Serialized B: {"bStr":"Bar","bBool":true}
Serialized List of A: [{"mainStr":"Hello","aStr":"World","aInt":42}]

If you have troubles making it work, we could discuss it on the chat and I could give you more help there.

Cheers!

like image 177
ccjmne Avatar answered Nov 15 '22 04:11

ccjmne