Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

Consider the following class:

public class Cars extends Observable{

    private ArrayList<String> carList = new ArrayList<String>();

    public void addToCarList(String car){
        // ...
        hasChanged();
        notifyObservers();
    }

    public void removeFromCarList(String car){
        // ...
        hasChanged();
        notifyObservers();
    }

    public ArrayList<String> getCarList() {
        return carList;
    }    
}

As you can see, every time the carList is changed, I want to notify the Observers. If someone does getCarList().add(...);, this is circumvented.

How can I give read access to the carList (for iterating over it etc.) but prevent write access to it except for the special methods addToCarList and removeFromCarList?

I thought about this:

public ArrayList<String> getCarList() {
    return (ArrayList<String>)carList.clone();
}

but someone using my class would, when adding something to the clone of carList, not be informed that that's not the way it's meant to be done.

share|improve this question
2  
Although to me, extending from ArrayList<T>, overriding the add and remove functions to notify the observers, and making Observable an interface would seem like a simpler way of doing things. –  Zhuinden 2 days ago
    
Instead of providing method getCarList(), why not provide getCarAtIndex() and getCarCount() –  Will 2 days ago
    
@Will I like that one. You can even implement an iterator! Might make for a good answer actually. –  Cruncher 2 days ago
add comment

5 Answers

up vote 33 down vote accepted

You can return an unmodifiable view of it, changing the return type to List<String> instead of ArrayList<String>:

public List<String> getCars() {
    return Collections.unmodifiableList(carList);
}

Note that as Collections.unmodifiableList does only provide a view, the caller will still see any other changes that are made via addToCarList and removeFromCarList (which I'd rename to addCar and removeCar, probably). Is that what you want?

Any mutating operations on the returned view will result in an UnsupportedOperationException.

share|improve this answer
    
This is correct. Just make sure to make your carList private (even though it already is). If not, users can still be tempted to access the variable that way. See here: javacreed.com/modifying-an-unmodifiable-list –  dberm22 12 hours ago
    
@dberm22: It's already private in the question... I'm not sure why you've brought that up. –  Jon Skeet 12 hours ago
    
I know...I was just adding so that future readers don't see your correct example and then ruin it by overlooking the public variable. It was just a warning to future viewers, not a note to the OP. –  dberm22 12 hours ago
add comment

First, always avoid using concrete class at the left side of assignment and as a return value of method. So, fix your class as

public class Cars extends Observable{

    private List<String> carList = new ArrayList<String>();
    ........................

   public List<String> getCarList() {
        return carList;
   }
}    

Now you can use Collections.unmodifiableList() to make you list read-only:

   public List<String> getCarList() {
        return Collections.unmodifiableList(carList);
   }

BTW, if you do not really have to return List you can probably return Collection or even Iterable. This will make increase the encapsulation level of your code and make future modifications easier.

share|improve this answer
add comment

Jon Skeet's answer is excellent (as always) but the one thing it doesn't touch on is concurrency issues.

Returning an unmodifiable collection will still leave you with issues if multiple threads are accessing this object at the same time. For example if one thread is iterating over the list of cars and then at the same time another thread adds a new card.

You will still need to synchronize access to that list somehow, and this is one reason why you might consider returning a clone() of the list as well as or instead of just wrapping it in the unmodifiableList wrapper. You would still need to synchronize around the clone() but once the clone is completed and the list returned to the querying code it no longer needs to be synchronized.

share|improve this answer
    
Good point. Jon Skeet actually mentioned that Collection.unmodifiable() returns a view but it is not clear from his answer what it can cause in multithreaded environment. –  AlexR 2 days ago
add comment

I think you could probably make your Object implement the Collection-Interface, if it is in fact an ObservableList. It is a List and it should be Observable - so it should implement both interfaces.

You could even Extend List<..> because you just want to add extra functionality (observers) to the current functionality and your List can be used everywhere where a normal List could be used...

share|improve this answer
    
1  
Hmm... I see a completely different usecase here. It looks like in fact he wants to implement an ObservableCollection, which is observable and IS a Collection. i.e. should implement the Collection interface. Then he could do the same as the ReadOnlyList class and just throw exceptions on unsupported actions and notify listeners and so on... –  Falco 2 days ago
    
For a generic ObservableCollection if that is what he is making that would make sense, it really depends on what the class in question is doing but that's a valid point. Removed the downvote :) –  Tim B 2 days ago
    
@TimB In C#, a List is a concrete class, but in Java List is an interface, so I don't think that answer is relevant here. –  David Conrad 2 days ago
1  
@DavidConrad It really depends on whether what he is writing is a generic ObservableList or is another class that contains an ObservableList. Having said that the second case may well be best handled by creating the ObservableList anyway and then using that internally so actually I'm sort of changing my mind here. –  Tim B 2 days ago
add comment

use Collections.unmodifiableList(list) as it provides a new List object which cannot be modified , it would throw an UnsupportedOperationException while trying to update/add/delete objects list.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.