0

I have two classes: Apartments and Building. The Apartment class:

public class Apartment {
    private String familyName;
    private int rent;
    private boolean isRented;
}

The Building class has an Apartment array, and a numOfApartments variable which indicates how many apartments the building has, and also a FINAL variable who indicates the max number for apartments in one building.

private Apartment[] apartments;
private int numOfApartments;
private final int MAX_APARTMENTS=20;

public Building() {
    this.apartments=new Apartment[MAX_APARTMENTS];
    this.numOfApartments=0;
}

Now I want to make a method which raises the rent for an apartment by an amount which is taken as parameter:

public void raiseRent(int amount) {
    for (int i=0; i<numOfApartments; i++) {
        apartments[i].setRent(apartments[i].getRent() + amount);
    }
}

the addAppartment method:

public boolean addApartment(Apartment a){ //adds an appartment to the building if
                                           //there's enough space in the array.
        if (this.numOfApartments<this.MAX_APARTMENTS)
        {
            this.apartments[this.numOfApartments]=new Apartment(a);
            this.numOfApartments++;
            return true;
        }
        return false;
    }

The apartment class string ToString method:

public String toString()
{
    if (this.isRented) //if the apartment is rented, prints:
    {
        return "Apartment rent: " + this.rent + ". rented to " + this.familyName + " family.";
    }
    else
        return "Apartment rent: " + this.rent  + ". not rented yet.";
}

The main is:

String familyName; int rent;
System.out.println("enter family name and rent by this order");
familyName=scan.next();
rent=scan.nextInt();

Apartment a=new Apartment(familyName,rent);  //build new apartment object.
Building b=new Building();
System.out.println(b.addApartment(a));
b.raiseRent(200); //initiate the raiseRent method.

Everything here works, but when I write this:

 System.out.println(a);

The rent is shown as the old rent, not the updated one. When I write this:

System.out.println(b.apartments[0]);

It works.

Why when I write System.out.println(a); it doesn't work?

Thank you for the answer.

7
  • 1
    Please show the code for addApartment. Commented Feb 2, 2013 at 15:06
  • @dasblinkenlight method added. Commented Feb 2, 2013 at 15:12
  • Why this.apartments[this.numOfApartments]=new Apartment(a); rather than apartments[this.numOfApartments] = a;? Commented Feb 2, 2013 at 15:14
  • 1
    @HovercraftFullOfEels I suspect this is some kind of homework or other learning exercise to teach pass-by-value. Commented Feb 2, 2013 at 15:16
  • @HovercraftFullOfEels - it fixed the problem! thank you. can you please explain why did it create the problem in my code? Commented Feb 2, 2013 at 15:17

2 Answers 2

1

In your

public boolean addApartment(Apartment a) method:

this.apartments[this.numOfApartments]=new Apartment(a);

Creates a new apartment which appears to be constructed from an existing apartment. This is a copy.

After you raise the rent, the

Apartment a=new Apartment(familyName,rent); //build new apartment object.

That you passed in to

b.addApartment(a)

Will not have an updated rent. This is because Java is pass-by-value. It's a "new" int in the new Apartment your Building Apartment[] collection contains, even if you set it from an existing one (this.rent = other.rent)

Sign up to request clarification or add additional context in comments.

7 Comments

And my new code: this.apartments[this.numOfApartments]=a; sends a copy of the a object to my array, and not the original?
Not quite - it stores a copy of the value of a - which is a reference. The object (instance) remains the same and is now shared within the Building.apartments collection, and whatever part of your program all this is being called from (e.g. main). This means that changes you make to the apartment either from the building or from a would be to the same object instance. As @dasblinkenlight pointed out, this isn't always a good thing - sharing state (access to objects) can present its own set of challenges.
So does it mean I have now created aliasing for the a object? If that the case, that is not what I wanted to achieve... Is there a way to make my goal without aliasing?
What is the goal? I mean, you can either alias the a object and share it inside and outside of the Building instance or you have different Apartment instances and that's that. Do you see what I mean? Perhaps if I better understand your goal I can answer you more clearly.
First, I want to add the a apartment to my Building array. The a object is an apartment which I recieve as a parameter. Then, I want to raise the rent of the apartments in the building, including a, and print the new rent. Thank you again.
|
1

The problem is in your addApartment method: rather than adding the Apartment object passed in to the array, you add a brand-new object created with new by calling a copy constructor. This breaks the connection between the variable called a that you passed in and the element at index zero of the apartments array, explaining the difference in printed values.

The copy semantic that you have in the addApartment is actually a good thing: it saves you from unusual aliasing later on, because the implementation is no longer dependent on the callers to pass distinct objects. Changing the code to

apartments[this.numOfApartments] = a; // No copy

creates problems, because Apartment is mutable. Consider this call sequence:

Apartment a = new Apartment();
for (int i = 100 ; i <= 1000 ; i += 100) {
    a.setRent(i);
    building.addApartment(a);
}

One would expect ten apartments, with rents 100 to 1000 to be added. With a no-copy change, however, you would end up with ten identical apartments, all with rent set to 1000. This is because under the hood all ten items in the apartment array would reference the same Apartment object - the one you created before the for loop. This causes numerous headaches down the road, so it is better to keep the copying code in place, and know that rent increases should be checked on the objects inside the building, not on the objects that you passed into the addApartment method.

6 Comments

The new Apartment is constructed with a reference to the existing Apartment - assuming the rent, family name and such are copied out, you haven't explained why this doesn't work.
@Doug Still it's a new object. The rent is raised on the copy apartment object, not on the original one. Once the copy is constructed, it lives its life independently of the original that has been passed into new Arartment(a).
Precisely. Your answer doesn't actually explain why it works that way. int x = 0; int y = x; y = 1 can let x = 0 or x = 1 depending on the semantics of the language. The fact the question exists inherently suggests that the poster is trying to understand how the pass-by-value semantics work in Java. Your answer doesn't explain why, it just says, "it's good that it doesn't work the way you expected" in so many words. The copy semantic is inherent in Java for a primitive type, so it wasn't intentional, hence the confusion.
@Doug I do not know if copying was or was not intentional: the code of addApartment could be coming from somebody else as part of a learning assignment.
That's right, but since the poster is performing a copy-construction of an object, then expecting the changes made in the copy to reflect in the original, is suggests a lack of understanding of how Java works with respect to these things. Therefore, I consider your answer to be lacking if it does not address those concepts. You've said what happens, you've said it's good it happens, but you haven't said why it happens.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.