📜 ⬆️ ⬇️

Bad java or how not to do

While working, I, as probably every one of you, sometimes have to notice the minor flaws of Java. Small and rare, but inherent. By writing this article I was a feat of one of the comments to my first post. The topic seemed very interesting to me and I decided to recall everything that I don’t like in my favorite programming language. So, let's begin:

Hashset

I don’t know why such a decision was made, but HashSet is implemented on a HashMap, yes - they saved time to create, but this is one of the main collections, why it wasn’t more responsibly created - it’s not clear. Still, it was possible to create a HashSet more optimally. HashMap carries redundant architecture in the context of HashSet tasks. For example, inside HashSet there is the following code:
// Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); 

This means that any of your values ​​inside HashSet will be associated with a link to this object. This is the same as:
 Map.put(key, PRESENT); 

It would seem, think - 8 bytes, which will be used by all. But do not forget that every time you insert into HashSet, a Map.Entry is created, in which 4 links (another 16 extra bytes to each element). Wasteful, is not it? Why is that? A great mystery ... Thanks at least not inherited.

Default logger

Who in the project does not use log4j? Can you just call the library, which also do without it? I think these are hard questions. I understand, java cannot adapt to each specific task, but they added the standard Logger, so why in the 10 years of its existence log4j, java has not taken the best of it? Imagine how much all applications would be reduced, especially complex ones, where there may be several different versions of the logger in the final assembly.

Clonable

Everyone knows that the Clonable interface is empty, without a single method and variable, just a marker for the JVM. The question is why? After all, if we inherit from it, then we try to define a certain behavior and, following the OOP paradigms, it should contain the clone () method, which would define this very behavior. Of course, it is sometimes very convenient without overriding clone to get a copy of the object. Nevertheless, in fact, it is necessary to redefine this method in all classes that have at least one collection of model objects in order to get a complete copy, and not just references to the same objects inside the collection. I do not know why the developers did not define a single method in this interface. I hope they had a reason for this, although they probably just forgot. As a consequence, the clone () method in Object.
')
Serializable

The situation is similar to Clonable, but at least you can argue here - in most cases
we do not define the behavior manually, but rely on the standard implementation and it would be very inconvenient to constantly redefine some methods of serialization + constantly monitor the addition of new fields, add them to the methods. Well, specifically for this purpose there is Externalizable. However, we know that you can change the standard serialization behavior by predefining writeObject () and readObject (). Somehow not on OOshny.

Properties

We all work with the Properties class anyway. We open source codes and what we see?
 class Properties extends Hashtable<Object,Object> 

What for? Anyone knows that composition is better than inheritance, especially if this inheritance has no logical meaning and does not simplify the application model. What about the load (InputStream inStream) and load0 (LineReader lr) methods? But what about overloading methods? It’s the same with store0 (), well at least hidden inside a class. By the way, such methods are found everywhere in core java, similar to someone's style.

Primitive Shell Classes

Wrapper classes are redundant, it would be much more convenient to operate with primitives and be able to assign a null value to them. If a simple int is 4 bytes, then the Integer object is already 16. 16. A constant cast, a comparison by equals. Why all this? Farther:
 Boolean.getBoolean(); Long.getLong(); Integer.getInteger() 

Mysterious methods. I still cannot understand what role they should play and why the methods that
check System.getProperty () are in the wrapper classes, not in the Properties class.

switch

Of course, every java developer knows that the switch statement works only with the integer type, and starting with the 7th java and with strings. But why these restrictions? Judging by the implementation of switch in the 7th version - it works with a string based on hashode () and equals (). Then the question is why only lines? It was easy to implement the operator for any object, but for some reason this was not done. Another mystery.

Collections

In List and Set there is an excellent retainAll () method - intersection of sets, but for some reason there is no method for difference of sets. Of course, you can easily implement it yourself:
 List first = someList(); List second = someList(); diff = first.clone(); diff.removeAll(second); 

But I would like this basic operation with one of the main data types to be out of the box.

Next - one of the most frequent tasks when working with collections is to find the desired item in the collection. Why List has indexOf (Object o), and HashSet does not have get (Object o), which by hash code and equals would return a reference to an object from the collection. Yes, when the hash code is not redefined it makes no sense, but when it is not, then there are opportunities for use. Maybe I, of course, too carp, but quite often this problem arises.

From the same category - there is no possibility to filter objects by some property. You always have to do it manually. Bad, very bad.

Generics

 Map<MyKeyObject, PersistedPlan> plansById = new HashSet<MyKeyObject, PersistedPlan>(); 

As already noted, such constructions are found everywhere in the code — in class descriptions, in constructors, in methods, everywhere ... They overload the code and complicate its perception. Fortunately, in the 7th java this was fixed. It is not clear only - why waited so long?

Multithreading

For the first time, when I read about multithreading in Erlang, I realized how much the java multithreading mechanisms are complex. All of these locks, monitors, synchronizations, deadlocks, theads, runnables, memory barriers. What for? Why so complicate our lives, because java more than 20 years, is it really difficult to consider the trends of today? After all, the main goal of java was to make life easier for us - simple developers.
PS I talked to my colleagues here, there is information that the moves in this direction are not small, so let's hope.

Closures

Often there is a need to bring the function as a parameter, right? How long can you wait? As a java developer, I’m not very pleased that one of the most anticipated improvements wasn’t. Let's hope that at least in the 8th Java will appear, there is nothing more to add.

Runtime and System

The logical separation of these classes is not entirely clear, it seems to me that one would be enough. Several oddities are associated with System - for example, the out and in fields of the class are final. But at the same time there are setOut and setIn methods. Bad example, is not it?

String

It is not very efficient, not only does 2 bytes occupy each character, so every line is an object of class String, and this in turn is 8 bytes per header + 3 integer values ​​inside + 1 reference to the array == extra 24 bytes per each line! Still expensive. It would be possible to at least create 2 variants of the lines: cheap and expensive. There would be a choice at least.

Numbers

 Long l = 2; //compilation error Map<int, Object> a = new HashMap<int, Object>(); //compilation error Map<Object, int> a = new HashMap<Object, int>(); //compilation error Set<int> b = new HashSet<int>(); //compilation error 

Why did you complicate such simple things? I still can not realize. Trifle, of course, but not nice. Next, everyone knows that standard primitives are not suitable for working with floating point numbers in Java, since they lose accuracy. Well, we use another type - BigDecimal. And what a simple formula will turn into:
 //(metric.getValue() * 100 / prevMetric.getValue()) + metric.getOffset() BigDecimal result =BigDecimal.valueOf(metric.getValue() * 100).divide(BigDecimal.valueOf(prevMetric.getValue()).add(metric.getOffset())); 

Well, if the computations are simple, with complex things it’s much sadder. Why not enter the data type that works normally with numbers along with double and float?

findings

Do not think that this is some kind of autumn aggravation. In no case. I love java very much and I want it to develop on a par with modern trends. And I also want to be able to program it in pleasure, without all the little things that only annoy and distract from the entertaining and interesting work.

Source: https://habr.com/ru/post/132465/


All Articles