4

Imagine any Java class which is entirely immutable. I will use the following as an example:

public class Point2D {
  public final int x;
  public final int y;

  public Point2D(final int x, final int y) {
    this.x = x;
    this.y = y;
  }
}

Now consider adding an operator on this class: a method which takes one or more instances of Point2D, and returns a new Point2D.

There are two possibilities for this - a static method, or an instance method:

public static Point2D add(final Point2D first, final Point2D second) {
  return new Point2D(first.x + second.x, first.y + second.y);
}

or

public Point2D add(final Point2D other) {
  return new Point2D(this.x + other.x, this.y + other.y);
}

Is there any reason to pick one over the other? Is there any difference at all between the two? As far as I can tell their behaviour is identical, so any differences must be either in their efficiency, or how easy they are to work with as a programmer.

0

3 Answers 3

4

Using a static method prevents two things:

  • mocking the class with most mocking frameworks
  • overwriting the method in a subclass

Depending on context, these things can be okay, but they can also create serious grief in the long run.

Thus, me personally, I only use static when there are really good reasons to do so.

Nonetheless, given the specific Point2D class from the question, I would tend to actually use the static methods. This class smells like it should have "value" semantics, so that two points for the same coordinates are equal and have the same hash code. I also don't see how you would meaningfully extend this class.

Imagine for example a Matrix2D class. There it might make a lot of sense to consider subclasses, such as SparseMatrix for example. And then, most likely, you would want to override computation intensive methods!

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

2 Comments

Hello you :) We spend our time to tell "avoid static". That is all the same terrible...
Guten tag Sehr geehrter GhöstKat : ) Much better. Thanks ! I see that you follow that carefully. I will do. I hope that he will say that will be firm about the main target : reforming !
2

There is no practical difference between the two. Where it matters most is in the area of OO design and readability.

The static version of the operation seems more aligned with the static factory pattern. In addition to using a common design pattern, it is a clear creational design, which seems to meet its intent: to create a new object.

On the other hand, instance methods creating new objects are very practical when it comes to immutable objects. The best example of this is the String methods (String.concat(string), etc.). In my opinion, this is more a question of practicality (you don't want to mutate the state of the object; you need to augment the it, but the operation has to result in a new instance).

Is there any reason to pick one over the other?

There may be cases where one fits better than the other (for example, I'd prefer the static method to the instance version in a stream pipeline's reduction - as an example), but there is no evident, absolute preference to be claimed here. So...

  • I would use the static method for factory operations (although I'd call the method something more like create..., newInstance... for clarity)
  • I would use the instance method for transformations operations that return new instances to avoid mutating the object.

3 Comments

Are you sure you mean you'd prefer the static version of a stream pipeline reduction? I assume we're talking about data.stream().someoperation().someotheroperation().reduce() here, which is clearly better as an instance method due to the chaining syntax.
@ChristopherRiches No, that was slightly misunderstood. I meant that I'd prefer the static version in a reduction operation. I mean as the binary operator: ...stream().reduce((a,b) -> Point2D.add(a, b)) - even if reduce(Point2D::add) would work with either, I think.
String.join isn't a great example - it isn't an instance method.
1

First and foremost, if it is an immutable make it unsubclassable to others. Usually final is used although you can hide the constructor. Not particularly relevant in this case, but static creation methods allows common values to be reuse instances, specialist implementations to be selected and the ugly diamond (<>) notation to be elided. (If you call your static creation method of it is clear to use when qualified with the type name.)

Addition is usually written as infix. If there are subexpressions involved this will make the client code look much better, though the Java syntax will still force you to have parentheses everywhere. A static method requires qualification or an import static for the client (the latter not really helpful if the method has a name like and, and 'import *' is bad if there other static method that don't make sense without qualification).

Reserve static methods for cases where the object is, in a sense, incidental to the function. For example String's join and format.

As for testing, it should not be necessary to mock a value class or static method. Immutable types should have trusted implementations and therefore not be subtypable by others.

Comments

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.