I have List like below code snippet:
List<String> nameList = new ArrayList<>();
nameList.add("Robert");
nameList.add("Tom");
nameList.add("Curran");
//robert, tom, curran
Now I want to modify these list values using reflection API in Java. Output something like below if we print list:
//xxx,xxx,xxx
You can achieve this using Java reflection as follows:
The class that contains nameList:
public class SampleClass {
List<String> nameList;
}
This is a simple test method using SampleClass:
#Test
public void testReflection() throws NoSuchFieldException, IllegalAccessException {
SampleClass sample= new SampleClass();
sample.nameList = new ArrayList<>();
sample.nameList.add("Robert");
sample.nameList.add("Tom");
sample.nameList.add("Curran");
Field fieldList = SampleClass.class.getDeclaredField("nameList");
fieldList.setAccessible(true);
List<String> listToModify = (List<String>) fieldList.get(sample);
for (int i =0;i<listToModify.size();i++) {
if(listToModify.get(i).equals("Robert"))
listToModify.set(i, "xxxxxx");
};
System.out.println(sample.nameList.get(0));
System.out.println(sample.nameList.get(1));
System.out.println(sample.nameList.get(2));
}
Output:
xxxxxx
Tom
Curran
NOTE: I don't recommend using setAccessible(true), it destroys OOP encapsulation.
You can add the custom annotation concept to solve this problem. By creating custom annotation you can simply modify those fields only whichever is required. Fields that need to be modified will be annotated with custom annotation and the rest are ignored. For reference: Java reflection.
Related
I am trying to split String values based on de-limiter and trim them before putting them in a list.
I am able to split the values, could you please suggest how can be trim the list.
#Value("#{'${string-values}'.split(',')}")
private List<String> textList;
The problem it seems is, Split returns a list and I need to invoke trim() before storing them in the variable.
Check
Java - Split and trim in one shot
#Value("#{'${string-values}'.split('\\s*,\\s*')}")
private List<String> textList;
Better to provide no space between values in properties file.
To put a check in code it can be done in this way.
private List<String> textList;
public YourController(#Value("${string-values}") String propertyFromFile) {
this.textList = new ArrayList<>();
Arrays.asList(propertyFromFile.split(",")).forEach(b-> this.textList.add(b.trim()));
}
I think it may be better to use #Configuration and then process that instead of doing like this, however you can add a new annotation on top of value annotation and use that annotation to process the list. For example
#Target(value = {ElementType.TYPE})
#Value
public #interface Trim {
//Override the method you want to override
}
Public TrimPricessor {
//Implement the annotation method here
}
Since you are using Spring Boot, use:
#Component
#ConfigurationProperties(prefix = "foo.bar")
public class MyConfig {
private List<String> list = new ArrayList<>();
public List<String> getList() {
return this.list;
}
public void setList(List<String> list) {
this.list = list;
}
}
foo.bar.list= a , b, c
The list entries are trimmed.
I am unit testing a Spring Boot service method. I have a void method with no args. It queries a db and gets a List. I can mock and assert on that. Then it runs through the list and changes object fields according to some condition. How can I check the contents of that list so I can assert on them?
#Transactional
public void someMethod() {
List<Person> myList = repository.findAllByAge(0);
myList.forEach(person -> person.setAge(18));
}
How can I assert on myList to check that each Person in myList with age 0 is getting set to age 18? I currently have this in my test class...
#Test
public void someMethod_withSuccess() {
List<Person> testList = new ArrayList<>();
testList.add(toPersonEntity(createPersonDto().setAge(0)));
when(mockRepo.findAllByAge(0)).thenReturn(testList);
mockService.someMethod();
}
You can call that method through Reflection API.
btw, if you need to test this method, it probably has to be redisigned to be public or not tested at all.
it is as easy as this
testList.forEach(person -> assertThat("person "+testList.indexOf(person),person.getAge(),equalTo(28)));
I have the following code, where each url in listOne is tested with the method testItem:
#Parameters(name="{0}")
public static Collection<Object[]> data() throws Exception {
final Set<String> listOne = getListOne();
final Collection<Object[]> data = new ArrayList<>();
for (final String url : listOne) {
data.add(new Object[] { url });
}
return data;
}
#Test
public void testItem() {
driverOne.makeDecision(urlToTest);
assertTrue(driverOne.success(urlToTest);
}
What if I now wanted to add a second list, listTwo, and run a test method defined as follows on JUST the items of listTwo (but not listOne?)
#Test
public void testItemAlternate() {
driverTwo.makeDecision(urlToTest);
assertTrue(driverTwo.success(urlToTest));
}
That is, I want driverOne to make the decision for all URLs in listOne, and I want driverTwo to make the decision for all URLs in listTwo. What is the best way to translate this to code? Thanks.
Cited from: https://github.com/junit-team/junit/wiki/Parameterized-tests
The custom runner Parameterized implements parameterized tests. When running a parameterized test class, instances are created for the cross-product of the test methods and the test data elements.
Thus, I assume No, that's not possible.
If you want to do such a thing I guess that you either
(1) will need to construct two test classes one for each test to be executed with the first collection and one for each test to be executed with the second collection or
(2) will need to use another mechanism besides the #Parameters annotation, maybe hand-crafted.
You could include some test set identifier in your test set data itself, and then use the org.junit.Assume class to help:
#Test
public void testItem() {
org.junit.Assume.assumeTrue(testSetId.equals("TEST_SET_1"));
driverOne.makeDecision(urlToTest);
assertTrue(driverOne.success(urlToTest);
}
#Test
public void testItemAlternate() {
org.junit.Assume.assumeTrue(testSetId.equals("TEST_SET_2"));
driverTwo.makeDecision(urlToTest);
assertTrue(driverTwo.success(urlToTest));
}
As a completely different answer, there exists junit-dataprovider
I don't see why I cant do this, without getting an error saying "Songs cannot be resolved or is not a field". I am a noob by the way, trying to learn this stuff :) Thanks in advance for you time, and please tell me if you need more information.
import java.util.ArrayList;
public class Band {
public String bandName;
public ArrayList<String> musician = new ArrayList<String>();
public ArrayList<String> songs = new ArrayList<String>();
// Constructor
public Band(String bandName) {
this.bandName = bandName;
}
public void getBandSongs(String bandName){
for (String s : bandName.songs) { <<<<<<<<<<<ERROR HERE
String rating = s.substring(0,1);
s = s.substring(1);
System.out.println("Rating: " + rating + " - Song name: " + s);
}
}
}
Sometimes the errors that the IDE gives you are misleading, but this one is quite clear.
So, let's analyse it:
"Songs cannot be resolved or is not a field"
So that should ring up an alarm. First thing you should do is look at the type that is your variable bandName.
public void getBandSongs(String bandName){
for (String s : bandName.songs) ...
It's a String! Of course you won't be able to access a field "songs" of a type String.
Maybe in your method signature you meant to have the following:
public void getBandSongs(Band band)
In that case, you should be able to access band.songs just fine.
Or you could have meant the following:
for (String s : this.songs)
That means you would be accessing the "songs" variable of the object instantiation of the class Band.
In summary:
The attributes that you define in your class:
public String bandName;
public ArrayList<String> musician = new ArrayList<String>();
public ArrayList<String> songs = new ArrayList<String>();
can be accessed through a variable that is of type of that class (in this case Band).
Hope this has made it a bit clear. Are you following any book in particular? I recommend the O'Reilly series. Good luck!
ps: I don't want to add too much since you are starting. But I advise you to read up on "encapsulation". That means that, unless strictly necessary, your should, by default, make your class's arguments private and let other classes access them through "getters" and "setters". Such that:
public class Band {
private String bandName;
private ArrayList<String> musician = new ArrayList<String>();
private ArrayList<String> songs = new ArrayList<String>();
public String getBandName(){
return this.bandName;
}
public void setBandName(String bandName){
this.bandName = bandName;
}
//And like that for the other two attributes. That way the classes that need access
// to these will either use a "get" method or a "set" method without directly
// accessing the attributes.
}
The work of creating getters and setters is so redundant that both Eclipse and Netbeans IDEs have a functionality that allows you to do these automatically.
songs is a part of Band, not bandName.
use this.songs instead (or just songs).
It should be noted though that public fields are against encapsulation (and thus OOP). Is this really what you want?
The major benefit of encapsulation (providing getters & setters for instance members) is to have a unified way of accessing your fields in a class. This allows you to, for example, add validation logic to your data.
private List<String> songs = new ArrayList<>();
public List<String> getSongs(){
return songs;
}
public List<String> setSongs(List<String> songlist) {
this.songs = songlist;
}
Now you can add validation to these methods. For example if you want to make sure you can only set the songlist if it has at least 5 songs in it:
public List<String> setSongs(List<String> songlist) {
if(songlist.size() > 5) {
this.songs = songlist;
}
}
We are having a SerializationException error when sending a list of objects using RPC and Java Generics.
I'm creating this widget to show the error:
public class Test<T> {
ListDataProvider<T> ldp = new ListDataProvider<T>();
public void setItems(List<T> list){
for(T t :list){
ldp.getList().add(t);
}
}
public List<T> getItems(){
return ldp.getList();
}
}
This is the code for creating the Test widget and passing a list of POJOs (where ExporterFormKey is the POJO object)
List<ExporterFormKey> list = new ArrayList<ExporterFormKey>();
ExporterFormKey key = new ExporterFormKey();
key.setKey("key1");
list.add(key);
Test<ExporterFormKey> test = new Test<ExporterFormKey>();
test.setItems(list);
At the end the next code throws a SerializationException:
service.sendList(test.getList(), new AsyncCallback...);
While the next one does fine:
service.sendList(list, new AsyncCallback...);
-----Edit----
I found that doing the next code also works
List<ExporterFormKey> newList = new ArrayList<ExporterFormKey>();
newList.add(test.getItems().get(0));
service.sendList(newList , new AsyncCallback...);
Or this also works
List<ExporterFormKey> newList = new ArrayList<ExporterFormKey>(test.getItems());
I also found this change on Test works!
public List<T> getItems(){
return new ArrayList<T>(ldp.getList());
}
http://blog.rubiconred.com/2011/04/gwt-serializationexception-on-rpc-call.html
As izaera suggested the ListDataProvider uses a non-serializable list implementation (ListWrapper) which cannot be sent directly across the wire.
Wrapping the response from ListDataProvider's getList() method into a new ArrayList as you have suggested in your post is the simplest way to workaround the issue.