0

I want to make a getter that doesn't allow the caller to edit the returned object.

Using a List as an example (though I would like the answer to apply to any other type as well), this is the usual approach for returning and for editing an attribute:

class MyClass {
    private List<String> strings;

    // to get the whole object
    public List<String> getStrings() {
        return this.strings;
    }

    // to alter the object
    public void addString(String newStr) {
        this.strings.add(newStr);
    }

    //...
}

However, this doesn't prevent that some other class from doing this

myClassInstance.getStrings().add("that's a dumb implementation, bro");

and that would be kind of rude since I created addString() for that specific purpose.

I would rather if other classes would only use the getStrings() method for reading, because there might be a similar case where I don't want to implement the addString() method. In that situation, other classes are able to edit strings anyway through the getter, but I still want to be able to edit the object privately in the C class.

I know this wouldn't be a problem if the attribute was a primitive type since those are saved directly in the instance, but objects are references to memory, so any class that's able to get its hands on those references can edit it if the object type allows that.

  • Can I just trust that other classes won't try to edit my object through the getter?

  • There's the option of cloning it (some classes may override the clone() method), but is this a good use for clone()? What are the best practices of cloning an object?

  • Is it worth it to create a custom class (called ReadOnlyList, for this example) that is only writeable in the constructor (like this), then copy my strings to a new ReadOnlyList, and return that?

  • Also, should objects provide a method that returns a non-writeable clone of the object to solve this?

Tovar
  • 99
  • 2
  • 6
  • Within the getStrings make a copy of the list and return it. – Charchit Kapoor Jun 11 '21 at 15:48
  • To stick with your example, you could give `addString()` a more restrictive access modifier. So you could prevent write access from other packages. –  Jun 11 '21 at 17:46

2 Answers2

2

You can have getStrings return an unmodifiable list.

public List<String> getStrings() {    
    return Collections.unmodifiableList(this.strings);
}

https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#unmodifiableList(java.util.List)

Micky Loo
  • 1,433
  • 9
  • 7
  • that works great for `List`s, but I was expecting a more general answer that would also apply to other types, including custom objects. maybe I should have specified that the `List` was just a common example for the problem – Tovar Jun 11 '21 at 17:10
0

Can I just trust that other classes won't try to edit my object through the getter?

No.

There's the option of cloning it (some classes may override the clone() method), but is this a good use for clone()? What are the best practices of cloning an object?

The oracle docs provide a proposed strategy:

  • Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods. (https://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html)

Is it worth it to create a custom class (called ReadOnlyList, for this example) that is only writeable in the constructor (like this), then copy my strings to a new ReadOnlyList, and return that?

In this case not (see Micky Loo's answer). However in a more special case yes (if you have to guarantee immutableness and can not copy the object).

Also, should objects provide a method that returns a non-writable clone of the object to solve this?

You can not create a const return value in Java. see: Const return values in Java

Sebphil
  • 488
  • 5
  • 12