I have an object that's designed to paginate a list and send a page of the results to a user. However, I want to be able to use Functions to pass behavior to this object. The behavior will be used by the object to dynamically generate the content of the lines using methods inside the objects in the list.
public class InteractiveJSONList<T> {
private final List<T> list;
private List<Function<T, Object>> formatArgFunctionSequence;
// ... Irrelevant code truncated.
// Constructor
public InteractiveJSONList(List<T> list) {
this.list = list;
}
}
I'm using generics to define the type of list, and likewise, the input type of the function. However, when attempting to define the Function as input to a method, NetBeans doesn't seem to recognize the input object as the correct type (it simply treats it as an instance of Object).
This code snippet resides inside an instance method in InteractiveJSONList. The method is designed to extract information from each item in the list and use the results as format arguments for String.format later on.
for (T item : itemsList) {
index++;
// Create a new JSON Message.
new JSONMessage(resultLinesBaseMessage)
.setFormatArguments(index, stringFunctionSequence.stream()
.map(o -> o.apply(item))
.collect(Collectors.toList())
.toArray(new Object[stringFunctionSequence.size()]))
}
I'm using Java 8's Stream methods to apply the function using each item in the list as input, then collecting the results and submitting them as an array for a varArgs method to use later in String.format.
Finally, this is an attempt to implement the mechanism.
List<String> data = ... //Using Strings at this point, but could be anything.
new InteractiveJSONList<String>(data)
.setStringBuildingSequence(input -> input.substring(0,5));
I'm only using String and its method substring as an example for how I want this mechanism to work. Certain objects in this list will be more complicated than Strings, and will have getters for variables inside the object. I want to define the behavior required to get variables from within the items in this list, and then use them to generate dynamic results. However, NetBeans won't recognize input as a member of String, and I subsequently am not able to use String's methods. input is only recognized as an object, even though InteractiveJSONList's generic type T is defined as a String when the object was created.
Am I setting up generics incorrectly? I might be led to think it's as simple as that, however, I tested this instead:
List<String> data = ... //Using Strings at this point, but could be anything.
Function<String, Object> testFunction = input -> new InteractiveText(input.substring(0, 8));
new InteractiveJSONList<String>(data)
.setStringBuildingSequence(testFunction);
And it compiled and ran correctly.
Any advice? Also, if this is a terrible practice for my desired purpose, please let me know. Thank you!
Related
For instance I have two Arraylists with different data types.
ArrayList<Integer> intValues = new ArrayList<Integer>();
intValues.add(1);
intValues.add(2);
intValues.add(3);
ArrayList<String> strValues = new ArrayList<String>();
strValues.add("4");
strValues.add("5");
strValues.add("6");
If both of these lists contained the same data type objects, I would easily call addAll function;
intValues.addAll(intValues2);
But of course if I try to call addAll function with these different type lists, compiler warns me with incompatible types: ArrayList cannot be converted to Collection<? extends Integer> warning.
So I have to create a bad solution like;
for(String s: strValues)
{
intValues.add(Integer.parseInt(s));
}
Is there a better way to do this, I mean, creating a class which implements List, overriding addAll function etc. so I will be able to call;
intValues.addAll(strValues);
And intValues list will contain 1,2,3,4,5,6.
Edit: I really don't want to store String values in an Integer array, I have to deal with some creepy old code at the moment and I need a Collection to hold some differend kinds of classes, trying to create a Constructor for those objects, this integer-string scenario is just a simple way to introduce my problem.
Let me tell you about my current situation with another integer-string like scenario:
Creepy class A is car, it holds car's weight, price, color, engine type.
Creepy class B is watch, it holds watch's still type, movement type, price, lug size etc.
I am trying to create a holder class, so it will hold those classes and adding a few functions (for example, overriding compare method makes the holder class to compare prices of different classes).
Now I think I have to create a HolderHolder class which implements List so I can call holderHolder.addAll(carsList) and holderHolder.addAll(watchesList), and it will hold these as Holder objects and yes, this does not look pretty.
You act as if what you want is self-evident and logical. It really isn't. "4" and 4 are entirely unrelated, and expecting that your list of integers now has a value 4 when you call addAll with "4" is, as a consequence, as bizarre as expecting your list of movies to gain 'Forrest Gump' when you call .addAll(colorsOfTheRainbow) on that, because in your mind, 'green' is so incredibly similar to 'Forrest Gump', that you might as well assume that. (Here, 'green' is "4" and 'Forrest Gump' is 4).
So let's do some work and make this more sensible:
That 'assumption' (that "4" is so similar to 4, that you want .add("4") to just mean that 4 shows up in your list) needs to encoded, explicitly, in your code. Now it makes sense, and now you can write a function that maps Green to Forrest Gump and use it for that example just the same - we've generalized the principle.
What you're really talking about is a mapping function that maps an element of your List<String> (so.. a String) to a type that your target list is of (Integer), and you then want the operation: Take this list. Map every value in it with my mapping function. Then, add all the mapped values to this other list.
That makes perfect sense.
So, write that.
List<Integer> intValues = ...;
strValues.map(Integer::valueOf).forEachOrdered(intValues::add);
Looks like bad smell.
One bad Solution can be an own implementation of an List with Type Object. But than you have to cast and work with the Classes of the primitive types.
I think i every case you have to parse or cast. That cost to much of performance just for easy call of addAll.
I would think about the incoming data and why they have to be the same but in different types?
Edit:
If i get to know it correct. It is a little bit hard to understand without more detailed infos.
But maybe you can write an mapper class to map thoose old creepy classes in one new class an then you can put these new class in an collection and can compare all by overriding equals.
public class CreepyClassMapper
{
public CreepyClassMapper(Car aCar, Watch aWatch)
{
}
#override
private boolean equals(Object obj)
{
// maybe add an instance check
CreepyClassMapper other = (CreepyClassMapper) object;
// do your compare stuff
return true;
}
}
if i were you, i will create a function like this in util class
public void append(ArrayList<Integer> intValues, ArrayList<String> strValues){
}
I need to create method transformer which can to change type of objects.
For example: I have List<Integer> sample and what change to List<String> sample.
How write method to do this? I really don't understand , because I know apache already have rhis method -> CollectionUtils.transform(); (full way - org.apache.commons.collections4.CollectionUtils.transform(); , but i can't understand how it work and how to write method whjich include all. In example(
http://www.baeldung.com/apache-commons-collection-utils,
http://apachecommonstipsandtricks.blogspot.ru/2009/01/examples-of-functors-transformers.html) i saw how in main methods folks override rhis method, but how to write just method and how to use it?
According to your link (http://www.baeldung.com/apache-commons-collection-utils), you need to use CollectionUtils.collect method, and give a Transformer as the 2nd argument to the method. I guess that the collect function iterates over the elements, and for each element calls the transform methods that takes the element from type A and returning an element from type B.
Overall it will look like that:
List<String>listOfString = CollectionUtils.collect(listOfIntegers, new
Transformer<Integer, String>() {
public String transform(Integer numbers) {
//Do whatever you need to transform it to a String and return the String.
return number.toString() // a suggestion
}
});
Another way is to just use the stream mechanism that java 8 has and use the map method that maps each element to something else.
listOfNum.stream().map(integer -> integer.toString()).collect(Collectors.toList());
I am trying to create an object with the following constructor
public PageBreak(String sheetName, ArrayList<Integer> rowPageBreaks, ArrayList<Integer> columnPageBreaks)
{
this.sheetName = sheetName;
this.rowPageBreaks = rowPageBreaks;
this.columnPageBreaks = columnPageBreaks;
}
In another method, I do the following call:
pageBreaks.add(new PageBreak(teamResultSheet.getName(),
new ArrayList<Integer>().add(teamResultSheet.getRowPageBreak()), null));
I then get the following error message: The constructor PageBreak (String, boolean, null) is undefined.
How can I create an ArrayList<Integer> on the spot?
You're going to want to populate your list before you attempt to use it. Here's what's happening:
You instantiate a new ArrayList<Integer>.
You immediately call add on that list.
The result of add is boolean (and consequently always returns true).
The boolean result is what is interpreted, and not the list.
While Makoto's answer is correct and explains why you are getting this error, and gives you sensible advice to create your ArrayList before you use it, you might want to know if there is any other way you can make your code more succinct.
Unlike more recent languages, such as Groovy or Kotlin, Java unfortunately does not have List literals as a language syntax. There have been some attempts to hack around this limitation; what you may have been attempting with your code is the double brace initialization idiom:
pageBreaks.add(new PageBreak(teamResultSheet.getName(),
new ArrayList<Integer>() {{add(teamResultSheet.getRowPageBreak());}},
null);
Although this may look cute, it does have its drawbacks, as described in the above link.
Do you really need to pass actual ArrayLists to your constructor? Why not make it take Lists, which will make it more flexible:
public PageBreak(String sheetName, List<Integer> rowPageBreaks, List<Integer> columnPageBreaks)
{
...
}
Then you have the freedom to pass it ArrayLists, as before, or any other kind of List:
pageBreaks.add(new PageBreak(teamResultSheet.getName(),
Arrays.asList(teamResultSheet.getRowPageBreak()),
null);
This looks more succinct and doesn't have the former's drawbacks; however, the list is fixed-size and immutable.
If you only want a single-element list, it's more efficient to use the following:
pageBreaks.add(new PageBreak(teamResultSheet.getName(),
Collections.singletonList(teamResultSheet.getRowPageBreak()),
null);
In java, I have a method which is modifying the contents of a list. Is it better to use:
public List modifyList(List originalList) { // note - my real method uses generics
// iterate over originalList and modify elements
return originalList;
}
Or is it better to do the following:
public void modifyList(List originalList) {
// iterate over originalList and modify elements
// since java objects are handled by reference, the originalList will be modified
// even though the originalList is not explicitly returned by the method
}
Note - The only difference between the two methods is the return type (one function returns void and the other returns a List).
It all depends on how you are using your List - if you are implementing some kind of list and this is the non-static method of your List class, then you should write
public List modifyList() // returning list
or
public int modifyList() // number of elements changed
If it's method outside this class
About performing operations on List or its copy: you should consider desired bahaviour and your expectations - the most importantly - do I need "old" list copy?. Deep copying list can be a little overhead. Shallow copy will unable you to perform operations on certain elements of list (i.e. changing it's attributes - if they are objects) without affecting the "old" list.
About returning void: it's good practise to return changed list (or at least number of changed elements) which will allow you to chain methods invocations, if not needed you can always ignore it.
If you are just manipulating the list, it entirely depends on temperament. Some people(including me) would argue is easier to read code using the first option (and it allows for chaining as pointed out by Adam, if you want that sort of thing).
However, keep in mind that its not really a reference being passed in. Its a pointer really. Hence, if you reinitialize the originalList instance for some reason, as in putting a
originalList = new ArrayList();
in your method body. This will not affect the list you actually passed into the method.
In my opinion you should only encourage method chaining with immutable classes.
If your function mutates an object it is too easy to do it accidentally if in a chain of methods.
One possible benefit of Option 1 is that it can accept a null List. For example, if you are collecting Foos, and generally create a brand new List, but want the option to add to an existing list. e.g. (note name of method as well)
public List<Foo> appendFoos(List<Foo> in) {
if (in == null)
in = new ArrayList<Foo>;
// now go do it, e.g.
in.add(someFooIFound);
return in;
}
and, if you wish, add an explicit no-arg "get" method as well
public List<Foo> getFoos() {
return appendFoos(null);
}
Now, in Option #2, you could do this by having the user create a new, empty ArrayList and passing that in, but Option #1 is more convenient. i.e.
Option 1 Usage:
List<Foo> theFoos = getFoos();
Option 2 Usage:
List<Foo> theFoos = new ArrayList<Foo>();
appendFoos(theFoos);
As List is mutable, so second method is better. You don't need to return modified List.
Java code:
Transformer TRANSFORM_TO_INTEGER = new Transformer() {
public Object transform(Object input) {
Integer i = new Integer((String) input);
return i;
}
};
String begin = "1,2,3,4,5";
List strList = Arrays.asList(StringUtils.split(begin, ","));
CollectionUtils.transform(strList, TRANSFORM_TO_INTEGER);
This code would throw ArrayStoreException:
java.lang.ArrayStoreException
at java.util.Arrays$ArrayList.set(Arrays.java:2360)
at java.util.AbstractList$ListItr.set(AbstractList.java:488)
at org.apache.commons.collections.CollectionUtils.transform(CollectionUtils.java:434)
Why is that?
The ArrayStoreException occurs when an attempt is made to store an object of an incorrect type is placed into an array.
What is the code doing?
In the example code given, the CollectionUtil.transform method takes a Collection and performs an in-place transform of the elements, which means that Objects are taken out of the original Collection (such as a List) and placed back into the same Collection.
The code for the Transformer takes a String and transforms it into a Integer -- this is core issue here -- the type of object is changing when the transform is applied.
What could be going wrong?
As previously mentioned, CollectionUtil.transform will use the given Transformer and perform the transformation on each element in the Collection and store it back to the original Collection, which is the strList.
I suspected that the List created by Arrays.asList is being backed by a String[], as that would be the likely be the source of the ArrayStoreException. Running the debugger confirmed that, as it was backed by a String[5]. (Using Eclipse, running on JRE 6 on Windows.)
What does the this example illustrate?
This is a prime example of how the lack of generics allows code that is not typesafe to be written, and consequently, a problem arises at runtime. If the code had been written with generics (and Apache Commons Collection supported it) these types of problems would be caught at compile time.
The bottom line -- one cannot transform type elements in a List -- if the List contains Strings, the Transformer.transform should only return a String.
What can be done?
As an alternative, Google Collections has a Collections2.transform method, which takes a given Collection and returns a Collection transformed by a Function.
This method supports generics, so it is typesafe, and the fact it returns a new Collection means that the types can change through the transformation.
The Arrays.asList method uses the same supplied array as the backing array for the new list instance. The API code looks like the following:
public static <T> List<T> asList(T... a) {
return new ArrayList<T>(a);
}
The call to StringUtils.split creates a String[] which is passed to the Arrays.asList method. This would restrict the type of elements that you can insert into the new list instance to only String objects.
CollectionUtils class supports 2 different types of transformations:
In place transformation - In this case the input collection instance gets updated with the transformed values. All the transform() variants fall in this category. When using Collection types which are backed by arrays (e.g. ArrayList) the transformation can be successful only when the transformed values are type compatible with the backing array type. This explains the exception that you are seeing.
Out of place transformation - In this case the input collection is never updated. Instead the transformed values are collected in a separate collection instance. All the collect() variants fall in this second category. The overloaded versions of collect() method either accepts the output collection as an argument or if no separate collection is specified creates a new list instance to collect the transformed values.
Based on the scenario that you are trying to address you should go with the 2nd type of transformation and call one of the collect() variants.