2

i'm having some trouble with returning objects and lists of objects. To problem is I want to return a object in my example an developer but I don't want to give the reference. What's the best approach for this? First i thought just returning a new Developer:

public Developer getDeveloper(int index){
    Developer d =  developers.get(index);
    return new Developer(d.getName());
}

But this looks good for simple objects but what if i have a developper object that has a instance field that is also a object or a list?

An other question is about lists i return a list of developers:

public List<Developer> getDeveloppers(){
    return Collections.unmodifiableList(this.developers);
}

So the list is now non modifiable but I think the objects are not... how can i return a list with unmodifiable objects?

The example code:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Project {

    private List<Developer> developers;
    private String name;

    public Project(){
        this("DEFAULT");
    }

    public Project(String name){
        this.setName(name);
        this.developers = new ArrayList<Developer>();
    }

    public List<Developer> getDeveloppers(){
        return Collections.unmodifiableList(this.developers);
    }

    public Developer getDeveloper(int index){
        Developer d =  developers.get(index);
        return new Developer(d.getName());
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }   
}

The developer class, note this one has no instance object:

public class Developer {

    private String name;

    public Developer(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }
}

Help would be appreciated! Regards

1
  • "Objects" are not values in Java (there are no "object types" in Java). Everything that is not a primitive is a reference. You cannot return "an object" or "a list of objects". You can only return "a reference" or "a reference to a list of references". Objects can only be manipulated through references.
    – user102008
    May 20 '15 at 21:32
4

You can make the objects immutable:

public class Developer {

    private final String name;

    public Developer(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }

}

Then there will be no issue with returning the reference.

Otherwise you can map each developer to a unmodifiable view of itself:

public Stream<IDeveloper> getDeveloppers(){
    return this.developers.map(dev => dev.unmoddifiableView());
}

Where unmoddifiableView() returns an instance of IDeveloper that doesn't allow changes:

private static IDeveloper readOnlyView = new UnmodifiableDeveloper(this);

public IDeveloper unmoddifiableView(){
    return readOnlyView;
}

And UnmodifiableDeveloper delegates all getters to the constructor parameter.

7
  • What if the object have setters to change the name?
    – John
    May 20 '15 at 8:45
  • @John then you need the read-only view method in the second part of my answer. That view will have no setters. May 20 '15 at 8:47
  • The second example appears to be using Java 8 features. Not everyone has updated to Java 8 yet - Java 6 and 7 are still both fairly common in my experiences. Also, I don't know where the unmoddifiableView() method comes from - I don't see that in any documentation for any version of Java and it's not in the provided class, either.
    – Thomas Owens
    May 20 '15 at 12:16
  • @ThomasOwens The last sentence explains what unmoddifiableView is. From the use of the word "otherwise", I think it's clear that this is an alternative to the code snippet above, so there's no reason to think there wouldn't be a method added. May 20 '15 at 12:23
  • 1
    @ThomasOwens That grammar structure tends to happen when I change my mind mid answer, I clarified it some. May 20 '15 at 13:09
3

There's no direct way to do what you're describing. However, you have a few options which will hopefully achieve what you want:

  1. Have an interface which contains only the accessors, and not the mutators, and make sure your return type is of the interface rather than the concrete class.
  2. Have a way of cloning a class- this might be an external method, or a .clone() method on the class. This should return a new instance of the class, but the state representing the same information (e.g. the name would be the same). Then instead of returning the original class, return a copy. These will still be mutable, but changes to these instances won't affect the originals.
  3. Have a second class which represents an immutable "view" on the first class. This is what ratchet freak describes in his answer. Again, you will need some method which can create an instance of the view from an instance of the original class.
  4. Design your classes so that you don't need to do this.

Assuming these are classes you control, 4 is my overwhelming favorite option, and should definitely be your first aim until you have a good reason to rule it out. Some reasons:

  • 1,2, and 3 all require annoying additions to your code (interfaces or methods). Especially with 2 and 3, if this is something that you do a lot throughout your code, you're creating significant maintenance work for yourself.
  • A class should be responsible for its own encapsulation. You're turning this idea on its head by making the class open, and having it depend on the kindness of strangers to find ways to pass it around which will give it some protection. Now instead of having this responsibility centralized in one place which will always take care of itself, every time you use this class in a different place, you as a developer have to remember these extra rules about how to use and pass it around.
  • One really useful aspect about encapsulation is that it allows a class to make assumptions about its state, as long as it also takes responsibility for keeping these assumptions true. An example might be a Fraction class. If this class manages its own encapsulated state and keeps itself in a reduced form, then its equals method can look like:

    equals(Object obj) {
        return obj.numerator = numerator  && obj.denominator = denominator;
    }
    

    If numerator and denominator weren't encapsulated though, it couldn't be sure that it was in a reduced form any more and would have to use more complex logic to ensure that, for example 1/2 and 2/4 compared as equal. This is called a class invariant. Just hoping that every time you use your class you'll remember to protect its invariants is a sure route to bugs and probably to horribly coupled code. This is another facet of the previous bullet point- you're turning the responsibility of encapsulation inside out if you rely on every class which uses this one to remember to call .clone() or whatever.

If you do find yourself in a situation where you have to do this (or the costs of not doing it are too high), then all other things being equal I'd recommend option 1, as it's a relatively natural use of interfaces.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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