I am writing an android application that parses an xml feed about car parks.
I've created a separate class that receives an xml feed as a string, this class will then parse the string, creating objects for each car park found in the feed. I will use this each time I refresh the xml feed and need to parse it, should I make this class static or not. I am still not 100% sure on when to make something static or not.
My code:
public class XMLParser2 {
private static CarPark carPark;
private static ArrayList<CarPark> carParkListings;
// Parse XML string and save each car park object created within an Arraylist collection
public static ArrayList<CarPark> parseXML(String xml) throws XmlPullParserException, IOException {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput(new StringReader(xml));
int eventType = xpp.getEventType();
carParkListings = new ArrayList<CarPark>();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if (xpp.getName().equalsIgnoreCase("situation")) {
// Create new CarPark object to hold car park data
carPark = new CarPark();
}
// Check start tag for each element wanting to obtain and save in the
// carPark object using its associated set method
if (xpp.getName().equalsIgnoreCase("carparkstatus")) {
String status = xpp.nextText();
if (status.matches("enoughSpacesAvailable"))
carPark.setCarParkActive("Open");
else if (status.matches("carParkClosed"))
carPark.setCarParkActive("Closed");
else
carPark.setCarParkActive("Full");
}
if (xpp.getName().equalsIgnoreCase("overallstarttime")) {
carPark.setDateCreated(xpp.nextText());
}
if (xpp.getName().equalsIgnoreCase("latitude")) {
Double latitude = Double.parseDouble(xpp.nextText());
carPark.setLatitude(latitude);
}
if (xpp.getName().equalsIgnoreCase("longitude")) {
Double longitude = Double.parseDouble(xpp.nextText());
carPark.setLongtitude(longitude);
}
if (xpp.getName().equalsIgnoreCase("carparkidentity")) {
String[] identity = xpp.nextText().split("\\:");
carPark.setCarParkName(identity[0]);
carPark.setCarParkID(identity[1]);
}
if (xpp.getName().equalsIgnoreCase("occupiedspaces")) {
int occupiedSpaces = Integer.parseInt(xpp.nextText());
carPark.setOccupiedSpaces(occupiedSpaces);
}
if (xpp.getName().equalsIgnoreCase("totalcapacity")) {
int totalCapacity = Integer.parseInt(xpp.nextText());
carPark.setTotalSpaces(totalCapacity);
}
}
// Check if the tag is an end tag
else if (eventType == XmlPullParser.END_TAG) {
if (xpp.getName().equalsIgnoreCase("situation")) {
// Calculate total spaces free
carPark.setFreeSpaces();
// Add CarPark class object to carParkListings ArrayList collection
carParkListings.add(carPark);
}
}
// Move to next event
eventType = xpp.next();
}
// Return ArrayList collection
return carParkListings;
}
}
Better make it non static. You manipulate objects inside the class and if multiple classes call the same method in the same time you might have problems. For example one class calls this method and it start executing and in the same time another class calls the method and reaches the line carPark = new CarPark(); before the first one has finished. It will compromise the results.
Better use instances and make the fields carPark and carParkListings non static.
If you don't want to create any instance of XMLParser2 class when you call the parseXML method, you can make it static.
But what you should not do is use the global static property in your parseXML method.
private static CarPark carPark;
private static ArrayList<CarPark> carParkListings;
Those two variables need to define local inside the method only.
If you do not maintain any other states in XMLParser2, I think it is a good candidate for a static method since it looks like an utility method to me, but you should make the carPark and carParkListings local variables of the method itself. One should use a static method if it does not use any instance variables. You only need to specify input as parameter to the method and expect to get back some result.
Related
I am trying to create a method that creates new LinkedLists. I want to pass a String parameter to use as the new LinkedList identifier but I'm getting an error "java: variable s is already defined in method createQueue(java.lang.String)"
Is there anyway to use a String to create the new LinkedList like this?
I need to do it this way for an assignment so I can't change the method declaration.
public void createQueue(String s){
LinkedList<obj> s = new LinkedList<obj>();
}
I may be looking at this the wrong way also. I'm just trying to create the linkedList atm. But my requirements are as follows:
boolean addQueue(String)
This method will have a String parameter. It will return a boolean.
It will add a new queue specified by the parameter. E.g. addQueue(“ready”) would create a new queue called “ready” to the list of queues. If there is already a queue by the specified name, then this method will return false. E.g. If you already have a queue named “ready” and you call addQueue(“ready”), it will return false. Otherwise, it will create the queue and return true.
You have to maintain a collection of queues. Because each queue has a unique name, the most appropriate collection is Map:
public class QueueManager {
private Map<String, List<Pcb>> queues = new HashMap<String, List<Pcb>>();
public boolean addQueue(String queueName) {
if (queues.containsKey(queueName)) {
// There is already a queue with that name
return false;
} else {
queues.put(queueName, new ArrayList<Pcb>());
return true;
}
}
}
Here I made the assumption that the queue is implemented with an ArrayList, but of course LinkedListwould work similarly. Then the method addPcb()is quite obvious:
public void addPcb(Pcb pcb, String queueName) {
List<Pcb> queue = queues.get(queueName);
if (queue != null) {
queue.add(pcb);
} else {
throw new IllegalArgumentException("Queue does not exist: " + queueName);
}
}
An alternative implementation of addPcb(), using addQueue()could be:
public void addPcb(Pcb pcb, String queueName) {
addQueue(queueName);
List<Pcb> queue = queues.get(queueName);
queue.add(pcb);
}
The problem is that you have two different variables named s - the String s parameter (which is a variable) and LinkedList<obj> s.
Just rename either one of them.
In the interest of not creating more variables than necessary and cluttering up within the scope of a method that could otherwise have been very slim, I've, instead, created a temporary to hold all of the files I'm going to be referencing throughout the rest of the method.
I dislike this solution because it creates an array object every time it is run when an array object is not necessary to be created.
I could also not use the array or wall of variables, and instead reference the get methods directly, but that creates a lot of redundancy as I am performing the same methods repeatedly, and I dislike that even more.
public void savePrices() {
MFilePrices file[] = {AutoEcon.files().getPrices(), AutoEcon.files().getIntangibles(), AutoEcon.files().getGroups()};
for (String price : sellPrices.keySet()) {
if (EconItem.fromString(price) != null) {
file[0].setPrice(price, sellPrices.get(price).getExpression());
file[0].setBuyRate(price, sellPrices.get(price).getBuyRate());
} else if (file[1].getLabels().contains(price)) {
file[1].setPrice(price, sellPrices.get(price).getExpression());
file[1].setBuyRate(price, sellPrices.get(price).getBuyRate());
} else if (file[2].getLabels().contains(price)) {
file[2].setPrice(price, sellPrices.get(price).getExpression());
file[2].setBuyRate(price, sellPrices.get(price).getBuyRate());
}
}
}
public Double setExpression(String id, String expr) {
savePrices();
MFilePrices file[] = {AutoEcon.files().getPrices(), AutoEcon.files().getIntangibles(), AutoEcon.files().getGroups()};
if (EconItem.fromString(id) != null)
file[0].setPrice(id, expr);
else if (file[1].getLabels().contains(id))
file[1].setPrice(id, expr);
else if (file[2].getLabels().contains(id))
file[2].setPrice(id, expr);
else return null;
sellPrices.clear();
total=0;
loadPrices(AutoEcon.plugin());
return sellPrices.get(id).getPrice();
}
Another solution could be to create an array within the FilePool class where I'm getting the files from, which contains those three configuration files, or a method which puts them into an array and sends over the array. However, the latter just moves the problem over to another class, and the former is still creating a single array that is not totally necessary.
Both of these solutions just moves the problem from one class to another.
public class FilePool {
private Config config;
private Prices prices;
private Intangibles i;
private Groups groups;
private Logs econLogs;
private ItemAliases a;
public FilePool(AutoEcon pl) {
config = new Config(pl);
prices = new Prices(pl);
i = new Intangibles(pl);
econLogs = new Logs(pl);
a = new ItemAliases(pl);
new ReadMe(pl);
}
public Config getConfig() {
return config;
}
public Prices getPrices() {
return prices;
}
public Groups getGroups() {
return groups;
}
public Intangibles getIntangibles() {
return i;
}
public Logs getLogs() {
return econLogs;
}
public ItemAliases getAliases() {
return a;
}
}
(Ignore the dumb variable names in the FilePool class, I just loved the fact that they all line up so perfectly. Will be naming appropriately before publishing)
I know I'm being a bit over-anal about this tiny thing that won't affect the running program at all, but after being constantly harassed for every minor detail of my code by my colleagues in the past, I've grown to be a bit of a perfectionist.
Thanks to anyone who spent their time reading this. <3
The creation of the array is not a problem. Resources to create an array are meaningless. What is more of a problem is that anyone reading your code will struggle to understand what the magic indices represent without referring back to the array. Which means that you should turn them into named constants which will complicate your code even further.
Much better is to have clear variable names that represent what each element represents. Also a good idea to iterate through the map so you can avoid getting the value for each item:
FilePool files = AutoEcon.files();
final MFilePrices prices = files.getPrices();
final MFilePrices intangibles = files.getIntangibles();
final MFilePrices groups = files.getGroups();
sellPrices.forEach((price, value) -> {
if (EconItem.fromString(price) != null) {
setPriceAndBuyRate(prices, price, value);
} else if (intangibles.getLabels().contains(price)) {
setPriceAndBuyRate(intangibles, price, value);
} else if (groups.getLabels().contains(price)) {
setPriceAndBuyRate(groups, price, value);
}
});
private void setPriceAndBuyRate(MFilePrices filePrices, Price price, Value value) {
filePrices.setPrice(price, value.getExpression());
filePrices.setBuyRate(price, value.getBuyRate());
}
If you're concerned that the number of variables make the method difficult to read then move the logic for comparing the price to the labels and setting the price and buy rate into a separate class. That's a good practice in any case as it gives the class a single reason to change.
I have this class that serves as a container which I will use the instance variable for processing later
class Data{
static int counter= 0;
boolean boolean1;
String string1;
public Data() {
counter++;
}
}
And I have this method that sets the values of Data
public Data setData()
{
Data data = null;
for (int i = 0; i < somecoutnerhere; i++) {
Data = new Data();
Data.boolean1 = some boolean put here;
Data.string1 = "some string to be put here";
}
return ProcessData(Data);
}
I also have this class ProcessData that will make use of Data and will construct the response
private class ProcessData
{
private final Map<String, List<?>> map = new HashMap<String, List<?>>();
int counter;
public ProcessData(Data data)
{
map.put("boolean1", data.boolean1);
map.put("String1", data.string1);
counter = data.counter;
}
public String someMethodToGenerateReturnData(){
// some code here to make use of the Data collected. Will basically use map to construct the return String
}
}
My problem is that I couldn't figure out how can I return all the instance variables created on the for-loop for Data on setData(). Any thoughts?
My problem is that I couldn't figure out how can I return all the instance variables created on the for-loop for Data on setData(). Any thoughts?
According to this your problem is not "returning all instance one variables in one call", as your title states, but rather a question about how returning all Data-Objects created in your for-loop, which is easier.
Your code is erronous though, so I went ahead & corrected it (I hope I didn't mess up). I also renamed a few things.
The changes I made are:
renamed "boolean1" and "string1" to "trueOrFalse" and "string"
added a public, fully parameterized constructor to the Data-class
added a ProcessData-list to the setData()-method, which is filled in the for-loop
(+ a comment)
However, I'd strongly recommend you to check your architecture, and also to learn a bit about naming conventions, or coding conventions in general. Names should point out the purpose or content of the method/variable/class, and "boolean1" isn't really doing that.
Regarding the architecture: The Data-class seems to exist solely for the counter, and you could easily change that, making the Data-class obsolete (unless it's used somewhere else).
Data class:
class Data {
static int counter = 0;
boolean trueOrFalse;
String string;
public Data() {
counter++;
}
public Data(boolean someBoolean, String someString) {
this.trueOrFalse= someBoolean;
this.string = someString;
counter++;
}
}
setData()-Method:
public List<ProcessData> setData() {
List<ProcessData> processedDataList = new ArrayList<ProcessData>();
for (int i = 0; i < someCounterHere; i++) {
processedDataList.add(new ProcessData(new Data(true, "testString"));
// a new Data-object is created (parameters true and "testString")
// a new ProcessData-object is created (parameter is the newly created Data-Object)
// the newly created ProcessData-object is added to the list
}
return processedDataList;
}
ProcessData-class:
private class ProcessData {
private final Map<String, List<?>> map = new HashMap<String, List<?>>();
int counter;
public ProcessData(Data data) {
map.put("trueOrFalse", data.trueOrFalse);
map.put("string", data.string);
counter = data.counter;
}
public String someMethodToGenerateReturnData() {
// some code here to make use of the Data collected. Will basically use map to construct the return String
}
}
Let me explain further. I have a String function (called stringReversal) that returns a reversed string, it has no errors in the function. But, when I try to print using System.out.println() from the main function, it gives me the error "Can not make a static reference to the non static method stringReversal (string s) from the type StringReverse".
I tried giving my stringReversal a static modifier, but after doing so, it gave me run time errors.
Here's what my code looks like:
public class StringReverse {
public String stringReversal(String s){
if(s == null){
return null;
}
else if(s.length()% 2 == 0){
int size = s.length();
for(int i =0; i<s.length(); i++){
s.replace(s.charAt(i), s.charAt(size));
size--;
if(i == (s.length()/2) || size==0)
break;
}
}
else{
for(int i =0; i<s.length(); i++){
int size = s.length();
s.replace(s.charAt(i), s.charAt(size));
size--;
if(i == ((s.length()/2) +1) || size==0 )
break;
}
}
return s;
}
public static void main(String[] args) {
String str = "Hello";
String rev = stringReversal(str);
System.out.println();
}
}
You have to instantiate your class to call object members, or you need to make your function static, indicating it's not part of object oriented paradigm
In your case you can do
StringReverse sr = new StringReverse();
String rev = sr.stringReversal("hello");
or declare your method differently
public static String stringReversal(String s)
In fact the class name StringReverse itself does not sound like some kind of object, so the second way is preferred impo
The deeper problem you have is the confusion on how Java handle OO and entrance function in general. Java is primarily an OO language so most of the time everything shall be an object or a member of a object. But when you telling the VM to run some java code, there got to be a place to start, which is the main method. There has to be one main method and it must be under some class, but it really has nothing to do with the class that contains it. Within the main method, you either start your OO life by instantiating objects and invoking their members (method 1) or stay in the spaghetti world for a bit longer, by calling other static members as procedures (method 2).
You have two options:
Keep the method non static and then create an instance of your class to call the method:
public static void main(String[] args) {
String str = "Hello";
StringReverse sr = new StringReverse(); // instance of class
String rev = sr.stringReversal(str);
System.out.println(); // just prints a blank line lol...
}
Make the method static (you should do this):
public static String stringReversal(String s) {
// ...
}
public static void main(String[] args) {
String str = "Hello";
String rev = stringReversal(str);
System.out.println(); // just prints a blank line lol...
}
Either way, you have to fix your "run time errors". You can't get around that. If your method doesn't work, keeping it not static won't make it work either.
By the way, I think you meant to do System.out.println(rev); instead of System.out.println();
For the record, here is how to easily reverse a string (both methods work):
public static String stringReversal(String s) {
StringBuffer reverseString = new StringBuffer();
// reverse the string
for (int i = s.length() - 1; i > -1; i--) {
reverseString.append(s.charAt(i));
}
return reverseString.toString();
}
/* using the reverse() method in the StringBuffer class
instead of reversing the string through iterations */
public static String stringReversal2(String s) {
return new StringBuffer(s).reverse().toString();
}
This is happening because your Main method is static, but the class that it's in is not. In order to call a non-static method, you need to create an instance of the class. Alternatively, the method can be made static, but in order to refer to it you need to include the class name in your call (as if to use the class itself like an object containing the method - see below).
There are three solutions to this problem:
Make an instance of the class and call the method from your object (recommended).
make the method static and use StringReverse.stringReversal().
Make the class AND the method static.
So from reading some of the articles, the message i got out of it was being able to modify fields and set values to classes in real time without recompiling.
so is it possible to do this to 3rd party java library created classes which no source code is available / is it possible to use reflection to modify class instances on run time?
in what other scenarios is reflection commonly used?
I am trying to understand how reflection can be applicable.
Any time you're dealing with a string at runtime and want to treat part of that string as an identifier in the language.
Remote procedure calling -- treat part of a message received over the network as a method name.
Serialization and deserialization -- convert field names to string so you can write the object's fields to a stream and later convert it back into an object.
Object-relational mappings -- maintain a relationship between fields in an object and columns in a database.
Interfaces with dynamically typed scripting languages -- turn a string value produced by a scripting language into a reference to a field or method on an object.
It can also be used to allow language features to be emulated in the language.
Consider the command line java com.example.MyClass which turns a string into a class name. This doesn't require reflection, because the java executable can turn a .class file into code, but without reflection it would not be able to write java com.example.Wrapper com.example.MyClass where Wrapper delegates to its argument as in:
class Wrapper {
public static void main(String... argv) throws Exception {
// Do some initialization or other work.
Class<?> delegate = Class.forName(argv[0]);
Method main = delegate.getMethod("main", String[].class);
main.apply(null, Arrays.asList(argv).subList(1, argv.length).toArray(argv));
}
}
One other case developing IDEs like eclipse/netbeans etc., to determine which methods in an abstract class need to be implemented by a child class, and automatically write the missing method calls for you (one example).
Injection frameworks like Guice or Spring use reflection to help you build instances of objects at runtime.
Reflection is also useful in cases where configuration is required to string things together. For example, in an application I wrote I have a #Report("debits") annotation that is simply added to methods that generate reports. Then, in the XML configuration a user can simply add:
<requiredReports="debits,blah,another"/>
This minimizes boiler plate code from mapping the XML code to the actual method, since reflection can discover the report methods and make it available directly.
I was asked to create a solution for the below statement.
"1) A diff service that:
• can calculate the differences between two objects and return the resulting
"diff"
• can apply a previously created "diff" against an original object, so that
the returned object matches the modified object that was used to calculate
the diff. "
This would have been very difficult without using reflection. Using reflection I could list all the unknown object's Class elements, properties and methods. I could use these to get the values contained in the object. I could compare the original and modified objects values, create "diff" object reflecting changes between the two objects.
Using Java reflection I could then read the instructions in the "diff" object and apply these to the original object. Java reflection gave me the tools needed to change the values on the unknown properties of the original object. I could invoke setter methods and instantiate types where needed if the original property was null to set the modified value on the original object.
The "diff" app works on any two objects of the same type, but they could be any type, both objects just have to be of the same type.
Reflection is very powerful and allow us to create true generic polymorphic methods, functions, libraries and system, where the passed object type does not need to be known at compile time. This applies when using Java Reflection and Generics together, a very powerful combination.
To end of, I have also used Java Reflection to create a generic sort function, that could sort any list of any Class Type, using any property of the Class as the sort key.As long as the calling method passed the list and the property name to use, the method would return a sorted list.
Here are some cases to use reflection in
public class Main {
public static void main(String[] args) {
displayProperties(Stage.class);
}
public static void displayProperties(Class class) {
boolean hasParam = false;
boolean hasReturn = false;
ArrayList<Method> propMethods = new ArrayList<>();
Method[] methods = clazz.getMethods();
for (Method m: methods) {
Parameter[] paraType = m.getParameters();
if(m.getParameterCount()<2) {
if ((m.getReturnType() == void.class && paraType.length == 1) || (m.getReturnType() != void.class && paraType.length == 0)) {
//Get the properties alone
propMethods.add(m);
}
}
}
for (int i = 0; i < propMethods.size(); i++) {
if (propMethods.get(i).getName().startsWith("get") || propMethods.get(i).getName().startsWith("set")) {
System.out.println(readWrite(propMethods.get(i), propMethods) + " " + propMethods.get(i).getName().substring(3)+"( "+propMethods.get(i).getReturnType().getTypeName()+" )");
} else
System.out.println(readWrite(propMethods.get(i), propMethods) + " " + propMethods.get(i).getName() + "( "+propMethods.get(i).getReturnType().getTypeName()+" )");
}
}
public static String readWrite(Method method, ArrayList<Method> propMeths) {
ArrayList<Method> temp;
temp = propMeths;
boolean readIn = false;
boolean writeIn = false;
String onlyName = method.getName().substring(3);
for (int i = 0; i < temp.size(); i++) {
//use the substring--
if (temp.get(i).getName().startsWith("get") && temp.get(i).getName().endsWith(onlyName)) {
readIn = true;
}
if (temp.get(i).getName().startsWith("set") && temp.get(i).getName().endsWith(onlyName)) {
writeIn = true;
}
}
if (readIn == true && writeIn == true)
return "rw ";
else if (readIn == true && writeIn == false)
return "r ";
else
return "w ";
}
}
Another case with the String class
public static void main(String[] args)
{
displayProperties(String.class);
}
public static void displayProperties(Class class){
clazz.getDeclaredFields();
Method[] methods = clazz.getDeclaredMethods();
for(int ii = 0; ii<methods.length; ii++){
System.out.println("Method Name: "+methods[ii].getName());
System.out.println("Method Type: "+methods[ii].getReturnType());
System.out.println("Method Pa: "+methods[ii].getParameterCount());
System.out.println("Method Type: "+methods[ii].getReturnType());
}
}
Loading from XML with reflection
public static Object loadFromXml(String filePath) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
File newFile = new File(filePath);
Document doc = builder.parse(newFile);
Node root = doc.getFirstChild();
return loadObjectElement(root);
}
/**
* This method loads from an xml file and returns all the contents of the file as an object
* #param root The node passed in to the method from which the "tree" gets a new level
* #return all the contents of the xml file as an object
* #throws Exception
*/
public static Object loadObjectElement(Node root) throws Exception {
//loads the root
String studentClass = root.getAttributes().getNamedItem("class").getTextContent();
Object newStudentObject = Class.forName(studentClass).newInstance();
//gets the children nodes (may have text elements like \n)
NodeList studentFieldList = root.getChildNodes();
//iterates through the children nodes
for (int i = 0; i < studentFieldList.getLength(); i++) {
//checks to make sure the child node is not a text node
if (studentFieldList.item(i).getNodeType() != Node.TEXT_NODE) {
//checks if the current node does not have children
if (studentFieldList.item(i).getChildNodes().getLength() == 0) {
//receives data of the current node
String nameField = studentFieldList.item(i).getAttributes().getNamedItem("name").getTextContent();
String valueField = studentFieldList.item(i).getAttributes().getNamedItem("value").getTextContent();
Field declaredFieldInClass = newStudentObject.getClass().getDeclaredField(nameField);
//makes the field accessible
declaredFieldInClass.setAccessible(true);
//checks the field type
switch (declaredFieldInClass.getType().getSimpleName().toLowerCase()) {
case "integer":
case "int":
declaredFieldInClass.set(newStudentObject, Integer.valueOf(valueField));
break;
case "float":
declaredFieldInClass.set(newStudentObject, Float.valueOf(valueField));
break;
case "boolean":
declaredFieldInClass.set(newStudentObject, Boolean.valueOf(valueField));
break;
default:
declaredFieldInClass.set(newStudentObject, valueField);
}
declaredFieldInClass.setAccessible(false);
} else {
//there are children in the current node
NodeList modulesObjectList = studentFieldList.item(i).getChildNodes();
String nameField = studentFieldList.item(i).getAttributes().getNamedItem("name").getTextContent();
Field declaredFieldInClass = newStudentObject.getClass().getDeclaredField(nameField);
List<Object> modules = new ArrayList<>();
//adds the modules into the array
for (int j = 0; j < modulesObjectList.getLength(); j++) {
if (modulesObjectList.item(j).getNodeType() != Node.TEXT_NODE) {
//recursively calls the the loadObjectElement method for any sub lists
modules.add(loadObjectElement(modulesObjectList.item(j)));
}
}
//sets the modules of the specific student that the method is working with
declaredFieldInClass.set(newStudentObject, modules);
}
}
}
return newStudentObject;
}