سؤال

What I am trying to do is sum up the impact values in Hazard Class

For example it will go through a list of occupants, find a hazard and get the impact amount from it. Then sum the total impact of all hazards and return that value to me.

Below I have the Cave class, Hazard class and Abstract Occupant class.

When adding a hazard to the cave, it becomes an occupant in the HashSet. When trying to get the energy levels with the getImpact() method, the method cannot be accessed as it is in Hazard and not Occupant.

I have two other classes that also extend of Occupant. Player and Item.

I cannot find a way to keep the hazard as a Hazard class when adding to the HashSet so that getImpact() method can be used.

This also needs to cater for the other classes Player and Item when adding to the HashSet.


public class Cave {

HashSet<Occupant> occupants;
private double impact;

/**
 * Creat a new Cave instance with no occupants.
 */
public Cave() 
{
    occupants = new HashSet<Occupant>();
}

/**
 * Adds an occupant to a Cave if the occupant is not already there and
 * if cave currently has fewer than the maximum number of occupants.
 * @param occupant, the occupant to add
 * @return  true if successfully added
*/
public boolean addOccupant(Occupant occupant) {
    boolean validNewOccupant = occupant != null;
    boolean enoughRoom = occupants.size() < MAX_OCCUPANTS;
    if (validNewOccupant && enoughRoom) {
        validNewOccupant = occupants.add(occupant);
    }

    return validNewOccupant && enoughRoom;
}

/**
 * Gets the sum of the impact from all hazards in the cave
 * @returns hazardEnergyImpact
 */
public double getHazardEnergyImpacts(){
    double energyImpact = 0.0;
    for( Occupant occupant : occupants ){
        if(occupant.toString() == "!"){
            energyImpact += occupant.getImpact();
        }
    }
    return energyImpact;
}
}

public abstract class Occupant {

private Address address;
private String name;

/**
 * Construct an occupant for a known address & name.
 * @ param row, row of address  
 * @ param column, row of address.
 * @ param name, occupant's name
 */
public Occupant(Address address, String name) {
    this.address = address;
    this.name = name;
}

@Override
public String toString(){
    return "";
}
}

public class Hazard extends Occupant  {

private String longDescription;
private double impact;

/**
 * Construct a hazard with know attributes
 * @param row
 * @param column
 * @param name
 * @param longDescription
 * @param impact
 */
public Hazard(Address address, String name, String longDescription, double impact) {
    super(address, name);
    this.longDescription = longDescription;
    this.impact = impact;
}

@Override
public String toString(){
    return "!";
}

/**
 * gets impact amount
 * @returns impact
 */
public double getImpact(){
    return this.impact;
}
}
هل كانت مفيدة؟

المحلول

When iterating over your occupants you can check to see if each item is a Hazard like so:

for(Occupant occupant : occupants){
    if(occupant instanceof Hazard){
        Hazard hazard = (Hazard) occupant; // now it's safe to cast
        double impact = hazard.getImpact();
        // do what you want with impact
    }
}

نصائح أخرى

Another option is to add the getImpact() method to Occupant, e.g.,

public double getImpact() {
    return 0.0;
}

whereas Hazard's @Override implementation of getImpact() would just return its impact instance variable as you already have it set up. Then, your loop is simplified to:

public double getHazardEnergyImpacts() {
    double energyImpact = 0.0;
    for( Occupant occupant : occupants ) {
        energyImpact += occupant.getImpact();
    }
    return energyImpact;
}

Should you need to extract to an appropriate interface abstraction later, it's a good thing modern IDEs make that easy.

Jeremy beat me to it.

However, instanceof isn't always the best solution. In this case though, it's a fix.

I would actually recommend using interfaces here for the behaviors instead of using an abstract class. However, if you must use an abstract class, a more efficient way to do this would be to simply create abstract methods that you want to use in your child classes. You would have to override them all in each child, but you don't have to implement them in every case.

I would use the Visitor pattern here.

public interface Occupant {
  void interact(Player p);
}

public class Player {
  public void handleInteraction(Hazard hazard) {
    // add code here
  }
  public void handleInteraction(Person person) {
    // add code here
  }
}

public class Hazard implements Occupant {
  public void interact(Player p) {
    p.handleInteraction(this);
  }

  public double getImpact(){
    return this.impact;
  }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top