I have a linkedlist of Accounts, containing Employees and Managers (inheriting from Account). The problem is I have noticed the last added item seems to be overwritting the rest in the list. Why is it doing this? what I am doing wrong? thanks. I'll put my code below and console output. Sorry in advance if i am being really stupid and missing something obvious!
public class Database {
static List <Account> Accounts = new LinkedList<Account>();
public static void main(String[] args) {
Employee Geoff = new Employee("Geoff", "password1");
Manager Bob = new Manager("Bob", "password2");
Employee John = new Employee("John", "password3");
Accounts.add(Geoff);
Accounts.add(Bob);
Accounts.add(John);
list();
}
public static void list() {
for (Account u : Accounts) {
System.out.println(u);
}
}
Console Output is:
John, John, John
:(
Edit: code has been changed sorry guys!
public abstract class Account {
protected static String name;
protected static String passcode;
public User(String name, String passcode) {
this.name = name;
this.passcode = passcode;
}
}
Both manager and employee inherit from this so for manager:
public Manager(String name, String passcode) {
super(name, passcode);
}
Remove key word `static from declaration of fields and it will work fine.
static variables are associated with the class, not with object. Which means those fields are shared between each instance of this class.
Class variables (static) will only have a single instance that is shared between all instanciations of the class. That means that every time you say "this.name", it is semantically equivalent to saying "User.name", since "this" refers to the instance, not the class.
Change the class variables (static variables) to instance variables (non static variables) and everything will work as you expect.
Here is the documentation that explains class vs instance variables.
http://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html
Related
I am having trouble with a method that accepts two classes Pokemon as its parameters because it could be whatever pokemon battling, but if I try to catch the name of the subclass, such as Totodile, the superclass's name attribute is printed
in Pokemon.java:
abstract public class Pokemon {
private String name="POKEMON";
public String getName() {
return name;
}
}
in Totodile.java :
public class Totodile extends Pokemon {
String name = "Totodile";
}
in Geodude.java :
public class Totodile extends Pokemon {
String name = "Geodude";
}
in Battle.java :
public class Battle {
public void initiateBattle(Pokemon pokemon1,Pokemon pokemon2){
System.out.println(pokemon1.getName()+ " is battling against " + pokemon2.getName());
}
}
in App.java:
public class App {
public static void main(String[] args) throws Exception {
Geodude geodude = new Geodude();
Totodile totodile = new Totodile();
Battle battle = new Battle();
battle.initiateBattle(totodile, geodude);
}
}
The output is "POKEMON is battling against POKEMON"
, but how could I get "Totodile is battling against Geodude"
You can't "override" a field. When you write:
class Foo {
String name;
}
you are always declaring a new field. If your superclass also has a field with the same name, okay. Now your Totodile class has 2 fields, both named name, one of which has value POKEMON, the other has value Totodile.
Which one do you get when you write myPokemon.name? Depends on the compile-time type of myPokemon (the variable, not the object it is pointing at!) - and given that it is Pokemon in your code, you get the Pokemon version of the name field.
This notion that you have 2 fields with identical names is called 'shadowing'.
Shadowing is a mistake; do not do it unless you really, really know what you are doing.
The solution is therefore quite simple: Don't define a field named name in the Totodile class. Instead, set the value of the name field (which you inherited, so the Totodile class has that name field already, no need to make a second field with the same name and confuse things):
class Totodile {
{ name = "Totodile"; }
}
This somewhat weird syntax creates an instance initializer, as you need to stuff your code somewhere (you can't just start putting statements straight into a class, you need to brace em up). This is a tad odd, the more usual design is instead something like this:
abstract class Pokemon {
private final String name;
public Pokemon(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Totodile extends Pokemon {
public Totodile() {
super("Totodile");
}
}
This is what most java programmers do:
It forces subclasses of Pokemon to actually set the name properly. That default 'POKEMON' value is weird and no longer needed in the above strategy.
It avoids exotic code constructs to make it work. Constructors and abstract classes tend to be a lot more familiar to your average java coder than instance initializers.
Adds final in the appropriate places.
I've got the following code as an example(I've removed most of the code to make it easier)
Main class:
class TestStudent_3
{
// To update student name to other name
private void updateObject (Student s, String otherName)
{
s.setName (otherName);
}
public static void main (String [] args)
{
... All variables, methods and scanner inputs etc removed ...
Student s = new Student (name, gender, age, subject1, subject2);
s.displayStudentInfo ();
// If you wish to change one the fields, you need to use mutator methods
TestStudent_3 ts = new TestStudent_3 ();
ts.updateObject (s, "New Name");
s.displayStudentInfo ();
}
}
Student class:
class Student
{
private String name;
... All variables, methods and scanner inputs etc removed ...
public void setName (String name)
{
this.name = name;
}
}
My question is what's these few lines in the main method doing? And why can it update the existing record('s')
TestStudent_3 ts = new TestStudent_3 ();
What's this doing? Creating a new object ts?
ts.updateObject (s, "New Name");
Passing Student s object variables(contents) over to the updateObject method together with "New Name" string, why does this work?
Thanks in advance!
Break it down.
s.setName(otherName);
sets the Name in instance s to otherName
ts.updateObject (s, "New Name");
is acting like a proxy of sorts. It accepts an instance of s and the name and then
does the same thing in the updateObject method.
It seems you are confused why the ts object had to be created with TestStudent_3 ts = new TestStudent_3 (); in order to update the Student s.
updateObject is not static, and is part of the TestStudent_3 class, so in order to call that method, you need to have an object of the class and invoke it using the object, as you see with the ts.updateObject(s, "New Name") where ts is the object created just so you can use the method.
Realistically updateObject should be static the way it is written:
private static void updateObject (Student s, String otherName)
Because the ts object is not used at all for logic of the method, it is completely unnecessary. If it was static, then you would just be able to use updateObject(s, "New Name") instead without the ts. (although you might as well just call s.setName("New Name") directly).
You pass an object reference to object s into the updateObject method.
So the updateObject method call:
<reference to s>.setName(otherName)
or you can say:
s.setName(otherName)
That will change the name value of the Student s object.
In the next line in the main method you call:
s.displayStudentInfo();
Thats kind of equal to:
<reference to s>.displayStudentInfo();
If I create an object with a constructor in one class, and the constructor gives the object a property like 'name', is there a way to access specifically the String 'name' from another class? As in, if I have a method that I pass the objects to, and that method needs to access just the String 'name' from the constructor, how do I get to it?
This is probably a bad question that already has an answer, but since I don't know the right terminology to search for it I'm a bit stuck...
You cannot read parameters passed to a constructor from outside the constructor definition, unless that parameter is stored in a field of the class. Of course, if you create an instance of a class like MyClass myObject = new MyClass("Some String");, you can access some string in the scope of the code that created the object.
The ways that a field of a class, say the field fieldName from an instance myObject an a class MyClass can be accessed by another class are:
If the field is public, access by myObject.fieldName
If the field is protected, access it by subclassing MyClass
If MyClass has a getter for the field: myObject.getFieldName()
If the field is private and does not have a getFieldName() method, then it cannot be accessed from outside of the class.
Here are a couple of classes that I think demonstrate what you are wanting to do.
I have a Person class that has a name field and a Friend class that has a method called sayHello and it accesses the name property of Person.
public class Main
{
private static class Friend
{
public void sayHello(Person person)
{
System.out.println("Hello " + person.getName());
}
}
private static class Person
{
private String name;
public Person(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
}
public static void main(String[] args)
{
Person person = new Person("John Smith");
Friend friend = new Friend();
friend.sayHello(person);
}
}
I've been banging my head against the wall for a while with this one -
I have a method to create a list of objects of another class:
public List <PayrollRecord> processPayroll() {
List<PayrollRecord> payroll = new ArrayList<PayrollRecord>();
for (Employee employee : staff) {
PayrollRecord payRec = new PayrollRecord(employee.getName(), employee.calculatePay());
payroll.add(payRec);
}
return payroll;
}
staff is a list of Employee class objects that can be added to the list by this method:
public void addEmployee(Employee employee) {
staff.add(employee);
}
Employee is also an interface implemented by a few other classes - Manager, SalesAssociate and StoreEmployee. (Can you tell I'm trying to get through an assignment yet? Maybe some of you are familiar).
The PayrollRecord class looks like this:
package my.package.ext;
public class PayrollRecord {
private String employeeName;
private double currentPay;
public PayrollRecord(String employeeName, double currentPay) {
}
public double getCurrentPay() {
return this.currentPay;
}
public String getEmployeeName() {
return this.employeeName;
}
}
So within a test class, I should be able to create some variables for employee info, add some employees to staff, and then run the processPayroll method and do some asserts on it. My test class currently looks like this (and will need several more asserts. I'm not going to bother adding those until I can get this basic problem figured out):
#Test
public void testPayroll() {
List<PayrollRecord> list = store.processPayroll();
assertEquals(managerTestPay, list.get(0).getCurrentPay(), 0);
}
Based on the number of employees I have added elsewhere in the test class, there should be five. I can set list.get out of bounds and verify that there are indeed 5. However the problem is that all 5 records are null and 0.0. I should have a name and pay for each employee record. However the assertion error comes back showing me what I expected, but that the actual value is 0.0 (or null when I try list.get(0).getName()).
Thank you in advance for your help and wisdom.
And you set the members how ?
private String employeeName;
private double currentPay;
public PayrollRecord(String employeeName, double currentPay) {
}
In your constructor you need to assign to the members e.g.
this.employeeName = employeeName;
It's not enough to simply call the constructor with arguments. The arguments need to be used to populate the members of the class (either directly or via some manipulation).
A useful technique here is to set the member variables to be final. This means:
you can't change their values post initialisation
you have to initialise the values either immediately or in the constructor
You might think this is a limitation. However a lot of the time you'll find you only set this info once (I suspect for your assignment the name and salary will remain constant). If you need to change one or other it's easy to remove the final qualification.
private final String employeeName;
private final double currentPay;
public PayrollRecord(String employeeName, double currentPay) {
this.employeeName = employeeName;
// etc...
}
Ensuring that the members don't change means the class instance is immutable. This is a good thing generally. It's easy to reason about and debug immutable classes, and they're implicitly thread-safe.
You are not assigning the constructor arguments to class variables. Update your constructor as :
public PayrollRecord(String employeeName, double currentPay) {
this.employeeName = employeeName;
this.currentPay = currentPay;
}
I met such a Java problem about making instance variables static:
public class Student {
private static String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
and test:
public void testBadStatic() {
Student studentA = new Student("a");
assertEquals("a", studentA.getName());
Student studentB = new Student("b");
assertEquals("b", studentB.getName());
assertEquals("a", studentA.getName());
I think the second assertEquals will fail, since the name variable is static, so it already been assigned "a". However, the correct output should be true for the second assertion and false for the last assertion. Could anybody help me understand this?
Thanks.
I think you are confused between final and static variables.
Final (but not static) variables are tied to instances, and can get values in the constructor, or through static initialization.
Static variables are tied to classes, so they share the same value across one JVM instance for all instances of the given class.
Final static variables offer the most restricted of the two: it is effectivelz a constant: cannot be changed, and is the same for all instances of the given class.
Conclusion: the
this.name = name;
statement updates a value, that is tied to the class, rather than the indvidual instances, so after such a call, each instance will "see" that same value - the result of the last assignment operation.
public Student(String name) {
This is your constructor. Whenever you create an instance of this class, this "method" is called. However, static variables belong to the class. Thus, setting the static String name to a value changes it for all existing instances.
The second assert won't fail, because this line:
Student studentB = new Student("b");
Will change the name variable for all instances.
So when the second assert comes around, Student.name is "b". (Note that this is the Student class I'm referencing here).
The third assert will indeed fail, because the name for all instances (both studentA and studentB are now "b".
In other words:
Student studentA = new Student("a");
// studentA.name = "a"
assertEquals("a", studentA.getName()); // This passes
Student studentB = new Student("b");
// studentB.name = "b"
// studentA.name = "b"
assertEquals("b", studentB.getName()); // This passes
assertEquals("a", studentA.getName()); // This fails
Try :
public class Student {
private static String name;
public Student(String name) {
Studen.name = name;
}
public String getName() {
return Student.name;
}
}
A static variable is not "static" in the sense of "unchanging" - it means that all instances share the same variable, so the second constructor call replaced the value of the static variable with "b".
The second assertion will pass you assign "b" to your static variable in constructor. This is also why the third assertion will fail (expected "a", found "b").