I'm writing a program to calculate GPA. It consists of several panels. The first panel
tells the user to specify the number of courses so that Comboboxes (gradeCombo),(hourCombo) and Textfields will be added dynamically to the second panel . Everything is fine to this point but the problem is with the listeners. In early stages, I registered the event listeners for these comboboxes individually for every element in the array and it resulted in 900 lines of codes, but it worked fine and all my results were correct. To enhance my code I'm trying to write a for loop for registering the events for the comboboxes and so far I couldn't succeed.
I tried to write the handling code as anonymous inner class and as separate inner class, Here is my last try:
for(i = 0; i<courseN;i++)
{
hourCombo[i].addItemListener(new HoursHandler());
gradeCombo[i].addItemListener(new GradeHandler());
}
public class HoursHandler implements ItemListener
{
public void itemStateChanged(ItemEvent event)
{
if(event.getStateChange()==ItemEvent.SELECTED)
{
String hour;
hour = (String) hourCombo[i].getSelectedItem();
currentHour[i]=Integer.parseInt(hour);
aquiredHours=aquiredHours+currentHour[i] prevHour[i];
prevHour[i]=currentHour[i];
}
}
}
public class GradeHandler implements ItemListener
{
public void itemStateChanged(ItemEvent event)
{
if(event.getStateChange()==ItemEvent.SELECTED)
{
String grade;
grade=(String) gradeCombo[i].getSelectedItem();
switch(grade)
{
case "A+":
currentPoint[i]=5*currentHour[i];
break;
case "A":
currentPoint[i]= 4.75 * currentHour[i];
break;
case "B+":
currentPoint[i]= 4.5 * currentHour[i];
break;
case "B":
currentPoint[i]= 4 * currentHour[i];
break;
case "C+":
currentPoint[i]= 3.5 * currentHour[i];
break;
case "C":
currentPoint[i]= 3 * currentHour[i];
break;
case "D+":
currentPoint[i]= 2.5 * currentHour[i];
break;
case "D":
currentPoint[i]= 2 * currentHour[i];
break;
case "F":
currentPoint[i]= 1 * currentHour[i];
break;
}
aquiredPoints=aquiredPoints+currentPoint[i]-prevPoint[i];
prevPoint[i]=currentPoint[i];
}
}
}
I get a NullPointerException for this statement:
hour = (String) hourCombo[i].getSelectedItem();
and everything goes wrong, none of my variables is updated and I cannot calculate the GPA..
It's hard to tell from the posted code what is wrong there. However, I assume that i is declared as an instance variable. In this case, the loop for(i = 0; i<courseN;i++) will change the value of this instance variable. Afterwards, all the listeners will internally use i with the last value that it received in the for-loop.
To circumvent this, you can declare an instance variable for each listener instance. So you can change your listener classes like this:
public class HoursHandler implements ItemListener
{
private final int index;
HoursHandler(int index)
{
this.index = index;
}
#Override
public void itemStateChanged(ItemEvent event)
{
// Use the "index" here:
String hour = (String) hourCombo[index].getSelectedItem();
currentHour[index]=Integer.parseInt(hour);
...
}
}
(similarly, introduce such an index for the GradeHandler).
Then, when you create the listeners, you can pass to each instance the index that it refers to:
// Note: "i" is declared here, and should NO longer
// be an instance variable!
for(int i = 0; i<courseN;i++)
{
hourCombo[i].addItemListener(new HoursHandler(i)); // Use "i" as "index"
gradeCombo[i].addItemListener(new GradeHandler(i)); // Use "i" as "index"
}
I assume that there might be some more elegant solutions, but this is one possible solution, solely based on the code that you provided.
It appears that i is a member field in your outer class. When your listener is executed, and your listener
hour = (String) hourCombo[i].getSelectedItem();
this statement will use whatever value i happens to be in this outer class. And after you execute your for loop, i will probably be equal to courseN, unless there's something else changing it somewhere else. In any case, it doesn't use the value that i held when you set up the listener, because you did nothing to tell it to use that value.
A simple way to fix this is to construct your listeners by giving it the index you want them to use:
public class HoursHandler implements ItemListener
{
private final int index;
public HoursHandler(int index) {
this.index = index;
}
public void itemStateChanged(ItemEvent event)
{
if(event.getStateChange()==ItemEvent.SELECTED)
{
String hour;
hour = (String) hourCombo[index].getSelectedItem();
and use index instead of i everywhere in the listener. When you construct the listener, it will store the index you want, and then the code will use that instead of the current value of i. Similarly for GradeHandler. Then
for(i = 0; i<courseN;i++)
{
hourCombo[i].addItemListener(new HoursHandler(i));
gradeCombo[i].addItemListener(new GradeHandler(i));
}
Note that the index you want the listeners to use is now passed as a parameter to the listeners' constructors.
EDITED to use final on the index member--plagiarized from Marco13's good idea.
Related
I am using a random number in a switch case. So have something like:
public void something {
Random myRand = new Random();
int number = myRand.nextInt(10 - 1) + 1;
switch(number)
case 1:
Do something and on completion go back and start running the something method again.
break:
case 1;
Do something and on completion go back and start running the something method again.
break;
Each case statement could be run through any number of times depending on input from user, some may not even be used.
What I would like is something inside the case statement saying :-
public void something (run);
Is what I am trying to do possible or is there a better way?
You can use do-while statement in this case with a condition to stop the execution.
Call your something method inside do while.
do {
something();
} while(condition);
This will call your method execute your switch case and again call your something method.
may i suggest you using interfaces?
what you are trying to implement is known as functional programming in which you pass functions as an argument to another functions
java supports functional programming in a way by using interfaces and has many built-in interfaces to ease-up the process
i recommend you to take a look at java.util.function package
now lets get on to your code
public void something(Supplier<Void> function) {
boolean condition = true; //use this boolean to control your loop
while (condition) {
Random myrand = new Random();
int number = myrand.nextInt(10 - 1) + 1;
switch (number) {
case 1:
function.get();
break;
case 2:
function.get();
break;
}
}
}
and you can call your "something" like this
public void Call() {
//if you want to declare the function only once
something(new Supplier<Void>() {
#Override
public Void get() {
System.out.println("the job is done!");
return null;
}
});
// if you already have a class implementing supplier
something(new MyFunction());
}
not that the Supplier interface is used because your function didn't have any inputs
you can also use Consumer, BiConsumer, Function, BiFunction .... for functions with inputs
The following code repeatedly calls the runSomeMethod() method for a random number of times.
Use a for loop:
public void something() {
Random myrand = new Random();
int number = myrand.nextInt(10 - 1) + 1;
for(int i=0 ; i<number ; i++) {
runSomeMethod();
}
}
concerned about your CASE syntax used
Solution goes as follows --> keep looping in a while loop
public void something {
boolean condition = true; // toggle this condition boolean to FALSE, when you want to break the loop
while(condition){
Random myrand = new Random();
int number = myrand.nextInt(10 - 1) + 1;
switch(number)
case 1;
Do something and on completion go back and start running the something method again.
break;
case 1;
Do something and on completion go back and start running the something method again.
break;
}
}
First i add new Popotnik in List popotnik depending on how big it is, which is working fine - function prostaMesta. Then i want to go through list popotnik and set popotnik value depending on where it is in for, but value of i will always be 0 everytime it is being called. Also i have break there as i only want to set one popotnik at the time. How should i increment (i) while having some sort of break in there?
Also if(popotnik.get(i) == null){} is not being called, but values inside popotnik are null(s)
private List<Popotnik> popotnik = new ArrayList<Popotnik>();
public void prostaMesta(List<Popotnik> popotnik, int sedez){
stanovanje.setPostle(sedez);
for(int i=0; i<stanovanje.getPostle(); i++){
popotnik.add(new Popotnik());
}
System.out.println(popotnik);
}
public void dodajPotnika(List<Popotnik> popotnik, Popotnik popotnik2){
for(int i=0; i<popotnik.size(); i++){
if(popotnik.get(i) == null){
setPopotnik(popotnik, i);
popotnik.set(i, popotnik2);
break;
}
}
System.out.println(getPopotnik());
}
public void setPopotnik(List<Popotnik> popotnik, int i){
this.popotnik = popotnik;
}
public List<Popotnik> getPopotnik(){
return popotnik;
}
Main class:
List<Popotnik> alPopotnik = new ArrayList<Popotnik>();
if(x.equals("p")){ //inside of a loop when prostaMesta() is being called
potovanje.prostaMesta(alPopotnik, sedez);
}
`if(x.equals("d")){` //inside of a loop when dodajPotnika() is being called
System.out.println("Vnesi ime: ");
String ime = skener.next();
Popotnik popotnik = new Popotnik(ime);
potovanje.dodajPotnika(alPopotnik, popotnik);
}
The if(popotnik.get(i) == null) is never true because objects on the list are not null. You initialize them in the for loop in prostaMesta.
If you have some fields inside the Popotnik class then they are null, but object itself is not.
You would need to do something like popotnik.get(i).getName() == null.
Besides, if you only want to add a number at the end of popotnik's name then it isn't necessary to initialize a list with empty objects.
You could just add objects to list using a different constructor.
For example popotnik.add(new Popotnik("Popotnik"+(popotnik.size()+1))).
It's not pretty but I think initialization like this here is not necessary.
Got a quick question which I hope someone can answer. Basically I have a String variable which needs changing based upon the value in a combo box which has an event listener attached to it. However if I make the string final then it cant be changed, but if i don't make it final then eclipse moans that it isn't final. Whats the best (and simplest) work around?
Code is as follows
final String dialogOutCome = "";
//create a listener for the combo box
Listener selection = new Listener() {
public void handleEvent(Event event) {
//get the value from the combo box
String comboVal = combo.getText();
switch (comboVal) {
case "A": dialogOutCome = "a";
case "B": dialogOutCome = "b";
case "C": dialogOutCome = "c";
case "D": dialogOutCome = "d";
}
}
};
You can't.
Consider this:
the local variable exists as long as the method it's declared in runs.
as soon as that method invocation ends (usually because the method exists) the variable vanishes
the listener can (and usually does) live much longer
So what should happen when the method returned already and the listener tries to modify the local variable?
Because this question does not have a really good answer, they decided to make that scenario impossible by not allowing access to non-final local variables.
There are two ways around this problem:
try to change a field instead of a local variable (this probably also fits better with the life-time of the listener) or
use a final local variable, of which you can change the content (for example a List or a String[] with a single element).
These answers are all telling you how to make a flawed approach "work" at a superficial level.
The real problem is that you're trying to apply traditional, synchronous, "call/return" coding patterns to a library that doesn't support it. The real answer is that when the event you're waiting for happens asynchronously, the stuff you want to do after the event has to happen asynchronously as well. Understanding the event-driven nature of Swing is vital to being effective in it.
In other words, think of what piece of code will read dialogOutCome. Will some action be performed? If so, perform that action from the handler (or as a result of the handler) instead.
An alternative is to use this approach.
public class ReturnValueListener implements Listener {
private String dialogOutCome;
public void handleEvent(Event event) {
//get the value from the combo box
String comboVal = combo.getText();
switch (comboVal) {
case "A": dialogOutCome = " gay";
ase "B": dialogOutCome = " ugly";
case "C": dialogOutCome = " smelly";
case "D": dialogOutCome = " great";
}
}
public String getDialogOutCome() {
return dialogOutCome;
}
}
Listener selection = new ReturnValueListener();
//Set my listener somewhere,
....registerListener(selection);
//After my handleEvent is called
String dialogOutCome = selection.getDialogOutCome();
Just create simple StringWrapper object:
static class StringWrapper {
String value;
StringWrapper(String value) {
this.value = value;
}
}
and use it in type of field dialogOutCome:
final StringWrapper dialogOutCome = new StringWrapper("");
Listener selection = new Listener() {
public void handleEvent(Event event) {
String comboVal = combo.getText();
switch (comboVal) {
case "A": dialogOutCome.value = " gay";
case "B": dialogOutCome.value = " ugly";
case "C": dialogOutCome.value = " smelly";
case "D": dialogOutCome.value = " great";
}
}
};
You can use a setter for indirection. This will take away the final restriction (since the field you're accessing is 'this' instead of 'dialogOutcome'.
String dialogOutCome = "";
void setOutcome(String value){
dialogOutCome = value;
}
//create a listener for the combo box
Listener selection = new Listener() {
public void handleEvent(Event event) {
//get the value from the combo box
String comboVal = combo.getText();
switch (comboVal) {
case "A": setOutcome(" gay");
case "B": setOutcome(" ugly");
case "C": setOutcome(" smelly");
case "D": setOutcome(" great");
}
}
};
Workaround solution: Use StringBuilder instead of String. Of course, StringBuilder cannot be changed reference in the handleEvent, but we could invoke its method.
final StringBuilder sb = new StringBuilder();
Listener selection = new Listener() {
public void handleEvent(Event event) {
...
switch (comboVal) {
case "A": sb.delete(0, sb.length()).append("a");
case "B": sb.delete(0, sb.length()).append("b");
...
}
}
};
...
System.out.println(sb.toString());
Then you could get String value by StringBuilder.toString();
EDIT: Please provide the reason voting down this answer.
i'm having a bit of a problem. Here's the situation. I have an amount field in my main class that gets incremented when certain buttons are clicked on. There is a method provided through which you can delete any order (I'm basically programming for a restaurant terminal) and the amount gets decremented. The delete method is placed in another class.
public void posdel(int pos, JTextField amountFieldGot, int amountGot)
{
if(slist==null)
{
JOptionPane.showMessageDialog(null, "No order has been placed yet.",null,JOptionPane.WARNING_MESSAGE);
}
else
{
if(pos==1)
{
reductionAmount = (slist.quantity*slist.price);
amountGot = amountGot - reductionAmount;
slist=slist.next;
}
else
{
int i=1;
Node temp=slist;
Node prev=null;
while(temp.next!=null && i<pos)
{
prev=temp;
temp=temp.next;
i++;
}
if(pos==i)
{
prev.next=temp.next;
}
else
{
JOptionPane.showMessageDialog(null, "Invalid order", null, JOptionPane.ERROR_MESSAGE);
}
}
}
amountFieldGot.setText(Integer.toString(amountGot));
}
So basically, I have an amountField in my GUI that i pass as a parameter to the posdel method. I also pass the amount value as a parameter. The new amount that i get is amountGot after the deletion of the first order. ( I haven't written the code for other positions.)
Suppose the amount value i pass into the method is 30 (14+16) 14 = order 1, 16 = order2.
And my first order has a value of 14.
So amountGot = 30 - 14 which is 16.
And the amountField in the GUI gets updated to 16.
Now my order 2 becomes my order 1. And if i try to delete this,
my amountField gets update to 14. (30-16 = 14).
So i'm guessing the amount value stays the same itself as 30 and does not get updated to the new amountGot value. Can someone please help me solve this problem ?
below is the code for my delete button.
deleteButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
dishDelPos = JOptionPane.showInputDialog("Enter the position of the order to be deleted");
try
{
dishDeletePosition = Integer.parseInt(dishDelPos);
order1.posdel(dishDeletePosition, amountField, amount);
repaint();
}
catch(NumberFormatException ex1)
{
JOptionPane.showMessageDialog(null,"This is not a valid position");
}
}
});
A few things.
You can make the delete method in the class static. The you would reference it
value = MyClass.deleteMethod();
You can create a new class to perform the method
MyClass myClass = new MyClass();
value = myClass.deleteMethod();
You can do it using a pointer of sorts, by passing in a reference to an already existing instance of the class holding the delete method, to where you want to call it.
myFunction(MyClass myClass)
{
value = myClass.deleteMethod();
}
basically set up your function to return a value
public static int deleteMethod()
{
}
this function returns an int.
or if you need to return more than that then set the class up with global variables of information
class MyClass
{
public int value1;
public int value2;
public String value3;
public void deleteMethod()
{
//does something with global variables
}
}
now fetch the info after calling delete like so
Myclass myClass = new MyClass();
myClass.deleteMethod();
value1 = myClass.value1
value2 = myClass.Value2
value3 = myClass.Value3
I'm trying to find a way to pick what an array stores based on which button the user presses on a GUI.
Obviously this will not compile due to the variable name being the same.
The calculations are performed outside of the loop but use "values". I just want the user to be able to determine what values are set in the array based on what button they press. The obvious issue is not being able to use the name "values" twice, which is where I am having a problem as I have a for loop that requires the variable "values" and I don't want to have to be re adding the code several times for each data set when there is most likely an easy workaround that I am currently not seeing.
Just pull the declaration out:
double[] Xvalues = null;
if (e.getSource() == X1btn) {
Xvalues = new double[]{2001,350,799,1004};
}
else if (e.getSource() == X2btn) {
Xvalues = new double[]{5,62,28,500};
}
A better approach would be to subclass JButton and associate a set of values with each instance. To retrieve the button's values, include an accessor.
Example
public final class JArrayButton extends JButton{
private final double[] values;
public JArrayButton(double[] values){
this.values = values;
}
// ... other stuff (e.g. constructors)
public final double[] getValues(){
return values;
}
}
What if you create the array ourside the loop.
double Xvalues[] = new Xvalues[5]; //or whatever size you want
and then use if/else statement
if (e.getSource() == X1btn) {
Xvalues ={2001,350,799,1004};
} else if (e.getSource() == X2btn) {
Xvalues={5,62,28,500};
}