8

I recently upgraded my project from Java 8 to Java 10 and had a LOT of warnings that Observer and Observable were deprecated now. The suggestion with which to replace them was the Flow API - The problem is I have yet to find a decent tutorial on how to code for the Flow API. Everything I've found says to not implement the Publisher/Subscriber directly and also much of what I've found suggests that the Flow API is heavily based on multithreading.

Note: I've sought this answer out already on StackOverflow as well as Quora and a few other sites and several people cite that Observable objects can't be serialized which is flatly untrue as I implement Serializable for every one of my beans and many of my other classes as well and have even serialized to file a few of my objects and read them back from the file back into object form.

However, the multithreading thing is an issue - I don't WANT to use streams or multithreading when responding to changes to a model object. Question 1 is - Is that an absolute requirement to using the Flow API or is there another set of classes/interfaces that don't require multithreading and streams?

When looking at the javadocs for Flow.publisher thinking that would be what I would replace Observable with - Because it's an interface, that means I guess that I'd have to put (similar to my ListModel and TableModel class implementations) a collection of subscribers(similar to Observers??) and put custom code to add and remove them from my publisher? Question 2 - Do I have that right?

Question 3.) What is the generic type argument in Flow.Publisher and Flow.Subscriber? It shows a type but I have no idea where that type is used - I'll put code here for one of my model classes:

package net.draconia.frenchstudy.model;

import java.io.Serializable;

import java.util.concurrent.Flow.Publisher;
import java.util.concurrent.Flow.Subscriber;

public class Category implements Publisher<Category>, Serializable
{
    private static final long serialVersionUID = 5281400324476454101L;

    public static final Category EMPTY_CATEGORY = new Category(); 

    private Integer miId;
    private String msCategory;

    public Category()
    { }

    public Category(final int iId)
    {
        setId(iId);
    }

    public Category(final String sCategory)
    {
        setCategory(sCategory);
    }

    public Category(final int iId, final String sCategory)
    {
        setId(iId);
        setCategory(sCategory);
    }

    public String getCategory()
    {
        if(msCategory == null)
            msCategory = "";

        return(msCategory);
    }

    public int getId()
    {
        if((miId == null) || (miId < 0))
            miId = 0;

        return(miId);
    }

    public void setCategory(final String sCategory)
    {
        if(sCategory == null)
            msCategory = "";
        else
            msCategory = sCategory;

        setChanged();
        notifyObservers();
    }

    public void setId(final Integer iId)
    {
        if((iId == null) || (iId < 0))
            miId = 0;
        else
            miId = iId;

        setChanged();
        notifyObservers();
    }

    public void subscribe(final Subscriber<? super Category> subscriber)
    {

    }

    public String toString()
    {
        return(getCategory());
    }
}

It's partially bastardized as I was trying to replace Observable with Flow.Publisher but have no idea what to put in the subscribe method code. I took a wild guess at the generic type as the same type as the class on which being worked but I am not sure if that's correct.

I've always been fine with putting logic in the various Observers to handle just 1 possible change in the Observable object and leave other changes to other Observers - The order of the execution of said observers to me never mattered because I coded with that in mind and it wouldn't rely on one executing before or after another. I've never noticed any reduction of execution speed due to Observers executing more than once in many cases so this seems foolish of a decision on Oracle's part but I still want to know - How do I use the Flow API to replace the Observable/Observer code in my program? Or if it can't function like that, then what can I use because I've seen a possible solution to use PropertyChangeListeners but my model objects are NOT properties and I don't want to define properties (Never used PropertyChangeListener so I'm not sure how they work either) given that in the dozens of model objects that would surmount to hundreds if not close to a thousand properties to define if I had to define them. Any links to tutorials, code, or examples to follow will be helpful.

Naman
  • 27,789
  • 26
  • 218
  • 353
Seth D. Fulmer
  • 490
  • 1
  • 4
  • 21
  • 4
    Completely unrelated to your question, but I see you're calling non-final methods (the setters) from constructors. This can lead to weird errors when using inheritance. See [Don't call non-final methods from your constructor, please](https://lustforge.com/2014/02/08/dont-call-non-final-methods-from-your-constructor-please/) – Modus Tollens Oct 13 '18 at 14:53
  • Regarding your problem: is seems like you are looking for a simple solution. Have you considered writing Observers yourself, implementing the [Observer Pattern](https://www.tutorialspoint.com/design_pattern/observer_pattern.htm)? – Modus Tollens Oct 13 '18 at 15:52
  • 1
    I did consider it but then the solution would be quite localized to my project and not reusable too well - I ended up switching to property change listeners but trying to find a way now to move the prop change code to an external class to extend so I'm not repeating code so much. The code repetition annoys me but that would probably be in Flow also as theyre interfaces. – Seth D. Fulmer Oct 13 '18 at 18:54
  • I disagree about the non-final methods from constructor consideration. I don't see how that could ever possibly cause any problem. final only stipulates that one can not overload the method in child/sub classes but all methods in Java are virtual so it will automatically execute the version of the method that is most appropriate for what is being instantiated. If I was creating a class (anything but as in Java, all classes are derived from Object by default) and in the constructor I called toString() I can guarantee nothing will happen ever in a billion lifetimes. – Seth D. Fulmer Oct 14 '18 at 18:47
  • Well, I got burned heavily by an error like that. Very hard to pinpoint because the code _looks_ ok, but does not do what is expected. Finding that bug under pressure was so painful that I developed an eye for method calls in constructors. Never again :). It's definitely not a one in a billion lifetimes chance. – Modus Tollens Oct 14 '18 at 20:18
  • 2
    [Quote from Oracle](https://docs.oracle.com/javase/tutorial/java/IandI/final.html): "Methods called from constructors should generally be declared final. If a constructor calls a non-final method, a subclass may redefine that method with surprising or undesirable results." – Modus Tollens Oct 14 '18 at 20:24
  • Ok Oracle can say whatever it wants - I've to this date in almost 20 years have never run into any unexpected behavior desirable or undesirable. I consider it a worse sin to duplicate code - which one would be doing if in the constructor, one simply did = instead of set(). if one does it in two places, it raises a risk of bugs because of one of the sections of code changing while the other stays the same and blows up the entire application then. – Seth D. Fulmer Oct 22 '18 at 15:01
  • To make set() final is a travesty too because what if I want to in children classes override set() to add additional functionality or totally dramatically different functionalityeven - I can't because it's final. I just shot myself in the foot in that case - I have always considered the concept of final methods to be one of the worst ever to "grace" Java anyhow as it prevents improvements. – Seth D. Fulmer Oct 22 '18 at 15:02
  • I don't want to blow this issue out of proportion, but I consider the topic important enough to point out (not only for you, but as well to others who happen to come across this question) that a design like this can lead to massive, hard to find problems. I see many Java beginner questions where a code acts strangely because they have overridden a method that was called in the constructor of a super class. The problem is discussed in _many_ SO topics. (Two examples: https://stackoverflow.com/q/18348797/1288408 and https://stackoverflow.com/q/3404301/1288408). It does happen. – Modus Tollens Oct 22 '18 at 15:24
  • Note that there is in principle nothing wrong with calling setters and other methods in a constructor when you take care to make that methods (or the class) final or private to avoid problems, and probably add a comment. I always point out method calls in constructors in code reviews; they are a massive red flag. – Modus Tollens Oct 22 '18 at 15:27
  • Well as I've said before in almost 20 years of programming Java, I have worked on many enterprise level applications where not just I but others too have used "non-final" methods in constructors and nothing has happened out of the ordinary. I've never until this thread heard of any weird issues. One is supposed to be able to override methods later. It doesn't help anything to make setters private - If you do then you need to have a separate set of setters that are public and ...that is code duplication - a mortal sin. – Seth D. Fulmer Oct 22 '18 at 15:50
  • 1
    I never said you should set setters private. But if they aren't they shouldn't be used in a constructor. Look, I've been programming Java since Version 1.0, and I have come across this problem - in my code and in the code of others. Sometimes problems (eg in calculations) won't even be obvious. It's lucky that you didn't, but that doesn't mean it is safe. I hoped that I could teach from experience here. But it is your code - do what you want with it. If it is code for medical, engineering or financial purpose - please reconsider ;). Good luck and bye! – Modus Tollens Oct 22 '18 at 15:55
  • So if one shouldn't use setters in constructors - what pray tell should one do to set variables? I hope you're not going to say directly set variables - As I've said - That's a mortal sin! – Seth D. Fulmer Oct 22 '18 at 16:36
  • 2
    I'd directly set variables, yes, since constructors should ideally have no logic and just initialize variables. I try to work with immutable classes if possible. For complex class initialization, I use builders. They are very convenient and provide a safe environment to initialize objects. – Modus Tollens Oct 22 '18 at 17:00
  • Similar question asked 8 months later after this one: "How to use use java 9 flow to implement observer/observable pattern for simple mvc?", https://stackoverflow.com/q/56948688/110126. In Aug 2023, has 1 "okay" answer. – buzz3791 Aug 14 '23 at 16:00

0 Answers0