I have a class, lets call it Fruit, and I have a HashMap. I want to be able to initialize a new instance of Fruit, but set to the values in HashMap. So for example:
Map<String, String> map = new HashMap<String, String>();
map.put("name", "Banana");
map.put("color", "Yellow");
Then I want to be initialize a new Fruit instance like so:
Fruit myFruit = new Fruit(map);
or
Fruit myFruit = (Fruit)map;
Is this possible in Java, by means of iterating the Map?
The second is not possible because a HashMap is not a Fruit. You could do the first by providing a constructor that takes a Map<String, String> argument.
public Fruit(Map<String, String> map) {
this.name = map.get("name");
this.color = map.get("color");
}
It seems like you can use reflection for this
Fruit f = new Fruit();
Class aClass = f.getClass();
for(Field field : aClass.getFields()){
if(map.containsKey(field.getName())){
field.set(f,map.get(field.getName()));
}
}
Little old but:
import com.fasterxml.jackson.databind.ObjectMapper;
...
final ObjectMapper mapper = new ObjectMapper();
final Map myObjectMapped = new HashMap();
//fill map
final Class clazz = Class.forName(MyClassToBeConverted.class.getName());
final MyClassToBeConverted convertedObj = (MyClassToBeConverted) mapper.convertValue(myObjectMapped, clazz);
...
Yes, it's possible. But you'd have to write a constructor for Fruit that knows how to pull values -- and which values -- from the map.
public Fruit(Map params) {
this.setColor(map.get("color"));
this.setName(map.get("name"));
}
I have fixed Anni's solution, now it supports inheritance, and static and final fields.
By the way, I have not checked for type mismatches.
public static void populateBean(Object bean, Map<String, Object> properties) throws Exception {
Class<?> clazz = bean.getClass();
while(clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (!Modifier.isStatic(modifier) && !Modifier.isFinal(modifier)) {
if (map.containsKey(field.getName())) {
field.accessible(true);
field.set(bean, map.get(field.getName()));
}
}
}
clazz = clazz.getSuperclass();
}
}
By the way Apache BeanUtils DynaBeans almost does what you want, as far as I remember it supports Java Beans Introspection.
Maybe it could be a little slower in comparison to other solutions, but for not demanding purposes, my code works very well for me (And it is very simple and clean):
public class Utils {
static Object parseHashMapToObject(HashMap map, Class cls) {
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
String jsonString = gson.toJson(map);
return gson.fromJson(jsonString, cls);
}
}
Gson Github: https://github.com/google/gson
You would traverse the map in your constructor and assign the values. If there's an actual library for doing this(almost like a Bean), then I've never heard of it.
Casting of a HashMap to a fruit wouldn't be possible.
The second is not possible but you can create a class that will take a Map as a constructor parameter.
class Fruit{
private Map<String, String> fruitMap;
Fruit(Map<String, String> map){
}
}
Assuming the keys in map correspond to setter methods in the Fruit class, you could use one of Apache bean's utilities like PropertyUtils.
final Fruit f = new Fruit();
for(String key : map.keySet()) {
PropertyUtils.setProperty(fruit, key, map.get(key));
}
For very complicated cases of this you might want to take a look at Dozer. We use Dozer to map very large Maps to very large objects.
Related
I have several classes that are doing the same thing : iterates over a List in an object, and add each items in a private field.
I have two objects : MyCustomObject, that have several fields, and ResultOfQuery, where it has a field called data that is a List<Map<String, Object>>.
For example:
private List<MyCustomObject> myCustomObjectList = new LinkedList();
public void setMyCustomObject (ResultOfQuery resultOfQuery){
ObjectMapper objectMapper = new ObjectMapper();
if(resultOfQuery!= null) {
for (Map<String, Object> map : resultOfQuery.getData()) {
myCustomObjectList.add(objectMapper.convertValue(map,
MyCustomObject.class));
}
}
The problem is that I have other classes that does the exact same method, but with another object instead of MyCustomObject.
So I thought that a good idea would be that all of these classes should extends a class that contains this method, and as a parameter it should take first a resultOfQuery, then a list of any objects, and then a Class.
Does it sounds good, or there is a better way to achieve this?
Also, how to give a list of any object ? I tried List<?>, but this shows me the following error :
Error at list add line
You can achieve type safety with generics and inheritance. If you declare a base class having the common stuff like:
public class BaseClass<T> {
private List<T> tList = new LinkedList<>();
private final Class<T> classT;
public BaseClass(Class<T> classT) {
this.classT = classT;
}
public void setObject (ResultOfQuery resultOfQuery){
ObjectMapper objectMapper = new ObjectMapper();
if(resultOfQuery!= null) {
for (Map<String, Object> map : resultOfQuery.getData()) {
tList.add(objectMapper.convertValue(map, classT));
}
}
}
}
Then it is easy to extend it for each different type, like:
public class MyCustomObjectExtendedClass extends BaseClass<MyCustomObject> {
public MyCustomObjectExtendedClass() {
super(MyCustomObject.class);
}
}
I have renamed stuff because it was decoupled from the MyCustomObject.
You can use private List myCustomObjectList = new LinkedList();
Instantiating a class using a raw type (i.e. without a type parameter, as in List list = new ArrayList(3)), is something you shouldn't do, as it is less type-safe, and is only allowed for backwards compatibility.
Link: Java Generics List and ArrayList with and without Parameters
I have a line of code:
private final Map<MyClassA<?>, MyClassB<?>> myMap = new HashMap<>();
Is there any way to define that map in a way that would tell the compiler that the ? in each case must be the same class?
Something like this?
private final <T> Map<MyClassA<T>, MyClassB<T>> myMap = new HashMap<>();
... which is not legal syntax?
It's just a self-learning question at this point.
FWIW, I want to add a method
public <T> MyClassB<T> getForA(MyClassA<T> a) {
return this.myMap.get(a);
}
But I get a compile error unless I can define myMap to insist that both the key and the value wrap the same type.
As you already figured out, you can't do that if key and value are different for different entries:
map.put(new MyClassA<Foo>(), new MyClassB<Foo>());
map.put(new MyClassA<Bar>(), new MyClassB<Bar>());
(I've taken this requirement from your comment)
What you can do is to write some helper methods, which enforce this constraint:
public <T> void put(MyClassA<T> key, MyClass<B> value) {
// Maybe check at runtime if the constraint is not validated?
map.put(key, value);
}
public <T> MyClassB<T> get(MyClassA<T> key) {
// This will produce an unchecked warning.
return (T) map.get(key);
}
As long as you only access the map through such helper methods (and don't use raw types), the constraint on the map will not be violated, which allows you to write type safe code.
The only part that is not typesafe are those helper methods, and that's where you have to be careful.
You can do something similar if you introduce one static inner class for the type you need. For example:
public class DoubleGenericTest<T> {
public static class MapHolder<Z> {
private final Map<MyClassA<Z>, MyClassB<Z>> myMap = new HashMap<>();
}
private final MapHolder<String> stringMap = new MapHolder<>();
private final MapHolder<Integer> integerMap = new MapHolder<>();
}
class MyClassA<X> {}
class MyClassB<Y> {}
This gives you the class you need to hang the type parameter onto. Maybe not ideal in every situation but it's the only thing I can think of.
Let's say I have simple HashMap:
Map<String, String> map = new HashMap<>();
map.put("field1","value1");
map.put("field2", "value2");
I also have simple java class:
class SimpleClass {
public String field1;
public String field2;
}
What is simplest and most elegant way to create SimpleClass instance with corresponding fields/values taken from map? In this case, resulting SimpleClass instance should get field1 value 'value1andfield2valuevalue2`.
SimpleClass is already defined, now we need to find matching keys in map, if match found, it's value should be assigned to corresponding class field.
In my real application, I will get list of maps and I need to transform it into List<SimpleClass>. Map can contain additional keys, that need to be ommited (if no matching class field is available).
Can I use (for example) Guava to make transoformation like this? I'm on Android so java streams can't be used so far.
[edit]
My attempt:
private SimpleClass mapToObject(Map<String, String> map)
{
SimpleClass result = new SimpleClass();
for(Field f: result.getClass().getDeclaredFields())
{
try
{
f.setAccessible(true);
f.set(result,map.get(f.getName()));
}
catch (Exception e)
{
Log.d("error", e.toString());
}
}
return result;
}
Use ObjectMapper ,
SimpleClass simpleClass = objectMapper.convertValue(map, SimpleClass.class);
I know similar questions have been asked here before but the solutions here, here and here really aren't helpful for me because they deal with a generic List that contains list of a single type.
I've got a POJO like the following that I parse into JSON using Gson.toJson(), save in my database table as a string and get it back using Gson.fromJson().
public class MyClass implements Serializable{
private HashMap<String, List> statistic;
#SuppressWarnings("rawtypes")
public void setStatistic(HashMap<String, List> statistic) {
this.statistic = statistic;
}
#SuppressWarnings("rawtypes")
public HashMap<String, List> getStatistic() {
return statistic;
}
}
Here is the problem. The generic List in the HashMap contains the following information.
List<ClassOne> one = new ArrayList<ClassOne>();
List<ClassTwo> two = new ArrayList<ClassTwo>();
HashMap<String, List> list = new HashMap<String, List>();
list.put("one", one);
list.put("two", two);
Now when I parse the JSON back to MyClass I get back the HashMap with the generic list. But the information about ClassOne and ClassTwo are lost and I just get the information in them as LinkedTreeMap which can't be cast to the mentioned types again by
MyClass myObject = gson.fromJson(jsonString, MyClass.class);
HashMap<String, List> statistic = myObject.getStatistic();
List<ClassOne> one = (List<ClassOne>) statistic.get("one");
List<ClassTwo> one = (List<ClassTwo>) statistic.get("two");
How do I solve this problem? I can't change the structure of my POJO to have two ArrayLists instead of one HashMap so I'm stuck with this.
This question already has answers here:
Java map with values limited by key's type parameter
(6 answers)
Closed 8 years ago.
I'd like to create a HashMap that maps specific class types to one single specific new object.
Later I want to pass the class type and get the reference to that specific object.
Simple example:
Map<Class<?>, ?> values = new HashMap<>();
public <T> t get(Class<T> type) {
return values.get(type);
}
//pet and car do not share any interface or parent class
class Pet;
class Car;
//error: not applicable for arguments
values.put(Pet.class, new Pet());
values.put(Car.class, new Car());
usage:
values.get(Pet.class);
How can I create such a generic hashmap and lookup function accordingly?
You need to store some type of object, if you want to be able to put anything else as Object in the map, so start off with this:
Map<Class<?>, Object> values = new HashMap<>();
This has to be done this way, because ? is not a concrete object, but Object is, for the type the map stores.
So the following snippet works, without warnings:
Map<Class<?>, Object> values = new HashMap<>();
values.put(Pet.class, new Pet());
values.put(Car.class, new Car());
Now the trick is to get objects, we do it as follows:
#SuppressWarnings("unchecked")
private <T> T get(Class<T> clazz) {
return (T)values.get(clazz);
}
Now your goal is to ensure that the map contains pairs that provide no errors on runtime. If you put a new Car() instance with Car.class , then you are going to get errors.
So the following example code:
values = new HashMap<>();
values.put(Pet.class, new Pet());
values.put(Car.class, new Car());
System.out.println("get(Pet.class).getClass() = " + get(Pet.class).getClass());
System.out.println("get(Car.class).getClass() = " + get(Car.class).getClass());
will print:
get(Pet.class).getClass() = class testproject8.Pet
get(Car.class).getClass() = class testproject8.Car
I think you are after a heterogeneous container. There is no easy way to create such a container in Java without any explicit casts.
I think the easiest way is to put your map in a class -
public class Container {
private Map<Class<?>, Object> values = new HashMap<>();
public <T> T put(Class<T> klass, T obj) {
return klass.cast(values.put(klass, obj));
}
public <T> T get(Class<T> klass) {
return klass.cast(values.get(klass));
}
}
And use the class as follows -
Container c = new Container();
c.put(Pet.class, new Pet());
c.put(Car.class, new Car());
Pet p = c.get(Pet.class);
You can use Object as the value type:
Map<Class<?>, Object> values = new HashMap<>();
You don't know the type of the values anyway.
Then, if you want to be sure that the object inserted via put is of the class that serves as key, you may create safePut() the following way:
public <T> T safePut(Class<T> key, T value) {
return (T) values.put(key, value);
}