So my issue is a bit complex. I've got one User class, which I put into ConcurrentHashMap<String, User>. One class corresponds to one user. Key is user's ID.
I'm using GSON to serialize this ConcurrentHashMap and save the data of my users.
Inside of user class i have multiple variables (ints, Strings, etc.) and few Collections.
Problem is in overwriting the file. 2 out of my 4 ArrayLists are serializing as usual, but when I add another ArrayList, or any collection for that matter, the collection won't show up in a file. However when I add a simple variable such as String or Int, the file updated and appends those values for every user. When new user is being created, those collections show up as nothing happened. I need to add those collections for already exsisting users.
My question is why in hell can't add another ArrayList to the class, and why it's not showing up in the file.
public class User {
private String nickname;
private String id;
private int coins;
...bunch of variables
private int bikes = 0;
private int scooters = 0;
private int goldIngots = 0;
private final ArrayList<Car> cars = new ArrayList<>(); //showing up
private final ArrayList<Hotel> hotels = new ArrayList<>(); //showing up
private final ArrayList<AwardType> awards = new ArrayList<>(); //not showing up
...Constructor
...Getters And Setters
sample of UserClass
collections inside UserClass
how it should look
values are not appending
EDIT
AwardType is an enum. This list cointaining AwardType is not showing up for existing users, only for new ones.
EDIT 1
After adding Gson serializeNulls() option, the list is being added to the file but as a null.
"bikes": 0,
"scooters": 0,
"goldIngots": 0,
"cars": [],
"hotels": [],
"awards": null
As mentioned in the comments you need to add a no-arg constructor (sometimes also called "default constructor") to your class. This constructor may be private (so you don't call it by accident).
This constructor is required for Gson to be able to create an instance and then afterwards update its field values during deserialization. Other constructors with parameters do not work because Gson cannot determine which JSON property matches which constructor parameter, and assuming default values (e.g. 0 and null) for the parameters might not be correct either in all situation.
If no no-arg constructor is detected Gson uses a specific JDK class called sun.misc.Unsafe to create an instance without calling any constructor and without executing any initializer block (including initializing fields). This can lead to issues such as the one you experienced. Additionally the Unsafe class might not be available in all environments. Due to this you should avoid relying on this behavior.
Alternatively you can also create an InstanceCreator for your class, but in most cases it is easier to add a no-arg constructor.
Related
Disclaimer: I am kind of new to Java :)
I am running a bunch of selections on some data and in order to keep track of what happens at each stage of the the selection I use int counters. These counters are all in a data object:
public class MyCounters {
private int counter0;
private int counter1;
...
}
I also have to count how many candidates end up in a given number of categories, which I account for with an enum. To do this I created List<Integer> where the index of the list covers the values of the enum.
private List<Integer> myList;
And later in the code I need a dedicated method to initialise the list with zeros:
for (MyEnum i : MyEnum.values()) {
myList.add(0);
}
In the main code then, once the final category has been assigned, this happens:
myCounters.getMyList().set(myEnum.ordinal(), myCounters.getList().get(myEnum.ordinal()) + 1);
I was suggested that the declaration/initialisation steps can be improved using Lombok's #Builder.Default functionality (or maybe #Singular), but I can't really find out how: in the end I need to initialise a List<Integer> to as many zeros as the values in the enum.
Is it really possible to do this using Lombok's extensions? Or are they targeted for something different?
Lombok's #Builder + #Singular on their own will initialize your List with an empty ArrayList, and that's it (they won't initialize this List with any elements, like zeroes). #Builder.Default could do that (you don't need #Singular then), but I would not follow that path if possible.
I don't fully understand what you want to do, e.g. I don't know if you have only one enum (MyEnum), or if there's more than one enum.
If you have only MyEnum, you'd be much better off using a different data structure than List:
An EnumMap is the easy choice, because it's native to Java:
initialization: EnumMap<MyEnum, Integer> myMap = new EnumMap<>(MyEnum.class)
incrementing: myMap.merge(myEnum, 1, Integer::sum)
final result: myMap.getOrDefault(myEnum, 0)
The best data structure for this, though, would be a multiset. One external library that supports mulitsets is Guava with its Multiset:
initialization: Multiset<MyEnum> myMultiset= HashMultiset.create()
incrementing: myMultiset.add(myEnum)
final result: myMultiset.count(myEnum)
Sticking to your architecture, I guess you have to initialize the List within a class that uses Lombok. That can be achieved as follows:
#Builder
public class Foo {
#Builder.Default
private List<Integer> myList = Arrays.asList(0, 0, 0);
}
Arrays.asList is how you can initialize a List with default values using the standard Java libraries. I know it can be a bit confusing to use a class called Arrays instead of List or Collection, but you can find more information on its Javadoc (here the doc for Java 8). The result of that initialization is a list with three Integers set to 0. You just need to put as many as you need.
The reason to use the annotation #Builder.Default on the myList field is to make the builder constructor aware of the default initialization that otherwise would be skipped by the Lombok's builder.
For brevity, I only included the very specific code for initializing your List and the builder. Note that probably you'd want to use also the Lombok annotations #Data and #AllArgsConstructor in combination with it.
You can find more information on the Lombok official documentation
Honestly, I suggest a different architecture:
Consider not using Enum.ordinal(). It will work fine if you just care about "one" point in time, but if you persist your data somehow then things break apart as soon as you want to compare different persisted data (and the enum changed in the meantime)
Maybe consider LongAdder.
Meaning: use a Map<YourEnumType, LongAdder> to count things. Retrieve the counter, call its add() method, done.
What I'm doing
I'm using Dependency Injection to decouple my classes.
How I'm trying to do it
The class I am making constructs ObjectImplementation (the interface) Objects to hold data and it acts as a sort of container. I'm doing this to parse data and cross reference two data-sets. My problem is that I currently have object construction tied to the data being formatted a certain way. I am using the Factory pattern and a properties file "config.properties".
What I want to be able to do
I want to be able to have the factory take in an array of fields or some other similar type and be able to construct instances of the reflected object type without dependencies on the data. In this case they are Salesrep instances but other times I want to construct Salesrep instances or other classtype instances with different fields filled and different ones null without formatting the data to contain the names of fields.
The end goal
The point is so that I can construct different objects with the same container code. If I want to contain the objects differently I'll simply make a new implementation of the parent interface of this container class.
What I'm thinking is the problem
I've figured out that a Fieldmap was a good idea through previous versions of this question and my own research. Yet there is no way to actually set those fields without having something in the data to match to the Fieldmap
Extra Clarification
I really want to know if I can find a way to achieve my goal without adding field names to the data
//creates new properties object and loads in the file configuration
Properties prop = new Properties();
prop.load(SalesRepbyId.class.getResourceAsStream("config.properties"));
//reflects in the class we wish to use
Class<? extends ObjectImplementation> Classtouse = Class.forName(prop.getProperty("ObjectImplementation")).asSubclass(ObjectImplementation.class);
//initializes the data and some hashmaps to store the data or the methods of the reflected class
ArrayList<String[]> Salesrep_contactlist = FileParser.ReadFile();
Map<String, ObjectImplementation> SalesrepByIdMap = new HashMap<>();
Map<String, Method> MethodMap = new HashMap<>();
//adds in the data (fields) by constructing objects of the reflected type using the ObjectImplementation interface
for (String[] fieldarray : Salesrep_contactlist) {
ObjectImplementation object_to_add = null;
try {
//utilizes the factory pattern to return an instance of the reflected class
object_to_add = Factory.getObjectImpl(prop.getProperty("ObjectImplementation"),fieldarray);
/**
uses a method hashmap to map the name of the method to the Method object.
I did it this way because dynamic variable declarations are not possible and
I wanted to decouple Method declarations from the specific class that has
them. If i just hardcoded in which methods I get from the implementing class
that introduces extra dependencies I don't want.
**/
for (Method method:Classtouse.getMethods()) {
MethodMap.put(method.getName(),method);
}
//same as above but for fields this time
for (Field field:Classtouse.getFields()) {
FieldMap.put(field.getName(),field);
}
//object_to_add is a String[] with the format [Fieldname1:fieldinput1,Fieldname2:Fieldinput2]
//so I want to get this array and get each element, seperate the fieldname and then use that string to access the actual Field object of the same name in FieldMap
String fieldname = object_to_add.get(0).split(":").get(0)
String fieldinput = object_to_add.get(0).split(":").get(1)
Field name_example = Fieldmap.get(fieldname)
name_example.set(String.class,fieldinput)
//This requires the data to have the fieldname in it rather than just the fieldinput (or data). Also it confines the input to be strings because I don't think I can use a generic type to set this field even though potentially I would want to.
There is no way for me to dynamically set Field types without something to go off of in the data or elsewhere. In order to avoid something hard coded like: Salesrep rep = new rep (arg1,arg2,arg3 ...) I needed to use the Fieldmap and be able to match the data coming in to what fields I wanted to set. Since I didn't want to do it by order ex:
List list = new list("bob","800-352-4324","foo#example.com");
int i = 0;
for(i = 0, i > list.size(), i++){
Field field = new Field(//need a name here automatically rather than hardcoded)
field.set(object_to_add,list[i])
i++
}
The above didn't have any reference to the actual name of the Field that I actually use in my class. I didn't want that and then it dawned on me that the first line of my data (which is in CSV format) has the Field names effectively listed. ex:
(in the CSV File) foo.txt:
1: name,phonenumber,email
2: "bob","800-352-4324","foo#example.com"
3: "steve","800-444-4444","annoyingcommercials#example.com"
4: ...
Using this knowledge My solution is to use the first line of my data to specify the field names and their order so that when I take in lines as an array of these strings I can use the first line array as a reference to how to set the fields. I will know that the first element in the array should be the name the second should be the number ect ect. This way I only have to change the first line if I want to change how many fields the data holding class actually has.
puesdocode:
ObjectImpl. Classtouse = refelct in the class to use here from properties file
List(String[]) fieldarray = the raw data taken in and converted to a list of string arrays
String[] firstline = fieldarray.getfirstline()
List(String[]) restoflines = fieldarray.getallotherlines()
for i = 0, i > firstline.size(), i++{
Fieldmap.put(Name of the field from firstline[i], create a new Field object here with the Name);
Field fieldtoset = Fieldmap.get(Name of the field again)
fieldtoset.set(make an instance of the Classtouse here, restoflines[i] which represents the data in the 'Name' column)
}
For some silly reason I had it in my head that there was a way to do this without any change to the data, as if the Factory which created the object could take in arbitrary/generic arguments and somehow just know where each field went. I realized that that was silly because I needed to tell the code how to actually set the fields but In a way that it wasn't hard-coded into the class. This solution puts the dependency on the data so now its not hard-coded into the class. I should have seen this sooner.
Given the following POJO example which stores local fields applicable only to the app running right here, nobody else whom also use the Firebase data:
#IgnoreExtraProperties
public class DispatchModel {
private String isotimestamp;
private String date_time;
private String event_uuid;
//...
private String locallyCreatedValue;
//...constructor, getters, setters
}
Given my Firebase data has the first three fields stored in it, and the locallyCreatedValue is added to my POJO during app runtime, is there a way to automatically fetch all the locally added content from a POJO instance and apply to the new instance when an update from onChildChanged event happens?
As it is right now, I'll have to manually obtain all the local field values and set them on the new instance:
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
DispatchModel newModel = dataSnapshot.getValue(DispatchModel.class);
// get list index of expiring instance
// get instance of old item at list index
// index = ...
// old = ...
// repeat this for every local item :-/
newModel.setLocallyCreatedValue(old.getLocallyCreatedValue);
dispatchList.set(index, newModel);
}
I plan on having quite a few local fields, is this my only option? Are there any functions Firebase offers that makes their automatic object instantiation more friendly to my extras? I'm not keen on creating distinct POJOs to track the Firebase POJOs in parallel. That lends to data errors from decoupled data updates and careful schedules for execution.
If all of your locally created values are expressed as standard getters and setters in your POJO, you are either going to have to copy them manually, or write some fairly intense Java reflection code to inspect the class, somehow figure out which properties are local (annotation? inclusion/exclusion from a known list?) and should be copied over, then actually do that work. You will not be able to get this "for free" using some utility (unless there happens to be some third party library that has solved this specific problem, and I doubt it).
Consider instead maybe storing your locally created values as a Map, then simply copying that map wholesale between objects. But then you have unknown types for the values if they're all different.
Or rewrite your code in JavaScript, which has easy property enumeration. :-)
Can I store text from different text fields in an ArrayList and store it into my Customer property in the Booking class. Right now it's reading every text field and saving them into different properties.
private String flighttime;
private String flightlocation;
private String flightfee;
private boolean car;
private boolean insurance;
private Customer customer;
private void savebookingButtonActionPerformed(java.awt.event.ActionEvent evt) {
Booking customerbooking = new Booking();
Customer cust = null;
try {
if (custnameTF.getText().equals("")) {
throw new EmptyField("Please Insert Customer");
} else {
FileOutputStream fos = new FileOutputStream("Bookings/" + custidTF.getText() + ".txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
cust.setPersonName((custnameTF.getText()));
cust.setPersonSurname((custsurnameTF.getText()));
cust.setPersonID((custidTF.getText()));
cust.setConsultantname(consnameTF.getText());
cust.setConsultantsurname((conssurnameTF.getText()));
cust.setConsulid(considTF.getText());
customerbooking.setFlightlocation(locationCB.getSelectedItem().toString());
customerbooking.setFlighttime(timeCB.getSelectedItem().toString());
customerbooking.setFlightfee(feeCB.getSelectedItem().toString());
customerbooking.setCar(carRB.isSelected());
customerbooking.setInsurance(insuranceRB.isSelected());
oos.writeObject(customerbooking);
oos.close();
fos.close();
custnameTF.setText("");
custsurnameTF.setText("");
custidTF.setText("");
considTF.setText("");
consnameTF.setText("");
conssurnameTF.setText("");
locationCB.setSelectedItem("");
timeCB.setSelectedItem("");
feeCB.setSelectedItem("");
The short answer is "no".
Dealing with property values in a list with an implicit order for mapping to your data object is brittle.
However, it is sometimes very useful to manage properties of objects in aggregate using something like property change support or beans, though an ArrayList is a poor choice of a data structure versus at least a Map of property-name to value or a more thorough data-binding solution such as typed properties to represent keys, pairs to map keys to values, and data structures to store current values and mutations.
There is overhead somewhere that must match your property names to accessors and mutators of your data object classes. This may be done using a library such as for databinding or some custom solution of property-to-field mapping or perhaps storing your state in a Map within the data objects. There are complex ways to do this while maintaining type-safety, but many solutions deal with properties as String and values as Object and throw away the type information and type checks, so that would be one hurdle to implementing it well.
Another related part of your question ought to be change management and only writing things that changed. For example, if one of your fields changes, you don't need to overwrite all the other fields of your data object but only the ones that are different. Whether you move to a property-based update model or stick with explicit use of accessors and mutators, you ought to investigate this also. If going with the property-based approach where you can deal with properties in aggregate or via delegation to handlers, the code in your UI should evaporate and be passed to libraries which could deal with type-safety, change management, firing subsequent change events, and other related concerns.
I want to pass my own POJO class via GWT rpc. This class extengs BaseTreeModel which has set(String value,X property) method.
I create many POJO objects, set their properties but on the client side, when I handle incoming array of POJO I see that only primitive types was serialized correctly. My POJO class
public class Field extends BaseTreeModel implements Serializable
{
private static final long serialVersionUID = 17832464L;
public Field()
{
}
public Field(int id,ArrayList<Double> lons,ArrayList<Double> lats)
{
set("id",id);
set("lons",lons);
set("lats",lats);
System.out.println("adding polygon in field constructor "+id);
for(int i=0;i<lons.size();i++)System.out.println(lons.get(i)+" "+ lats.get(i));
}
}
Debug output showed that all values of ArrayList are correct and came from DB as expected.
But problem is on client side - when I print all properties of each POJO class, only primitive property (integer id) is different for all entries, but ArrayList property is the same for all entries!
Client code:
for(Field field:fields)
{
System.out.println("adding polygon to map "+field.get("id"));
ArrayList<Double> lons = (ArrayList<Double>)field.get("lons");
ArrayList<Double> lats = (ArrayList<Double>)field.get("lats");
for(int i=0;i<lons.size();i++)
{
System.out.println(lons.get(i)+" "+ lats.get(i));
}
}
Init debug output is correct and showed me (surely I publish fake coordinates, values doesn't matter)
adding polygon in field constructor 1
1 1
1 1
adding polygon in field constructor 2
2 2
2 2
But recieve debug output showed me
adding polygon in field constructor 1
2 2
2 2
adding polygon in field constructor 2
2 2
2 2
It looks like GWT use some global map for storing BaseTreeModel properties and last ArrayList value simply overwriting previous values when I do set("lons",lons);
But I looked through BaseTreeModel code and saw that each instance uses it's own map for stroring properties. Note, that primitive types works perfectly. Something wrong in the way I work with nested complex type.
UPD:
I still don't solve this issue, but it seems I localized it. Problem in serialization. I definetely should set some property of BaseTreeModel class, but I don't know exact property to change. When I extract data on server side right after setting it with set("lons"lons) everything is ok.
ArrayList<Double> lons = (ArrayList<Double>)field.get("lons"); // works on server side
But on the client side I saw only last ArrayList which somehow overwrited others.
Problem was solved by replacing ArrayList<Double> to Double[]. It looks like this is serialization side effects. I mean serializing primitives or arrays of primitives is available by default while serializing maps,lists and other complex data structures requires playing with properties and some magic) Anyway, I'm interested how one can perform set("property",ComplexDataStructure) inside BaseTreeModel and I would be very grateful for clearing things up.