1

For example:

public void builCarManager(Car car) {
   Manager<car.getClass()> carManager = new Manager<>();
}

Some suggestion to solve this?

EDIT

My main problem is in this :

public class CustomSerializer<T> extends StdSerializer<T> {

    @Override
    public void serialize(T car, JsonGenerator gen, SerializerProvider sp) {
        // I get car.getClass() fine but I need add it
        Manager<T> carManager = new Manager<>();
        // Manager<car.getClass()> carManager = new Manager<>();
    }
}

Here I can't pass CustomSerializer<Lamborgini>.class.

@JsonSerialize(using = CustomSerializer.class)
private Lamborgini car; 
Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
  • You'll need to use reflection, but depending on exactly what you're asking for, even that might not be enough. https://stackoverflow.com/questions/37628/what-is-reflection-and-why-is-it-useful – Gus Feb 28 '19 at 15:35
  • I edited my question... –  Feb 28 '19 at 15:49
  • What does `Manager` do? Why does it need a generic type? And why is `Car` not good enough for it? – Thilo Feb 28 '19 at 15:53
  • Because Manager will manage a different type of car, I wanna know if the question based on my example can be solved with some work around... @Thilo –  Feb 28 '19 at 15:58
  • I think your `CustomSerializer` is not supposed to be generic, it should be a `LamborghiniSerializer extends StdSerializer`. Because it needs to know what methods to call on its input object. – Thilo Feb 28 '19 at 15:58
  • 1
    `Manager` cannot manage different types of car differently based on its generic type, because that generic type is no longer present at runtime. When writing code to serialize an object to JSON you need to either know the exact type at compile-time or use reflection. In both cases, there are no generics involved. – Thilo Feb 28 '19 at 16:01
  • 1
    @Thilo then you might help me how reflection can do that instead generic type? –  Feb 28 '19 at 16:02
  • What is wrong with the default Jackson serializers (configurable by annotations)? Those use reflection. Why do you need to make your own? And if you need to do, why not a dead-simple `LamborghiniSerializer extends StdSerializer`? And I still don't get what the `Manager` is doing during JSON serialization. – Thilo Feb 28 '19 at 16:04
  • @Thilo this idea was because I will have a lot of cars then I can't create a lot of serializer as well... then the idea it is... –  Feb 28 '19 at 16:10
  • @nextsoft, May I ask why do you need class there? What you will do with it? Why do you want to custom serialiser for each type of class? Default configuration/serialiser does not work? – Michał Ziober Feb 28 '19 at 16:36
  • @nextsoft, please check my solution, maybe it help somehow to solve your problem. – Michał Ziober Feb 28 '19 at 17:21
  • @MichałZiober It's OK, I'm going to check it thanks!!! –  Feb 28 '19 at 18:39

3 Answers3

1

The proper way to handle this is to have two classes (e.g. Car and Desk) implement a common interface (e.g. Item) and associate that interface with the Manager class.

public class Instance {
    private static interface Item {
        String getName();
    }

    private static class Car implements Item {
        @Override
        public String getName() {
            return "Car";
        }
    }

    private static class Desk implements Item {
        @Override
        public String getName() {
            return "Desk";
        }
    }

    private static class Manager<T extends Item> {
        private T item;

        public Manager(T item) {
            this.item = item;
        }

        @Override
        public String toString() {
            return String.format("Manager[item=%s]", item.getName());
        }
    }

    public static <E extends Item> Manager<E> buildItemManager(E item) {
        return new Manager<E>(item);
     }

    public static void main(String[] args) {
        Item car  = new Car(), desk = new Desk();

        Manager<Item> manager1 = buildItemManager(car);
        System.out.println(manager1);

        Manager<Item> manager2 = buildItemManager(desk);
        System.out.println(manager2);
    }
}

Output

Manager[item=Car]
Manager[item=Desk]

Another take... instantiating a Manager by passing in a class that represents an Item.

public class Instance {
    public static interface Item {
        String getName();
    }

    public static class Car implements Item {
        @Override
        public String getName() {
            return "Car";
        }
    }

    public static class Desk implements Item {
        @Override
        public String getName() {
            return "Desk";
        }
    }

    public static class Manager<T extends Item> {
        private T item;

        public Manager(T item) {
            this.item = item;
        }

        @Override
        public String toString() {
            return String.format("Manager[item=%s]", item.getName());
        }
    }

    public static <E extends Item> Manager<E> buildItemManager(Class<E> item) throws InstantiationException, IllegalAccessException {
        return new Manager<E>(item.newInstance());
     }

    public static void main(String[] args) {
        try {
            /* Cannot initialize array with generic types... */
            @SuppressWarnings("unchecked")
            Manager<? extends Item>[] managers = new Manager[2];

            managers[0] = buildItemManager(Car.class);
            managers[1] = buildItemManager(Desk.class);

            for (Manager<?> manager : managers) {
                System.out.println(manager);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • Note that you still get a `Manager` here (not a `Manager`) because that is the compile-time type. It is trivial to change it to get a `Manager` (by changing the type of the variable), but it still won't consider the runtime type. But this answer shows how the manager can keep a "witness" around to inspect the intended (runtime) type. – Thilo Feb 28 '19 at 15:46
  • @Mr.Polywhirl I edited my question, could you please help me? –  Feb 28 '19 at 15:50
  • @nextsoft I'll take another look, but I also covered instantiating a class at run-time using reflection. – Mr. Polywhirl Feb 28 '19 at 15:52
  • 1
    @Mr.Polywhirl Awesome answer, please let me try it! thanks a lot if this solves then I'll mark as asnwer –  Feb 28 '19 at 15:56
  • 1
    @Mr.Polywhirl Can you please take a look my edit? I wanna know if what I'm thinking it is possible.. –  Feb 28 '19 at 15:59
1

Let's assume you have below POJO model:

class Car {
}

class Lamborghini extends Car {
}

class Ferrari extends Car {
}

Your Manager class looks like below:

class Manager<T extends Car> {

    private Class<T> clazz;

    public Manager(Class<T> clazz) {
        this.clazz = clazz;
    }

    public String generateCustomCarName() {
        if (Lamborghini.class == clazz) {
            return "Lambooooo!";
        } else if (Ferrari.class == clazz) {
            return "Wild horse Ferrari!";
        }

        return "Not for everyone!";
    }

    @Override
    public String toString() {
        return String.format("Manager[clazz=%s]", clazz.getName());
    }
}

Now, let's create a Store which has one Ferrari and one Lamborghini:

class Store {

    private Car ferrari = new Ferrari();
    private Car lamborghini = new Lamborghini();

    public Car getFerrari() {
        return ferrari;
    }

    public void setFerrari(Car ferrari) {
        this.ferrari = ferrari;
    }

    public Car getLamborghini() {
        return lamborghini;
    }

    public void setLamborghini(Car lamborghini) {
        this.lamborghini = lamborghini;
    }
}

Now, we need to have a class information in runtime. Because of Java's Type Erasure we need to provide class:

class CustomCarSerializer<T extends Car> extends StdSerializer<T> {

    public CustomCarSerializer(Class<T> clazz) {
        super(clazz);
    }

    @Override
    public void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
        throws IOException {
        Manager<T> manager = new Manager<>(_handledType);
        jgen.writeString(manager.generateCustomCarName());
    }
}

Notice that StdSerializer also has constructor which needs class and we can read it later using _handledType. Right now we need to configure our implementations with serialisers using SimpleModule:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;

public class Cars {

    public static void main(String[] args) throws Exception {
        SimpleModule carsModule = new SimpleModule("CarsModule");
        carsModule.addSerializer(Lamborghini.class, new CustomCarSerializer<>(Lamborghini.class));
        carsModule.addSerializer(Ferrari.class, new CustomCarSerializer<>(Ferrari.class));

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(carsModule);
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        System.out.println(mapper.writeValueAsString(new Store()));
    }
}

Above code prints:

{
  "ferrari" : "Wild horse Ferrari!",
  "lamborghini" : "Lambooooo!"
}
Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
0

Yes, you can use generics.

  public <T> void builCarManager(T car) {
    Manager<T> carManager = new Manager<>();
 }

Now you can pass any type of object. The created manager will be of the same type.

Abhijay
  • 278
  • 1
  • 8