This site requires JavaScript, please enable it in your browser!
Greenfoot back
getreides
getreides wrote ...

2015/1/29

Getter/Setter methods in Superclass

getreides getreides

2015/1/29

#
I'm currently working on an abstract Superclass (let's call it Enemy) to provide methods for its subclasses (i.e. minor) like setHealth:
public abstract class Enemy extends Actor
{

private double health = 1.0;


       public void setHealth(double damage)
       {
             health = health - damage;
       }


}
However, whenever an actor of another class (i.e. bullet) accesses an object of minor - or any other subclass which also has the field health - , the field of the superclass is referenced and changed, not the one of the subclass object. I understand that this is the way java works (the compiler will always look in the direct (sub)class of the object for the respective method, and will look on the class above if it doesn't find the method). So to solve this problem, I would have to write the method in the very subclass, which would then overwrite the one in the superclass. Regarding this, how would one write a super method that references/changes the fields of the respective subclass? How would one write a super method like getX() or setLocation()?
danpost danpost

2015/1/30

#
getreides wrote...
How would one write a super method like getX() or setLocation()?
Those methods are already super methods located in the Actor class and can be used for any object created from any subclass of Actor.
how would one write a super method that references/changes the fields of the respective subclass?
A non-private field declared in a super class is given to all objects created from its subclasses. A non-private method in a super class can be called on any object created from its subclasses. Do not re-declare the fields declared in a superclass in its subclasses -- a method cannot change any field declared in one of its subclasses.
davmac davmac

2015/1/30

#
However, whenever an actor of another class (i.e. bullet) accesses an object of minor - or any other subclass which also has the field health - , the field of the superclass is referenced and changed, not the one of the subclass object.
The way you say this makes me wonder if you really understand what is happening. Yes, the field is declared in the superclass, but every instance of the superclass - including instances of any subclass - have their own "copy" of the field. There's normally no need to have a new field which does the same thing in the subclass. To put it another way: your object already has a 'health' field, why should your 'setHealth' method change a different field?
getreides getreides

2015/1/30

#
Hello both, thanks for your quick reply! I think I begin to understand: A field declared in a super class is inherited to all objects of the super class and all its subclasses. As my super class Enemy is an abstract class, this will only apply to object of the subclasses, correct? However, Danpost described this field as non-private - but as you can see in the code, it is indeed a private field - am I missing something there? However, let's say have field like health in the super class and I'd like to give it a different value for different sorts of enemies (and therefore object of different classes) - if I understand the two of you correctly, this is actually not advisable, as a field declared in a super class should not be redeclared in one of its subclasses, correct?
danpost danpost

2015/1/30

#
Actually, as private, then can only be accessed in the class it is declared in (the superclass); as public, access is granted to all classes that have access to the superclass (which can be any class within the project). There is also a protected modifier, which will grant access only from the superclass and its subclasses.
davmac davmac

2015/1/31

#
A field declared in a super class is inherited to all objects of the super class and all its subclasses.
Technically the field is not inherited if it is private (at least by the Java definition of "inherited"). But yes, subclass instances will each have their own value for this field.
However, Danpost described this field as non-private - but as you can see in the code, it is indeed a private field - am I missing something there?
Danpost was not describing your field, he was explaining the rules for inheritance. Part of the cause of confusion is that Java defines "inheritance" to mean something a bit different from the common understanding of the term. Many people would say that private fields are inherited, but they are inaccessible to the subclasses. By the Java definition, though, private fields are not "inherited", but they do have a unique value for each instance of the class in which they are declared (unless they are also "static"), including subclasses.
However, let's say have field like health in the super class and I'd like to give it a different value for different sorts of enemies (and therefore object of different classes) - if I understand the two of you correctly, this is actually not advisable, as a field declared in a super class should not be redeclared in one of its subclasses, correct?
You should generally avoid re-declaration, but that isn't the same as giving it a different value. You should feel free to change the value from the subclass. You can even potentially keep the field private and have protected accessor methods to allow the subclasses to change its value, or even have a constructor which takes the desired value as a parameter.
getreides getreides

2015/2/1

#
Hello guys, thanks again for your quick replies! I'm afraid this feels a lot more complicated to me than I thought, thanks for your patience :-). Right now, as described above my code for Enemy (super class) and Minor (sub class) looks like this for the super class...
public abstract class Enemy extends SmoothMover
{
        private double health = 100.0;


        public void setHealth(double damage)
                    {
                        health = health - damage;
                        if (health <= 0)
                        {
                            getWorld().removeObject(this); 
                        }

                    }
}
...and for the subclass:
public class Saw extends Enemy
{
      private double health;       // this a redeclaration davmac wrote 
      about, which should be avoided - correct?
}

      public Saw (double health)
      {
             this.health = health   // health is redefined (here with a value of 150.0) 
             by the constructor - 
             but this is only possible as I redeclared health above - 
             is there any other way to reset the 
             value of this field without redeclaring it? 
             davmac wrote about a protected accessor 
             method - what would this look like?
      }
When an object of a weapon class (as described above, i. e. a Bullet object) accesses an object of Minor, the field declared in the super class (health = 100.0) is changed, health = 150.0 remains untouched. Is there any way to change that (that 150.0 is accessed/changed, instead of the hidden field)? What would have to look my code like in this example? Thanks in advance!
danpost danpost

2015/2/1

#
Just change 'private' in line 3 of your Enemy class code above to 'protected':
protected double health;
and, yes, remove line 3 in your Saw class above. You should not have to use a parameter to the constructor to set the value of the 'health' field. Because the constructor is for that type of object, you can set it directly to its value there. That is, for example, you can change the constructor of your Saw class code to this:
public Saw()
{
    health = 150.0;
}
Only Saw objects will get this value for 'health'. Do the same in your other subclasses of Enemy using their appropriate values.
davmac davmac

2015/2/1

#
As an alternative, you can leave 'health' as private. Yes, remove line 3 in Saw. You don't need a parameter to the Saw constructor, but instead, add one to the Enemy class. Your Enemy code becomes:
public abstract class Enemy extends SmoothMover
{
        private double health;
 
        public Enemy(double health)
        {
            this.health = health;
        }
 
        public void takeDamage(double damage)
        {
            health = health - damage;
            if (health <= 0)
            {
                getWorld().removeObject(this);
            }
        }
    }
}
Your Saw code becomes:
public class Saw extends Enemy
{
      public Saw ()
      {
             super(150.0);
      }
}
Note, I renamed your 'setHealth' method to 'takeDamage' because it doesn't set the health - it reduces it. Your method names should reflect their function. From your comments:
is there any other way to reset the value of this field without redeclaring it?
Yes, by calling an accessor methods (specifically the 'setter' method).
davmac wrote about a protected accessor method - what would this look like?
If you had accessor methods in Enemy, they would look like this:
    // "setter" for health
    protected void setHealth(double health)
    {
        this.health = health;
    }

    // "getter" for health
    protected double getHealth()
    {
        return health;
    }
You don't necessarily need these though, especially if you use the Enemy constructor with a parameter as above. I also fixed your indentation. You can do this easily yourself (press ctrl+shift+I), so you should do so before you post code here.
getreides getreides

2015/2/4

#
Hello both, thank you again for quick and detailed answers! I've chosen to use the super keyword as described by davmac, and it does work!
You need to login to post a reply.