(This question applies to the equivalent code in both Java and PHP)
I have a class like this:
class Foo {
private int $bar;
public function __construct(int $bar) {
$this->bar = $bar;
}
public function add(int $bar) : Foo {
return new Foo($this->bar + $bar);
}
}
Due to certain reasons (the reason is irrelevant to this question), cloning the object is a faster implementation than calling new Foo
, so I can change the add
function to this:
public function add(int $bar) : Foo {
$foo = clone $this; // equivalently, this.clone() in Java
$foo->bar += $bar;
return $foo;
}
If Foo
was final, this should not result in any BC issues. Unfortunately, this is not the case. In fact, there is a subclass of Foo
in the API:
class HeavyFoo extends Foo {
private SomeHeavyObject $object;
}
SomeHeavyObject
is a heavy class (as in it references lots of other data), and it should be garbage-collected as soon as unneeded to keep the memory usage low.
Meanwhile, there are lots of users writing this kind of code:
$added = getHeavyFoo()->add(3);
$someLongLivingStorage->insert($added);
With the assumption that Foo.add
will return a light (only containing an int
field) object, users insert the result of add
to a storage that lives much longer than HeavyObject
would. This is not exactly a memory leak, but greatly increases the memory consumption anyway.
However, according to OOP principles, returning a subclass instead of the superclass is totally fine.
Should I bump my semver major version because of this change?
(For those wondering why change to clone: I wrote a PHP extension such that if(ref_count_for($this) > 1) { $this = clone $this; }
, so no need to allocate a new object if $this
is a temporary that isn't referenced anywhere else, e.g. the intermediates in $foo->add(1)->add(2)->add(3)
)