With this material, we begin a series of “Java Practices” publications by Andri Osta, within the framework of which you will get acquainted with the best practices of Java-development, and both novice and experienced developers will be able to learn something new and useful.
So, make some coffee and sit back, here we go.
Use static factory methods instead of constructors.
For example, we have an Iphone class:
To hell with the extra code? (Screw the extra code? / Hang with the extra code?)
Having checked this example, you will say what the hell, it is so much better. I do have a constructor for creating objects, why the hell do I need to create an extra code for the same work that the constructor does and call the constructor anyway?
Okay, I’ll explain why and what are the benefits here. In fact, doing so is not always necessary. It is only useful if you have many parameters and constructors with different parameters.
In front of you, there is an Iphone class that has two constructors. One of them is responsible for the color, and the second one for the version. That is why, to make a large number of constructors of this type – is not always convenient. Now we remember that we have only two constructors, but when there are ten of them, I can bet you are going to get confused with them.
It won’t take long to find an example. Have a look at BigInteger.
If you look at the structure, you’ll see that there are many different constructors and you can really get confused remembering what is intended for what. That is, you need to go to the documentation and read what’s going on there.
If you have many constructors like that, then in such situations you need to use static factory methods that provide the following benefits:
– In the names
Unlike constructors in which the name is the same as in the class, you can use your name in the static factory method:
So what are the benefits? Now, when I want to use iPhone in the code, I’ll take Iphone.getIphoneX() or Iphone.getGoldIphone(). That is, we give a description in the name itself, and we don’t need any documentation at all. The one who will use your API will see and understand it intuitively (yes, we are creating a gold iPhone or iPhone 10). So this is exactly that what you should’ve written in the documentation, you write in the method name which is intuitively clear, and for such cases you need to use static factory methods.
Again, finding an example won’t take you long. Developers of the BigInteger class replaced some of their constructors on static factory methods eventually.
Each time you call a constructor, you create a new instance in memory, but on the other hand, why should you create 100 gold iPhones?! To avoid this, you can create a cache, for example: a map.
I suppose, everyone knows how the cache works. That is, if an object is present in the cache, then we use it, but if it is not, then we create a new one.
The advantage is that we can return the various inherited specific implementations. This gives you great flexibility. Let’s imagine, today we give the Iphone plus, and tomorrow there appear new requirements and we give the Iphone SE. The bottom line is that it gives us the opportunity to expand.
– We can’t succeed if we do not have a public constructor.
Let’s think about creating objects for a while. So, the more objects we create, the slower our program will be.
Let’s start with primitive.
I’m sure that all the time everyone creates it in a second way and it is good, it is generally accepted 🙂
But not everyone knows why 🙁
So if we create a string in a second way, then it is created in a stream pool, that is, if we create the same one, the new one will not be created and will be taken from one place. We will once again use the readymade string. There will always be created a new string right in the memory if we use the first method. The first method: excessive memory – more time.
The next example:
There were added such objects as wrappers to Java 5. This is when we write Long with a capital letter for example. You can use them, but carefully, for instance, with collections.
Each time creating such a long is a grind that takes a certain amount of time. For instance, we summed numbers from 0 to the max integer value. It took more than 8 and half seconds. Now, let’s change it into a primitive long and check it out.
Well, I’m sure that everyone knows about it, but just in case, I’ll tell you now, what’s the point.
Please, note that when I use a primitive long, everything happens eight times faster. 1 second instead of 8.
Why is it so? Everything is simple – since with a big long there is always created a new object, so, this is a costly operation and this should always be avoided.
Now I will explain to you why I’m writing this and what I’m getting at. Take a look at the example below:
This is the isModelVectorPointProprity method in front of you. Its concept is that we transmit some BigDecimal priority here. It checks for us whether the given BigDecimal priority falls into the vector of model priorities. There are proper checks and there would have been nothing complicated if you were shown such a code and asked what is wrong with this code? You would look and say – “Well, it seems that everything is fine. Really, everything is fine, it works, works fine, everything is cool”.
And now let’s take a look at the result.
Above, I wrote a code that runs the method 100,000,000 times. 1579 mls. It seems like everything works, but in fact, there is a big problem in this code and very often this code is written by beginners. It is wrong. Why is everything so? – It’s simple 🙂
Because there are three objects made in the code. 1 – a model is created. Then we get it out and create startPriorityPoint and endPriorityPoint.
That is, we have startPriorityPoint and endPriorityPoint. And then we check whether this priority belongs to the priorities’ vector.
The problem in all these lines is that they always create 3 objects when calling methods. It seems like, what is difficult in creating 3 objects?!
But in fact, it gives 1569 milliseconds.
It seems to me that it would be better if we created these two startPriorityPoint and endPriorityPoint objects and then simply re-used them.
That is, in simple words, let’s look at the example below.
Here is a normal code that does the following.
We have static final variables: startPriorityPoint and endPriorirtyPoint. That is, the initial and final point of the priorities’ vector. We also have a static initialization block, which from the very beginning of the program startup initializes (assigns value) to the data. Everything is created one at a time. Now we are re-using it all. The code is the same, but everything is used over again. Let’s take a look at the result.
“Ta-da” and we have 9 milliseconds, which is 1,500 times faster!
So, the main message I want to send you is that you have to pay a lot of attention creating your methods’ objects if they are created repeatedly. And you can take them to any static final variables, then, always do that. This will speed up the work of your code in many times. And it’s absolutely right 🙂
Points for the next topic:
- Immutable classes if necessary;
- Composition better inheritance.