简体   繁体   中英

Java generics override static methods workaround

I'm working on a project that requires me to serialize and deserialize generic objects. The way I'm going about this, is defining an abstract class Serializer that implements a toBytes() and a static fromBytes() . All is well with this approach, as I can pass an object instance to a generic class Foo that expects a Serializer subclass, and I can ensure the object knows how to serialize and deserialize itself.

Now my question. Java serialization kinda sucks. I have multiple implementations I'd like to try swapping in and out, and ultimately I'd like the user to be able to decide the format. How would I go about changing the implementation details of Serializer ? I know I can't override static methods, so how would I do this without decoupling Foo and Serializer and not being able to ensure my generic object has the appropriate toBytes() and fromBytes() method in Foo ?

Here is code if anyone is confused:

public abstract class Serializer {
    public static Serializer fromBytes(byte[] bytes) {
        ...
    }

    public byte[] toBytes() {
        ...
    }
}

public class Foo<T extends Serializer> {
    private T t;
    public Foo(T t) {
        this.t = t;
    }

    public void foo() {
        t.toBytes(); //this will polymorph into the correct call because it's called on the object instance and not the Serializer class
    }

    public void bar(byte[] bytes) {
        T.fromBytes(bytes); // I'd like to be able to override this method so I can use different implementations
    }
}

I'm not sure if this is a good approach, but how about using Jackson library and serialize your object as a json node? for example:

@JsonTypeInfo(
          use = JsonTypeInfo.Id.NAME, 
          include = JsonTypeInfo.As.PROPERTY, 
          property = "type")
        @JsonSubTypes({ 
          @Type(value = SoundFile.class, name = "sound"), 
          @Type(value = VideoFile.class, name = "video") 
        })
abstract class File{
    private String id;
    private String type;
    @JsonCreator
    public File(@JsonProperty("id") String id)
    {
        this.id=id;
    }
    public String getId() {return this.id;}
    public abstract String getType();
}

class SoundFile extends File{

    @JsonCreator
    public SoundFile(@JsonProperty("id") String id) {
        super(id);
    }

    @Override
    public String getType() {
        return "sound";
    }


}

class VideoFile extends File{

    @JsonCreator
    public VideoFile(@JsonProperty("id") String id) {
        super(id);
    }

    @Override
    public String getType() {
        return "video";
    }

}

public class GenericApp {



    public static void main(String[] args) {
    ObjectMapper om = new ObjectMapper();

    List<File> files = Arrays.asList(new VideoFile("1"),new SoundFile("2"));

    //serialize
    List<byte[]> fileSerialized = files.stream().map(file->{
        try {
        return om.writeValueAsBytes(file);
        }catch(Exception e) {return null;}
    }).collect(Collectors.toList());

    //de-serialize
    List<File> filesDeSerialized = fileSerialized.stream().map(bytes ->{
        try {
            return om.readValue(bytes, File.class);
        }
        catch(Exception e) {return null;}
    }).collect(Collectors.toList());

    filesDeSerialized.stream().forEach(file->{
        System.out.println("id :"+file.getId()+" - "+file.getClass());
    });

    }

}

this would properly deserialize these objects and print:

id :1 - class com.dsncode.stackoverflow.VideoFile
id :2 - class com.dsncode.stackoverflow.SoundFile

however, you should define a @JsonTypeInfo and a @JsonSubType for all your sub-classes of your Generic Type. Because, by indicating this field, you will indicate to Jackson deserializer, which class should create for your generic type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM