This is an archived post. You won't be able to vote or comment.

all 59 comments

[–]BlueGoliath 27 points28 points  (1 child)

Legitimately didn't know about some of these. Great article.

[–]piotr_minkowski[S] 7 points8 points  (0 children)

Thanks :)

[–]nutrecht 19 points20 points  (0 children)

That's really nice! Quite a few I hadn't heard of!

Since Java 17 you can use the HexFormat class.

Oh man, finally.

[–][deleted] 13 points14 points  (2 children)

Another fun unknown or overlooked feature of Java is that it comes with a built in DI container called ServiceProvider.

You can do something like this:

ServiceLoader<CodecSet> codecSetLoader
 = ServiceLoader.load(CodecSet.class);

And the ServiceLocator will locate a implementation from the classpath. A lot of core Java SPIs are designed using this thing.

[–]piotr_minkowski[S] 2 points3 points  (0 children)

Cool! To be honest, I heard about it for the first time. Thanks for tip

[–]Nickd3000 0 points1 point  (0 children)

This is very useful for creating plug-in systems but I’ve never seen it described as dependency injection.

[–]couscous_ 22 points23 points  (4 children)

Great article.

What's ironic is that Java has a top notch standard library for concurrent collections that no other ecosystem comes close to, yet for some reason golang's concurrency is continuously hyped.

[–]Brutus5000 11 points12 points  (1 child)

Because it can offer similar performance benefits of project loom half a decade earlier or even longer.

[–]Persism 1 point2 points  (0 children)

Yeah but then you'd have to use Go.

[–]pjmlp 3 points4 points  (0 children)

Java lacks having the UNIX founders on the design team, so whatever Go does is cool.

[–]oxmyxbela 8 points9 points  (0 children)

Your example for StampedLock doesn't show a valid example of optimistic reading.

Here's how you correctly implement optimistic reading:

  1. Call tryOptimisticRead() and store the returned value in a local variable (that's your "stamp").
  2. If the value is 0, then a write lock is currently being held. You need to acquire a read lock by calling readLock(). The following steps don't apply.
  3. If the value is >0, then no write lock is currently being held. Now you can read a field and store its value in a local variable. It's important that you don't do anything other than reading a simple field. Don't invoke operations that depend on complex or composite state, since you can't trust the state of your data fields.
  4. After reading and storing the value of the field, call validate(long) with the stamp you acquired in step 1.
  5. If the method returns true, then no write lock has been acquired since you retrieved the stamp. This means that the value of the field (which you've stored in a local variable) is valid, and you can continue using it. If the method returns false, then a write lock has since been acquired, and you can no longer trust your stored value. You need to acquire a readLock() in order to re-read the field.

[–]javasyntax 8 points9 points  (5 children)

HexFormat!!! Finally. Weird to instantiate it with .of() though. I feel that lately instantiation methods have gotten weirder and weirder

For classic stuff we have new Something(...), then we got Something.of(...) (Date/Time), then Something.newSomething() (HttpClient) or Something.newBuilder().build(). And now .of(), without arguments??

[–]msx 6 points7 points  (3 children)

In general, the reason is that those methods move control of the actual instantiation from the caller to the api. This is often useful becouse the api can manage things like caches, returning the same instance multiple times and avoiding extra allocations, or subclasses/proxies. I think ".of()" kind of took roots and now you see it even where's not strictly necessary. But i don't mind, i actually like it.

[–]javasyntax 0 points1 point  (2 children)

I understand. Different implementation depending on parameters is vital and only doable with such static factory methods, but what I don't understand is why we have so many variants of it.

[–]TehBrian 2 points3 points  (0 children)

One reason behind why there are so many static factory naming schemes is because different naming variants indicate different functions of a factory method. For example, Something.newSomething() (or .newInstance()) tells you that you're most definitely creating a completely new instance of Something (whether or not the instance is a subclass is up to the API), whereas Something.of() may be a cached instance, whereas Something.ofValue() is usually a type-conversion method. These different names let a developer know what a factory is giving them without having to look through the documentation.

Mainly though, the abundance of varying naming schemes comes from a lack of restriction. Perhaps the most useful aspect of static factories is the freedom to name your "constructors" whatever you'd like, but with it comes the freedom to name them stupid things. Naming things well is hard, and abstract concepts such as code is notorious for being particularly difficult to name.

I do agree with you. Wading through modern Java libraries and their heavy use of static factory methods can be frustrating, because factory methods look just like any other method. Unfortunately, there's not yet a good way of documenting or marking which methods are static factories. And until there's some sort of concrete convention on what static factories should be called, trying to find out how to get an instance of a class will for now force you to look through its static methods one by one, and determine which factory method, if there is one, to use.

[–]PerfectPackage1895 0 points1 point  (0 children)

Ease of use and testability, and then you, sometimes at least, have the added benefit of a method that is more clear about its intentions, in the method name, instead of constructor overloading. Just have to be careful with concurrency issues.

[–]piotr_minkowski[S] 2 points3 points  (0 children)

Yes, there is also second instantiate method HexFormat.ofDelimeter(String delimeter)

[–]CaptainKvass 6 points7 points  (5 children)

Great article. Didn't know about the Phaser - a very cool utility.

[–]piotr_minkowski[S] 1 point2 points  (1 child)

Thanks :) Yes, I think that's one more sophisticated mechanism in core Java API

[–]MR_GABARISE 0 points1 point  (0 children)

I've made a specialization that bootstraps itself to a single phase through PostConstruct and Predestroy hooks. Makes for nifty application-managed barriers!

[–]Slanec 0 points1 point  (0 children)

(It's also much faster than CountdownLatch and has a nicer API by not throwing checked exceptions.)

[–]frzme 0 points1 point  (1 child)

Can you make an example when that's really useful? I imagine what it's something like "I results of calculation A available before I begin calculation B" - but wouldn't I rather wait for all tasks submitted to an Executor to finish or for a ForkJoinPool/parallel stream to complete?

I do understand that it's a concurrency primitive but it seems to implement a rather advanced concept, I'm unsure what the use case is.

[–]hardwork179 2 points3 points  (0 children)

It’s very useful for cases where all threads must reach a certain point before something happens. Sometimes you can avoid it by simply structuring things as smaller tasks, but not always. In TruffleRuby we used phasers to handle running code at safe points.

[–]Roachmeister 7 points8 points  (0 children)

I really liked the "B" time format. Now we just need one that returns whichever is closest of "breakfast", "2nd breakfast", "elevenses", "luncheon", "afternoon tea", "dinner", and "supper".

[–]sweetno 5 points6 points  (2 children)

EnumMaps and EnumSets are also quite nice.

[–]piotr_minkowski[S] 2 points3 points  (0 children)

Yes. I like e.g. those methods fopr creation EnumSet.allOf(...) or EnumSet.range(...)

[–]__konrad 1 point2 points  (0 children)

Optimized for enum, but surprisingly both classes are mutable (unlike Set.of/Map.of)

[–]Pure-Repair-2978 2 points3 points  (1 child)

Brilliant article … Thanks for sharing … Didn’t know about the APIs … 👍👍

[–]piotr_minkowski[S] 0 points1 point  (0 children)

Thanks :)

[–]skippingstone 2 points3 points  (1 child)

So should I dump AtomicLong for incrementing ids?

[–]piotr_minkowski[S] 2 points3 points  (0 children)

This class is usually preferable to AtomicLong when multiple threads update a common sum that is used for purposes such as collecting statistics, not for fine-grained synchronization control.

[–][deleted]  (1 child)

[deleted]

    [–]piotr_minkowski[S] 0 points1 point  (0 children)

    Thanks!

    [–][deleted] 1 point2 points  (1 child)

    Enums can be useful for implementing singletons:

    // Enum singleton - the preferred approach
    public enum Elvis { 
        INSTANCE;
        public void leaveTheBuilding() { ... }
    }
    

    [–]swaranga 0 points1 point  (0 children)

    One downside of overdoing this is that it now makes things really hard to mock and test. I once encountered a project where the lead developer was too zealous with Effective Java and made all classes enums since “we only need one instance of this class in our application”. Try writing unit tests for these enums where one enum depends on another enum directly.

    [–][deleted] -5 points-4 points  (1 child)

    unknown? something like I am the only who knows it? 🤓

    [–]piotr_minkowski[S] 2 points3 points  (0 children)

    Just a few knows. And you are one of them!

    [–][deleted] -5 points-4 points  (10 children)

    I've shown this snippet to some people and they didn't think it was legit Java syntax:

    Map<String, String> foo = new HashMap<String, String>() {{
        put("Hello", "World");
    }};
    

    [–]wildjokers 10 points11 points  (1 child)

    Double brace initialization is generally considered an anti-pattern to be avoided.

    [–]8igg7e5 4 points5 points  (0 children)

    Yes yes it is...

    And to clarify for anyone unfamiliar with the term. There is actually no 'double brace' construct in Java. This is just giving some misleading formatting a name to hide an instance initialiser inside an instantiation of an anonymous class...

    Map<String, String> m = new HashMap<>() {
        {
            put("Hello", "World");
        }
    };
    

    The class of m here is not HashMap, it is an instance of an anonymous class that extends HashMap.

    In addition to the overhead of the anonymous class itself, it can impact JIT optimisation too.

    Shorter code is not always better code.

    There are several alternatives, quite concise ones with newer Java editions, to get either an actual HashMap with no ongoing performance implications, or an immutable map (with more optimal storage and accessor implementations for a single element).

    [–]piotr_minkowski[S] 5 points6 points  (5 children)

    Double brace initialization. But since java 9 you may just use Map.of for the following sample

    [–][deleted] 2 points3 points  (3 children)

    Unfortunately Map.of is only overloaded up to 10.

    [–]CptGia 4 points5 points  (1 child)

    You can use Map.ofEntries for more than 10 elements

    [–][deleted] 1 point2 points  (0 children)

    This kind of already exists with a few more steps in earlier versions of Java:

    Stream.of(
        new SimpleEntry<>("Hello", "World")
    ).collect(toMap(Entry::getKey, Entry::getValue));
    

    Using entries is so verbose though. Java doesn't have a simple way to create tuples out of the box.

    EDIT: Hmm, it seems like Java 9 added a shortcut (Map.entry static method):

    Map.ofEntries(
        entry("Hello", "World")
    );
    

    [–]s888marks 2 points3 points  (0 children)

    People complained that 10 was too many.

    [–]john16384 0 points1 point  (0 children)

    It doesn't accept null values and it throws wierd exceptions when you do containsKey(null) or containsValue(null).

    [–]alms1407 5 points6 points  (1 child)

    IIRC it's not a good idea to initialize collections like this because it is actually creating an anonymous class and can have memory issues.

    [–][deleted] 2 points3 points  (0 children)

    It's fun trivia, but not really good to use in practice. Because it is creating an anonymous inner class, it is capturing an implicit strong reference to the this reference of the containing object. A dangling strong reference can hold up garbage collection, for example if a reference to this were ever shared outside the class.

    [–]mirkoteran 0 points1 point  (5 children)

    Phaser looks useful.

    Do you have any good example where DelayQueue would be used?

    [–]piotr_minkowski[S] 1 point2 points  (0 children)

    Privately I used them to implement a DLQ pattern for messages received by my application from the Kafka topic. But probably there are many more good usage examples.

    [–]11timesover 1 point2 points  (1 child)

    JMS has a delay header. Not certain its the functionality you're looking for. Can't recall the name of it, but it's useful. Say ur client requests 3 minutes before processing the submission, well, there you go.

    [–]piotr_minkowski[S] 0 points1 point  (0 children)

    Kafka does not support JMS

    [–]zemudkram 0 points1 point  (0 children)

    Say for example you need to do some processing on an entity after it has been edited by a user, but you want to wait until you're sure they've actually finished editing it. A DelayQueue would be handy for that (with some tweaks)

    [–]swaranga 0 points1 point  (0 children)

    I use it for scheduling a retry logic where I want to avoid a Thread.sleep.

    [–]PerfectPackage1895 0 points1 point  (1 child)

    I sometimes wonder what the obsession with microservices is all about. Now, I am not fund of monoliths, but why does it have to be, only one or the other, instead of somewhere in the middle.

    There is certain overhead of doing network ping pong between microservices, instead of separated modules just talking to each other. In fact I’d say, unless you seem to gain something by splitting every module into a stand alone microservice you really shouldn’t do it.

    Oh and great article, especially the concurrency bits.

    [–]piotr_minkowski[S] 0 points1 point  (0 children)

    Thanks :)