Bad Java or how not to do it

    During work, I, like probably each of you, sometimes have to notice minor Java flaws. Small and rare, but inherent. One of the comments on my first post was a feat for writing this article . The topic seemed very interesting to me and I decided to recall everything that I do not like in my favorite programming language. So, let's begin:

    Hashset

    I don’t know why such a decision was made, but HashSet was implemented on HashMap, yes - they saved time on creation, but this is one of the main collections, why it wasn’t approached to create it more responsibly - it’s not clear. Still, it was possible to create a HashSet more optimally. HashMap brings redundant architecture in the context of HashSet tasks. For example, inside the 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 the 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 everyone. But do not forget that each time you insert into a HashSet, Map.Entry is created, in which there are 4 links (another 16 extra bytes per element). Wasteful, don’t you? Why is that? A big mystery ... Thank you at least not inherited.

    Default logger

    Who does not use log4j in the project? Can you name libraries that also do without it? I think these are difficult questions. I understand that java cannot be adapted to each specific task, but they added the standard Logger, so why over the 10 years of existence of log4j, java has not taken the best of it? Imagine how much all applications, especially complex ones, would decrease, where in the final assembly there may appear several different versions of the logger.

    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 are trying to determine some behavior, and following the OOP paradigms, it should contain the clone () method, which would determine this very behavior. Of course, sometimes it is very convenient to get a copy of an object without redefining clone. Nevertheless, overriding this method, in fact, is necessary in all classes that have at least one collection of model objects in order to get a full copy, and not just links 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 result, the clone () method in Object.

    Serializable

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

    Properties

    One way or another, we all work with the Properties class. We open source codes and what we see?
    class Properties extends Hashtable

    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 method overloading? The same is with store0 (), well even though they are hidden inside the class. By the way, such methods are ubiquitous in core java, similar to someone's style.

    Primitive Type Shell Classes

    Shell classes are redundant, it would be much more convenient to operate with primitives and be able to assign them null values. If a simple int takes 4 bytes, then the Integer object is already integer 16. Constant casting, comparison by equals. Why all this? Further:
    Boolean.getBoolean();
    Long.getLong();
    Integer.getInteger()
    

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

    switch

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

    Collections

    List and Set have 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 such a basic operation with one of the main data types to be out of the box.

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

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

    Generics

    Map plansById = new HashSet();
    

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

    Multithreading

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

    Short circuits

    Often there is a need to betray a function as a parameter, right? How long can I wait? As a java developer, I am not very pleased that one of the most anticipated improvements was never made. Let's hope that they will appear at least in the 8th, there is nothing more to add.

    Runtime and system

    It is not entirely clear the logical separation of these classes, it seems to me, one would be quite enough. There are several other oddities associated with System - for example, out and in fields of a class - final. But at the same time there are setOut and setIn methods. Bad example, isn't it?

    String

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

    Numbers

    Long l = 2; //compilation error
    Map a = new HashMap(); //compilation error
    Map a = new HashMap(); //compilation error
    Set b = new HashSet(); //compilation error
    

    Why complicate such simple things? I still can’t realize. A trifle, of course, but not nice. Further, 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 this is 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 calculations are simple, with complex, everything is much sadder. Why not introduce a data type that works well with numbers along with double and float?

    conclusions

    Do not think that this is some kind of autumn aggravation. In no case. I really like java and I want it to develop along with the trends of our time. And I want it to be possible to program on it without all these little things that only annoy and distract from an entertaining and interesting work.

    Also popular now: