3

I am playing with the preview sealed classes in java 15, and I was wondering why the keywords sealed and non-sealed only apply to classes and interfaces, but not methods (like other modifiers do). I imagine it might be useful to decide specifically, which of the methods can be overridden by permitted subclasses.

An Example: I have a class Unit that has two subclasses Metric and Imperial, which both finally implement a basic functionality, here kind().

public abstract sealed class Unit permits Imperial, Metric {
    public abstract String kind ();
}

public abstract non-sealed class Imperial extends Unit {
    @Override
    public final String kind() { return "imperial"; }
}

public abstract non-sealed class Metric extends Unit {
    @Override
    public final String kind() { return "metric"; }
}

This works. However, now I don't want to implement kind() in all subclasses, but provide an implementation that is final to all subclasses except the ones where overriding is permitted. In my mind this would look like this:

public abstract sealed class Unit permits Imperial, Metric {
    // this is not supported
    public sealed String kind () permits Imperial {return "easy"; }
}

public abstract non-sealed class Imperial extends Unit {
    @Override
    public final String kind() { return "oh my god"; }
}

public abstract non-sealed class Metric extends Unit {
    // should not be possible to override kind() here or in any subclass
}

Is there any way that I could achieve this with the new features or is there any other way I am missing?

Naman
  • 27,789
  • 26
  • 218
  • 353
jf_
  • 3,099
  • 2
  • 13
  • 31
  • 4
    Yes, by discipline. Sealing the class means you can control all the implementations, so you don't need additional prevention against other implementors, because it's all you. – Brian Goetz Dec 02 '20 at 15:02
  • Of course, that's correct. But if the method is abstract in Unit the implementations may get somewhat redundant. If not, I'd have to override it in every permitted class to ensure it's final for further subclasses. And even with discipline this may fall under the radar in extensions a while after. – jf_ Dec 02 '20 at 15:42

1 Answers1

1

You can do this with package-access classes.

Place all three classes in their own package, and delegate kind() to a package-private method—that is, a method which is neither public nor protected nor private. This will allow only classes in that package to override it:

package com.example.units;

public abstract sealed class Unit permits Imperial, Metric {
    public final String kind () {
        return kindImpl();
    }

    String kindImpl() { return "easy"; }
}

package com.example.units;

public abstract class Imperial extends Unit {
    @Override
    String kindImpl() { return "imperial"; }
}

kindImpl() is package-private, so only classes in the same package are able to override it.

This works in any version of Java, regardless of whether sealed classes are in use.

VGR
  • 40,506
  • 4
  • 48
  • 63
  • I think this solution solves the example above, but may it may get difficult when there are more methods with different requirements involved – jf_ Dec 04 '20 at 22:33
  • 1
    @achim If you have methods that you want to limit to specific classes in different packages overriding, I would argue that your design probably needs to be rethought. – VGR Dec 04 '20 at 23:03
  • The question does not state anything about different packages, but yes, if I really had a problem that needed this I could come up with different designs. I was just wondering why the sealed functionality (which I was just trying to explore) was not defined for methods. From the comments I understand "because we would never need it". Not entirely sure if that is true... – jf_ Dec 05 '20 at 06:47
  • 4
    @achim I think where you've gotten stuck is that "benefit might be nonzero" is not the proper bar for judging a language feature. Every feature adds complexity and has opportunity cost; doing feature X means we don't do Y. So "benefit is nonzero" is not the bar (nor is, even, "benefit in line with the costs" unless you have unbounded capacity.) Here, the benefit is so miniscule as to be negligible, so it doesn't even get out of the gate. (You really don't want to program in a language that has every feature that someone, somewhere, thought was useful.) – Brian Goetz Dec 12 '20 at 17:20