Avoid creating multiple Class Objects in a for loop- Performance - java

Suppose I have a class named Song with lots of information like
Song title
Song artist
Song file extension
other Song information...
A setter in this class sets the Song title.
public class Song {
private String title;
private String artist;
...
public Song(String songTitle, String songArtist, ...) {
this.title=songTitle;
this.artist=songArtist;
...
}
public void setTitle(String Songname){
this.title = Songname;
}
public String getTitle(){return title;}
public String getArtist(){return artist;}
...
}
I have a List of Strings with different song titles, but no other information about the songs. Now I want to populate a List of Songs and just set the title of those Songs classes with the help of the setter to the titles in the List Strings. I do this in a for-statement, but if I check the List entries I only get the title of the last Song that was added.
int index = 0;
Song songClass = new LocalSong(null, null, ...);
for (final String song : Songs) {
try {
songClass.setTitle(song);
//Set Titles for Local Song Objects
AnotherList.add(index, null);
AnotherList.set(index, songClass);
System.out.println("Title of Song number " + index + " is " + AnotherList.get(index).getTitle());
index++;
} catch (Exception e) {e.printStackTrace();}
}
I found that when I don't use the setter in the Song class and create a new Song object inside the for-statement for each List of Strings entry, it works. Like this:
int index = 0;
for (final String songname : Songs) {
try {
Song songClass = new LocalSong(songname, null, ...);
//Set Titles for Local Song Objects
AnotherList.add(index, null);
AnotherList.set(index, songClass);
System.out.println("Title of Song number " + index + " is " + AnotherList.get(index).getTitle());
index++;
} catch (Exception e) {e.printStackTrace();}
}
But isn't that solution performance-heavy? For instance if I would have hundreds of entries in the List of Strings, i would create hundreds of song classes that i don't use, or does the garbage collector handle this well?
Also, is there a better way to create Songclasses with Titles that I get from a List of Strings.

You're referring to instances of the Song class, not lots of classes, and object-oriented programming is all about creating instances whenever you have identifiably distinct objects. A single HTTP request in a modern Java framework will typically create and then discard thousands of objects, and the JVM is engineered to handle it just fine.
More broadly, don't optimize for performance unless you have a demonstrated performance problem--it's actually slow, and investigation (such as profiling) tells you where the problem is.

You seem to have a fundamental misunderstanding of the keyword new which creates new instances of objects. If you only new an object once then all object references in the list are pointing at the same object, therefore it's the last setter that dictates that values seen no matter which reference is accessed.
You are also attempting to do what is called "premature optimisation". Be wary of this as you will often over-complicate and hence adversely impact maintainability of your code by focusing on this. It's ok to do minor easy optimisations or choose alternatives when they are just as easy or easy to refactor to, but be careful you're not solving a non existent problem.

The reason why the second solution works is because you're creating a NEW instance of a Song class every iteration of the for loop and adding it to the list.
In the first solution, you're adding the SAME instance every iteration of the for loop, songClass. What you're doing here is changing the title of songClass, and adding it to the list.
As for garbage collection, it's perfectly fine at this stage of the code to create a list with a lot of objects. The garbage collector will deallocate objects when you aren't using them anymore.

There is nothing unusual here, in first example you reuse the same object, your list contains the same references to the same single object. Since you modify it you are modifying also the whole list.
But isn't that solution performance-heavy?
no, at least until you will measure that it takes too much memory or your loop execution takes too much time. If that happen then you should redesign your code, ie. process your song list in chunks, only what user sees on screen. Maybe redesign your UI, allow your user to see songs alphabetically - only on latter A, B, C, ... - whatever suits your app.

Easiest way to restricting multiple instance of
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.*;
import javax.swing.JFrame;
public class SingleWindow {
static String str;
private SingleWindow() {
}
private static void single() {
readFile();
System.out.println("Single str =" + str);
if (str.equals("false")) {
gui();
writeFile("true");
}
}
private static void writeFile(String str){
try {
PrintWriter pw = new PrintWriter("E:\\Temp.txt");
pw.print(str);
pw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static void readFile() {
String status = null;
System.out.println("readFile() entered \n");
try {
FileReader fr = new FileReader("E:\\temp.txt");
BufferedReader br = new BufferedReader(fr);
while((status = br.readLine())!=null){
str = status;
System.out.println("Status " + status);
}
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static void gui() {
System.out.println("Entered GUI method \n");
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(300,300);
frame.addWindowListener(new WindowListener() {
#Override
public void windowOpened(WindowEvent arg0) {}
#Override
public void windowIconified(WindowEvent arg0) {}
#Override
public void windowDeiconified(WindowEvent arg0) {}
#Override
public void windowDeactivated(WindowEvent arg0) {}
#Override
public void windowClosing(WindowEvent arg0) {
System.out.println("Windows Closing \n");
writeFile("false");
}
#Override
public void windowClosed(WindowEvent arg0) {
System.out.println("Windows Closed \n");
}
#Override
public void windowActivated(WindowEvent arg0) {
//writeFile("false");
System.out.println("Windows Activate \n");
}
});
}
public static void main(String args[]) {
single();
System.out.println(str + "main method \n");
}
}

Related

how to use a list in another class?

I want to create a list, add blocks to it and then use it in a BlockBreakEvent to check if the block is in the list and cancel the event if it's not. But I can't seem to create and add things in it any other way than in the actual event itself (which looks to me like it would create issues). The only thing that is working for me is creating the list in the event and adding blocks to it one by one which looks really messy compared to: creating the list in a separate class and just checking the list with if(Listname.contains(block)) does anyone know how I can achieve this? Whether its dependency injection, or whatever else. I just can't figure out how to put it to use.
Here's what I've tried and is currently working for me, but I believe it's theoretically incorrect:
public class Event implements Listener {
#EventHandler
public void onBreak(BlockBreakEvent e) {
List<Material> allowedblocks = new ArrayList<Material>();
allowedblocks.add(Material.STONE);
//repeat this 10-50 times for whatever item
Player p = e.getPlayer();
Material block = e.getBlock().getType();
if(allowedblocks.contains(block)){
p.sendMessage("Invalid block. Break cancelled");
e.setCancelled(true);
}else{
p.sendMessage("Valid Block");
}
}
}
You can make allowedBlocks List a class field and fill it with elements inside of the constructor.
public class YourClass {
private List<Material> allowedBlocks = new ArrayList<>();
public YourClass() {
allowedBlocks.add(Material.STONE);
//repeat this 10-50 times for whatever item
}
#EventHandler
public void onBreak(BlockBreakEvent e) {
Player p = e.getPlayer();
Material block = e.getBlock().getType();
if(allowedBlocks.contains(block)){
p.sendMessage("Valid Block");
} else {
p.sendMessage("Invalid block. Break cancelled");
e.setCancelled(true);
}
}
}
Another approach would be to make the list static and fill it with values inside of a static block. I would not recommend making the list static if you are planning to change its values, but if your allowed blocks are going to remain the same, it may be a good idea to even go further and make it public, so you can access it from anywhere without an instance of YourClass
public class YourClass {
public static final List<Material> allowedBlocks;
static {
List<Materials> list = new ArrayList<>();
list.add(Material.STONE);
//repeat this 10-50 times for whatever item
//use an unmodifiable list,
//so you do not accidentally change its content later
allowedBlocks = Collections.unmodifiableList(list);
}
#EventHandler
public void onBreak(BlockBreakEvent e) {
Player p = e.getPlayer();
Material block = e.getBlock().getType();
if(allowedBlocks.contains(block)){
p.sendMessage("Valid Block");
} else {
p.sendMessage("Invalid block. Break cancelled");
e.setCancelled(true);
}
}
}
In the first case, there will be a list of allowedBlocks per instance of YourClass, which means, that every time you call new YourClass() a new List will be created and filled. In the second case, there will be only one list which will be created and populated on class loading (at the very beginning of the program) start up.
P.S. I would rather use a Set instead of a List here, considering you are using contains very often.
Since you are using an enum to store your Material types, you can simply call the static .values() method through Material.
Ex:
#EventHandler
public void onBreak(BlockBreakEvent e) {
Player p = e.getPlayer();
Material block = e.getBlock().getType();
if(List.of(Material.values()).contains(block)){
p.sendMessage("Invalid block. Break cancelled");
e.setCancelled(true);
}else{
p.sendMessage("Valid Block");
}
}
}
If you need to be able to customize what values are in the List you can use the singleton pattern to access that information globally.
The instance can be accessed statically from anywhere in the application:
import java.util.List;
public class BlockController {
public static BlockController instance = new BlockController();
private List<Material> allowedBlocks;
public BlockController() {
this.allowedBlocks = new ArrayList<>();
}
public void addAllowedBlock(Material mat) {
this.allowedBlocks.add(mat);
}
public void removeAllowedBlock(Material mat) {
this.allowedBlocks.remove(mat);
}
public boolean containsBlock(Material mat) {
return this.allowedBlocks.contains(mat);
}
public void clear() {
this.allowedBlocks.clear();
}
/**
* You can add more functionality here...
* This class can be accessed anywhere in the application
*
* use:
*
* BlockController controller = BlockController.instance;
* controller.containsBlock(Material.BLOCK);
*/
}
One approach to creating the list in a separate class is to use a static initializer block:
public class MyClass {
public static final List<Material> ALLOWED_MATERIALS = new ArrayList<>();
static {
ALLOWED_MATERIALS.add( Material.STONE );
}
public static List<Material> getAllowedMaterials() {
return Collections.unmodifiableList( ALLOWED_MATERIALS );
}
...
}
Try to create the List in a static context. This way the list is the same for all instances:
public class MyClass {
public static List<Material> allowedblocks = new ArrayList<Material>();
#EventHandler
public void onBreak(BlockBreakEvent e) {
allowedblocks.add(Material.STONE);
...
Then you can call the List from everywhere like this (e.g. if statement):
if(MyClass.allowedblocks.contains(block))
Your problem seems similar to this question, maybe this answer helps too: .

Using getters and setters with ArrayLists in java

I'm trying to read a csv and storing the records in an ArrayList.
Since I know the no. of records in the csv file I'm specifying the size i.e. 600 when creating the object.
I want the program to be able to read files of unknown no. of records.
How do I make it dynamic.
Here's the working code for the file with 600 records.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.io.*;
public class BankRecords extends Client{
//Create objects for processing data
//private static int count;
static BankRecords[] obj=new BankRecords[600];
static List<List<String>> array = new ArrayList<List<String>>();
#Override
void readData() {
// TODO Auto-generated method stub
String line=" ";
//int i=0;
//try with resources statement
try(BufferedReader br = new BufferedReader(new FileReader("bank-Detail.csv"))){
while((line=br.readLine()) != null) //read from file
{
array.add(Arrays.asList(line.split(",")));
//check data
//count++;
//System.out.println(array.get(i++));
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
processData();
}
#Override
void processData() {
// TODO Auto-generated method stub
int idx=0;
for(List<String> bankData: array)
{
obj[idx]= new BankRecords();
obj[idx].setId(bankData.get(0));
obj[idx].setAge(Integer.parseInt(bankData.get(1)));
obj[idx].setSex(bankData.get(2));
obj[idx].setRegion(bankData.get(3));
obj[idx].setIncome(Double.parseDouble(bankData.get(4)));
obj[idx].setMarried(bankData.get(5));
obj[idx].setChild(Integer.parseInt(bankData.get(6)));
obj[idx].setCar(bankData.get(7));
obj[idx].setSact(bankData.get(8));
obj[idx].setCact(bankData.get(9));
obj[idx].setMort(bankData.get(10));
obj[idx].setPep(bankData.get(11));
idx++;
//System.out.println(obj[idx].getId());
}
printData();
}
#Override
void printData() {
//Printing First 25 ID, age, sex, region, income and mortgage
System.out.println("ID\t\tAGE\t\tSEX\t\tREGION\t\tINCOME\t\tMORTGAGE\n");
for(int i=0;i<25;i++){
String s=String.format("%s\t\t%s\t\t%s\t\t%-10s\t%8.2f\t%2s", obj[i].getId(),obj[i].getAge(),obj[i].getSex(),obj[i].getRegion(),obj[i].getIncome(),obj[i].getMort());
System.out.println(s);
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
public double getIncome() {
return income;
}
public void setIncome(double income) {
this.income = income;
}
public String isMarried() {
return married;
}
public void setMarried(String married) {
this.married = married;
}
public int getChild() {
return child;
}
public void setChild(int child) {
this.child = child;
}
public String getCar() {
return car;
}
public void setCar(String car) {
this.car = car;
}
public String getSact() {
return sact;
}
public void setSact(String sact) {
this.sact = sact;
}
public String getCact() {
return cact;
}
public void setCact(String cact) {
this.cact = cact;
}
public String getMort() {
return mort;
}
public void setMort(String mort) {
this.mort = mort;
}
public String getPep() {
return pep;
}
public void setPep(String pep) {
this.pep = pep;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
BankRecords bnk= new BankRecords();
bnk.readData();
}
}
ArrayList can the elements dynamically, so it is not required to know the size in advance.
However, for the BankRecords array, do not initialize it with 600 initially. Instead do something like this:
static BankRecords[] obj = null;
static List<List<String>> array = new ArrayList<List<String>>();
void processData() {
// TODO Auto-generated method stub
obj=new BankRecords[array.size()];
// TODO do your work here
}
You do not have to know the number of records beforehand in order to use an ArrayList. You can specify a default size in the constructor, however it is smart enough to expand itself if you add more records than this.
You are almost there, but for some strange reasons you are using Lists in places where you already have an array; yet on the other side, you are using an array where a List would be a much better fit.
You can rework your code as follows:
// TODO Auto-generated method stub
HINT: those TODOs are generated by your IDE. The idea is that you delete them as soon as you have some real content instead. Keeping them means leaving garbage in your source code. Anything that doesn't add real value to your source code: remove it. Always. Immediately!
String line=" ";
List<Bankrecord> records = new ArrayList<>();
//int i=0; ... again: unused code --- remove that!
try(BufferedReader br = new BufferedReader(new FileReader("bank-Detail.csv"))){
while((line=br.readLine()) != null) //read from file
{
String[] lineData = line.split(",");
BankRecord recordForNewLine = buildRecordFrom(lineData);
records.add(recordForNewLine);
} ...
And then you could rework your processData into something like:
private BankRecord buildRecordFrom(String[] lineData) {
BankRecord newRecord = new BankRecords();
newRecord.setId(lineData[0];
...
return newRecord;
}
And things that you should really consider changing, too:
Building your bank records by simply assuming that column contains a valid ID, and the next column contains a valid xyz ... is a bad idea.
Instead, you should be validating all your input: you should check that each array you gain from split has **exactly the expected length. And then have to validate that each value from that array has the expected "content"
Then, from a modelling perspective: you have a ton of setters on your Bankrecord class. But that is simply wrong! In real life, when some "record" is created, then its essential properties (such as its ID) can't be changed after creation!
Instead, you should make sure that such properties in your class can't be changed after the object has been created. The way to go here: Builder pattern!
Finally: my code above is meant as "inspiration point" to get you going. Dont blindly copy/paste it; there might be various typos in - just read it until you get what (and why) it is doing (what it is doing)!
Then: I hope you understand that real CSV parsing is much more complicated than splitting around "," (for example: strings in CSV data can contain ',' too; and then your simple split would rip up that string!)
If you are serious about parsing real-world-other-people CSV input, then you better look into using existing libraries to do this for you. Writing a correct CSV parser is hard work (and not very rewarding; since that means re-inventing a complicated wheel for no good reason)!

Java method parameter for creating new object of different class

I have searched high and low for a solution to this problem, and finally signed up here to see if someone can point me to what is undoubtedly a really simple solution that is evading me. I'm working on MIT's OpenCourse to teach myself Java and am stumped on this problem.
I have two classes, Library and Book. The program keeps track of books created for each library, their location, and whether or not they are checked out. I am given the main method for Library that cannot be edited. Instead its methods must be built to produce the desired output. The issue I am having is determining the argument syntax to pass to a method in Library that creates a new Book in the main method of Library.
Relevant Library Code:
package mitPractice;
public class Library {
String library_address;
static String library_hours = "Libraries are open daily from 9AM to 5PM";
Book[] catalog;
//List of Methods for Libraries
public Library(String address) {
library_address = address;
}
public static void printOpeningHours() {
System.out.println(library_hours);
}
public void addBook(/*unsure what parameter to add here*/) {
catalog[ (catalog.length + 1)] = //unknown parameter
}
public static void main(String[] args) {
//Create two libraries
Library firstLibrary = new Library("10 Main St.");
Library secondLibrary = new Library("228 Liberty St.");
//Add four books ***This block of code is the problem and is uneditable
firstLibrary.addBook(new Book("A Game of Thrones"));
firstLibrary.addBook(new Book("Rama"));
firstLibrary.addBook(new Book("Understanding Space"));
firstLibrary.addBook(new Book("Way of the Clans"));
So my question is how to make the method compatible with the code given in the main method? I have attempted numerous combinations for arguments to pass to addBook() and none seem to deliver.
Book code:
package mitPractice;
public class Book {
Boolean isCheckedOut = false;
String book_title;
public Book(String title) {
book_title = title;
}
public void Borrow() {
isCheckedOut = true;
}
public void Return() {
isCheckedOut = false;
}
public boolean isBorrowed() {
if (isCheckedOut == true) {
return true;
} else {
return false;
}
}
public String getTitle() {
return book_title;
}
/*public static void main(String[] args) {
Book example = new Book("A Game of Thrones");
System.out.println("Title: " + example.getTitle());
System.out.println("Borrowed?: " + example.isBorrowed());
example.Borrow();
System.out.println("Borrowed?: " + example.isBorrowed());
example.Return();
System.out.println("Borrowed?: " + example.isBorrowed());
}*/
}
You want the parameter to be of type Book:
public void addBook(Book newBook) {
catalog[(catalog.length + 1)] = newBook;
}
This will throw an ArrayIndexOutOfBoundsException though, because you're trying to put an element in the array after the last index. You'll need to keep track of how many books have been added and rebuild your array when it gets full. Like this:
private int numberOfBooks = 0;
public void addBook(Book newBook) {
numberOfBooks++;
if(numberOfBooks >= catalog.length) {
// Rebuild array
Book[] copy = new Book[catalog.length * 2]
for(int i=0; i<catalog.length; i++){
copy[i] = catalog[i];
}
catalog = copy;
}
catalog[numberOfBooks] = newBook;
}
Or use a collection like java.util.ArrayList.
So instead of having this line:
Book[] catalog;
You might have
ArrayList<Book> catalog = new ArrayList<Book>();
This creates an ArrayList which is a Java implementation of a dynamically sized array.
Then your addBook method might look like this:
public void addBook(Book newBook) {
catalog.add(newBook);
}
In order to use ArrayList you'll need to include this line at the beginning of the file, after the package mitPractice; line:
import java.util.ArrayList;
This always ends in array out of bounds :
public void addBook(/*unsure what parameter to add here*/) {
catalog[ (catalog.length + 1)] = //unknown parameter
}
Actually, the best would be to use List :
public class Library {
List<Book> catalog = new ArrayList<>();
...
}
Then you can simply do this :
public void addBook(Book book) {
catalog.add(book);
}
Try this code. This will add a book to a very important data structure called an ArrayList found in the util package in java. A programmer has to import this before the class statement but after any package statement. The code also uses a method from your Book API to print the title when a book is successfully added to the ArrayList. ArrayLists are favourable over Arrays because an java.util.ArrayList can expand without problems whereas an Array is a fixed size and is not so dynamic.
Check out this code I modified of the Library class that compiles and runs.
package mitPractice;
import java.util.ArrayList;
public class Library {
String library_address;
static String library_hours = "Libraries are open daily from 9AM to 5PM";
ArrayList<Book> books = new ArrayList<Book>();
//List of Methods for Libraries
public Library(String address) {
library_address = address;
}
public static void printOpeningHours() {
System.out.println(library_hours);
}
public void addBook(Book b/*unsure what parameter to add here*/) {
books.add(b); // add the book to the ArrayList
System.out.println("The book: " + b.getTitle() + " was successfully added.");
}
public static void main(String[] args) {
//Create two libraries
Library firstLibrary = new Library("10 Main St.");
Library secondLibrary = new Library("228 Liberty St.");
//Add four books ***This block of code is the problem and is uneditable
firstLibrary.addBook(new Book("A Game of Drones"));
firstLibrary.addBook(new Book("Raman Noodles"));
firstLibrary.addBook(new Book("Understanding Space"));
firstLibrary.addBook(new Book("Way of the Yoga Baywatch Babes"));
} // end main
} // end Library class
I hope this helps some.
All the best,
user_loser

Save and Delete buttons not working

I want to save whatever I write in a textfield and delete the line the program finds after clicked on search button. why doesn't it work? Here are my two buttons:
private class dDelete implements ActionListener {
public void actionPerformed (ActionEvent e) {
HM.remove((String)result.getText());
}
}
private class sSave implements ActionListener {
public void actionPerformed (ActionEvent e) {
Set <String> ISet = HM.keySet();
Iterator itr = ISet.iterator();
String tuple = "";
java.io.File iwrite = new java.io.File("c:\\temp\\savetest.txt");
if (iwrite.exists()){
System.out.println("The file exists");
System.exit(0);
}
java.io.PrintWriter output = null;
try {
output = new java.io.PrintWriter(iwrite);
} catch(Exception ex) {
ex.printStackTrace();
}
while (itr.hasNext()) {
String Keys = (String)itr.next();
String val = HM.get(Keys);
tuple = Keys + " " + val;
output.print(tuple);
}
}
}
You should close your output writer output.close() that could be why.
You are not showing what you are adding to the set HM so hard to tell if the remove will work. Check the return value of the call to HM.remove and you will see if it succeeds, else you are using the wrong keys (not using the same/equal one when adding as removing).
Other than that I would recommend iterating over the entrySet when you need both the key and the value (HM.entrySet()).
Also rename your classes and variables according to Java naming conventions (classes start with a capital letter, instance variables shouldn't etc). For more info. see the Java naming conventions.

pass class name via context and use it to store static value in different class

The dialogue and the array displays just fine, I just want to be able to set the static variable from the originating class within the onClick that is in a method that is in a different class. All of the try, catch and
<?> were things that I put in at the insistence of the compiler:
public class Setter
{
public void myList(Context context, Class<?> thisclass, int arrayid, String choice)
{
return new AlertDialog.Builder(context)
.setItems(arrayid, new OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
setChoice(thisclass, context, arrayid, which, choice);
}
})
.create();
}
public void setChoice(Class<?> thisclass, Context context, int arrayid, int which, String choice)
{
String[] array = context.getResources().getStringArray(arrayid);
try
{
Field f = thisclass.getDeclaredField(choice);
f.set(null, array[which]);
}
catch (SecurityException e)
{
e.printStackTrace();
}
catch (NoSuchFieldException e)
{
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}
}
public class ClassA extends Activity
{
static String stringa;
Setter setted = new Setter();
...
public void onCreate()
{
super.onCreate();
...
AlertDialog thinga = setted.myList(this, getclass(), R.array.thinga, stringa).show();
...
}
}
When I select an item from the list, I get this from debugger:
ClassCache.findFieldByName(Field[], String) line: 438
Class.getDeclaredField(String) line: 666
Setter.setChoice(Class, Context, int, int, String) line: 45 // the line with the Field
I think I'm passing it the class wrong but this is a bit out of my current depth.
I have a number of different classes each with their own static Strings. I am passing the method below the name of the String (in choice) and the context of what I had hoped was the original class that called a method that called a method that led to the code below. I was hoping I could call context.choice = something and the machine would read that as ClassA.stringa = something; how do I do that?
Briefly, I want to have a list of items that the user can choose from be the content of a dialogue, and have their selection be saved and accessible to the class that called for the creation of the dialogue. Perhaps I'm going about this all wrong but I got tired of dealing with other 'kludges' involving using spinners to do the same thing.
Because onClick can't have non-final objects declared elsewhere (at least that is my understanding) I thought maybe I could get around that by calling to another method, setChoice that would store the value of whatever was chosen. I would definitively say this is a kludge and would love to be shown the light as to how you are supposed to deal with these things.
Java does not have closures, but you can get close with anonymous inner classes.
String output;
public void onCreate() {
Setter.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
output = "selected";
}
});
}
See also this swing tutorial: http://download.oracle.com/javase/tutorial/uiswing/events/actionlistener.html
Edit:
In spirit of your example, this should look like this:
public class Setter
{
public void setChoice(IsetString setter, String something)
{
setter.setString(something);
}
}
class ClassA extends Activity implements setString
{
static String stringa;
string polka = "dots";
Setter setted = new Setter();
...
public void onCreate()
{
super.onCreate();
...
setted.setChoice(new IsetString() {
#Override
public void setString(String s) {
stringa = s;
}
}, polka);
...
}
}
interface IsetString {
void setString(String s);
}
The short answer - use the Reflection API.
The long answer - you'll need to obtain access to the Fields of the desired Context Class. Once you gain access to the Field instances, you can set their values using the set() method; the API call is a bit tricky in that you'll need to pass in the object reference (the context object and not the context class) whose field you wish to modify.
It is necessary that your Context, choice and something parameters to the method, contain the necessary information to make this operation as simple as possible. In other words, the Context class might have to contain the actual Class object (or provides a way to get one) that contains the field.
You can use reflection for that. Suppose you context is class itself
public void setChoice(Class<?> context, String choice, String something)
{
try {
Field f = context.getDeclaredField(choice);
f.set(null, something);
} catch (....) {
}
}
Add proper exception handling
Note that first argument to set is null. That is only valid for static methods. So you may want to check that method is static using f.getModifiers().

Categories