I have an object which contains some package-private member variables and I'm adding them to a Google Sheets v4 ValueRange in another object. The current code looks a little bit like this:
List<List<Object>> data = new ArrayList<>();
...
/**
* Sets all the values in the ValueRange member variable
* #return the ValueRange object
*/
ValueRange requestBuilder() {
...
//For each case, add it to the value range
for (int i = 0; i < closedCases.size(); i++) {
data.add(
Arrays.asList(
closedCases.get(i).number,
closedCases.get(i).priority,
closedCases.get(i).firstResp,
closedCases.get(i).accName,
closedCases.get(i).subject,
closedCases.get(i).assigned,
closedCases.get(i).lastUpdated,
closedCases.get(i).daysOld,
closedCases.get(i).jiraCase
)
);
}
vr.setValues(data);
return vr;
}
The question that I'm seeking to answer is, is there any way to do Arrays.asList( closeCases.get(i) ) or add some kind of method on the case object to simply fill all that stuff in, rather than calling out each member variable in the Arrays.asList(). I'm also aware I can use a foreach, but would still need to use the same notation for adding items, which is what I'm trying to avoid.
In case anyone is interested, closedCases is just an ArrayList of an object with some strings and doubles in it.
You somehow need to specify what fields go into this list, in what order. If you want to capture all fields, you could use reflection to iterate over the object (potentially choosing declared, not inherited fields, and potentially choosing only package-private fields), as described here.
But that is not the idiomatic way to do it in Java.
Can you change the definition of the "object which contains some package-private member variables" so that instead it has a Map with key-value pairs?
You could add a List field in the object that is held by closedcases and call that field from inside the loop.
For instance, say the object is Foo,
Inside foo, create a field:
ArrayList<String> allFields = new ArrayList<String>{number. priority …… };
Method:
public ArrayList<String> getAll() {
return allFields;
}
And from inside the loop, just do
data.add(closedCases.get(i).getAll());
If the fields are not just string, you could create different arraylist that holds different types of object, which will increase the list again but could be substantially less that what you gave us.
Related
I would like to know what is the best practice to return 'updated' ArrayList?
For example, if I am adding in a new element, it seems that whether if I did or did not specify the return type (see the addNewA() and addNewB()), the ArrayList in my main() method will still be 'updated' nonetheless.
Should I or should I not specify the return type?
Currently in my client program, most of the methods, I have specified it as void (no return type) and while the overall program still does works as intended, thought I would like to get this clarified and make the necessary changes if necessary.
public class MyClient
{
public static ArrayList<Person> addNewA(ArrayList<Person> myArray)
{
Person jack = new Person("jack", 24);
myArray.add(jack);
return myArray;
}
public static void addNewB(ArrayList<Person> myArray)
{
Person ben= new Person("ben", 19);
myArray.add(ben);
}
public static void main(String[] args)
{
ArrayList<Person> personArray= new ArrayList();
addNewA(personArray); // will return me an array size of 1
addNewB(personArray); // will return me an array size of 2
}
}
In a case like this, you should not return the list and should make your method void.
Different languages have different conventions, but one of Java's is that methods that operate by modifying their arguments (or the object they're called on) should not return values. Returning a value implies to someone using your code that a different object is being returned, since otherwise there is no use in returning an object the caller already has1. A method that is void, on the other hand, couldn't possibly be returning a copied-and-extended list, so it's very clear that it's intended to operate by modifying the list that you give it in the first place.
(By the way, you should also just use List<Person>, and you should pay attention to the warning you get about using new ArrayList() instead of new ArrayList<>().)
1 There is a specific exception to this, called the fluent builder pattern, but it's not easily confused with general code like this.
In java (and most high level strict type languages) Objects are passed by reference and primitives passed by value.
When using the new keyword you create an object.
While primitives (like int, char, double ect) are passed by value (meaning that a copy of the value of the variable will be sent to the invoked function), Object types are passed by reference, meaning that the actual original object is passed to the function.
To sum up - since you are using object here (ArrayList), you don't need a return type since the original object is changing.
I have a statement:
searchResults.sort(Comparator.comparing(WCCTableRowData::getD));
where getD is an accessor in the class WCCTableRowData and searchResults is a list of WCCTableRowData. The WCCTableRowData class has accessors from getA through getZ. I need to be able to set the sort field on the fly from a passed in variable. Is there an elegant way to do this or will I need a series of if statements or similar?
UPDATE 1
Unfortunately, neither approach in the accepted answer worked though I think in general the direction is correct. With approach 2 I get:
With approach 1, row.getField does not pick up the getField method in WCCTableRowData class and I get similar "does not conform to upper bound(s)" error. I think the error is saying that WCCTableRowData class has to implement Comparable?
One way is to add a method in WCCTableRowData that can be given a field name and returns the value of that field.
class WCCTableRowData {
Comparable<?> getField(String name) { ... }
}
String name = "C";
searchResults.sort(Comparator.comparing(row -> row.getField(name)));
If you don't want to modify the class, then you could set up an external map.
Map<String, Function<WCCTableRowData, Comparable<?>>> getters = new HashMap<>();
getters.put("A", WCCTableRowData::getA);
getters.put("B", WCCTableRowData::getB);
getters.put("C", WCCTableRowData::getC);
String name = "C";
searchResults.sort(Comparator.comparing(getters.get(name)));
One way would be to store method references in a map with keys - values of your variable. It would be analogue of switch statement. You can use guava ImmutableMap.<WCCTableRowData, Comparable<?>>of() to make it a bit nicer.
Update
You can explicitly tell that comparator which is constructed by Comparator.comparing(...) is comparing Comparable :
Comparator.<WCCTableRowData, Comparable>comparing(getters.get(name));
Also you can just store comparators of your object (sounds much more reasonable but adds boilerplate), not functions returning comparables:
Map<String, Comparator<WCCTableRowData>> comparators = new HashMap<>();
comparators.put("A", Comparator.comparing(WCCTableRowData::getX));
comparators.put("B", Comparator.comparing(WCCTableRowData::getY));
String name = "C";
searchResults.sort(comparators.get(name));
I know how to add an object:
ArrayList<Object> ob = new ArrayList<Object>();
ob.add (index, some_object);
But let say the object has a field called 'name', how can I change that single field only?
For example:
ob.setName(name);
(I know this does not work.)
There's no special treatment here:
ArrayList<SomeObjectType> ob = new ArrayList<SomeObjectType>();
//...
ob.add(index, some_object);
some_object.setName(name);
The object you wanted to set name for is some_object, not ob.
If you are asking about a situation where you have a List which contains the object you want to update, but you don't yet have a reference to that object, then you will need to first find the object within the list, and then update its name field.
If you do have the reference to the object, then the fact that it is contained in a list is irrelevant: call some_object.setName(name) and the object will have the new name whether it is fetched from the list or directly.
I think maybe you want to see
public void stuff(List<? extends Foo> list, int indexToAlter){
// Get the item in the list at the index and call the appropriate method
list.get(indexToAlter).methodOnFoo();
}
You seem to be confusing the list you are storing your object in and the object itself. Try something like:
Object temp = ob.get(index);
temp.name = new_name;
I understand that immutable means that it is an object that will not change state after it is instantiated. But in this line of code I dont see Final when the array values is declared.
Is this class immutable? Can anyone explain how to find out. Thanks
public class A {
private double[] values;
public double[] getValues(){
return values;
}
}
As other have written this object is considered to be mutable in its state. What it is immutable to is that you can not exchange the array it holds. But you can change the array's content (getValues()[0] = 10;).
To convert this to a immutable object you must use List instead of an array. With List you can use Collections' method unmodifiableList to convert a given list into a version you can savely expose to the outside. If the caller of getValues() uses add or remove on a unmodifiable list it will result into a UnsupportedOpertionException keeping your object save from being modified.
If you need to stick to arrays you need to provide a copy (System.arraycopy) or a clone (clone()) of the array.
Usually a object is considered to be immutable if you can not change its properties (including inherited properties from superclasses. This usually includes the properties values as well but this is a blurred definition.
For example if you have a class that holds a File instance which points to document file and this File instance can not be changed the class is considered to be immutable (the inforamtion it provides never changes) but the document it points to can be mutated and changed every time. So its a blurred line actually (remember in your example you can not change the array but the content of the array).
Yes the code pasted is not having any final keyword associated and has no immutable behavior.
Well i would like to bring forth some key guidelines related to writing immutable classes in java :
1.) Ensure the class cannot be overridden - make the class final, or use static factories and keep constructors private
2.) Make fields private and final
force callers to construct an object completely in a single step, instead of using a no-argument constructor combined with subsequent calls to setXXX methods (that is, avoid the Java Beans convention)
3.) Do not provide any methods which can change the state of the object in any way - not just setXXX methods, but any method which can change state
4.) If the class has any mutable object fields, then they must be defensively copied when they pass between the class and its caller
A a = new A();
a.getValues()[0] = 1.2;
This would work as long as values is not empty. You will however not be able to reassign values to a new array. That is: a.getValues() = new double[5]; will not work.
The class is not immutable, as I can change values, just not reassign it.
Here is a simple verification. the values are initialized to 1,2.
Using the getter and a reference, one is able to change the values inside the first item in the array after the object is created
public class A {
private double[] values;
public double[] getValues() {
return values;
}
public static void main(String[] args) {
A test = new A();
test.values= new double[]{1, 2};
double[] valuesref = test.getValues();
valuesref[0] = 10;
for (int i = 0; i < test.values.length; i++) {
System.out.println(test.values[i]);
}
}
}
This can be avoided if getValues() returns a copy of the array.
I think that a final reference to an array of enums should be immutable.
The uniqueness and singularity of enums is enforced by the JVM, so I believe it is safe to say that they are immutable.
A final reference cannot be changed, so the reference is immutable.
But ... what about the array? Might it still be possible to subvert the array that contains the enum references?
I have a list of enums that correspond to database columns. These column names and their associated data do not change, so ... I would like to have the list as a class variable like so:
static final List<MetaData<Client>> C_COLUMNS =
DataTables.CLIENTS.getTableColumnsAsEnums();
where CLIENTS is the DataTable enum for which a list of column enums is being generated. The method that does this follows:
public <T extends DB> List<MetaData<T>> getTableColumnsAsEnums() {
Class<? extends MetaData> cls = this.columnsEnumToken();
return new ArrayList(Arrays.<MetaData<T>>asList(cls.getEnumConstants())); }
Am I right? This ought to become part of a multi-threaded design, and so I am concerned about the way that making this critical list of static data would render by app very vulnerable ... if it actually were mutable.
But ... what about the array? Might it still be possible to subvert the array that contains the enum references?
Yes. All arrays in Java are mutable, irrespective of how you declare the variable that holds the reference to the array.
If you want to avoid this "risk", then you must not expose the array; i.e. you need to declare it as private. You could then do one (or more) of the following:
Define a static method that will create and return a copy of the array. (Probably not the best option here ...)
Define a static get(int) method that returns the ith element of the array.
Wrap the array in a list (using Arrays.asList) and create an unmodifiable wrapper for it (using Collections.unmodifiableList).
If you want to get the public <T extends DB> List<MetaData<T>> getTableColumnsAsEnums() to return an immutable List you need to use Collections.unmodifiableList()
Also when you are using an unmodifiable list you don't have to worry about the internal array because the toArray method will return an copy of the internal array, not a reference to the internal array itself. This is true for all the Collections.
The REFERENCE is immutable, the content of that reference is not, that's just how things work.
So the following won't work
public enum TheEnum {
//......
}
final TheEnum[] arr = new TheEnum[5];
var = new TheEnum[6];
but this will work
public enum TheEnum {
OPTION_ONE;
//......
}
final TheEnum[] arr = new TheEnum[5];
var[1] = TheEnum.OPTION_ONE;