I am studying the fragile base class problem and found the following paper interesting: https://www.research.ibm.com/haifa/info/ple/papers/class.pdf
In this paper it is argued that it would be great if Java had a 'sealed' access modifier; not like 'sealed' in C#, which is equivalent to Java's 'final' keyword. The sealing mechanism proposed would make it impossible to extend these sealed classes outside of their packages.
However, most of the material that I have found about the FBC problem dates back to the late 90s, early 00s, so it makes me wonder if the 'problem' is no longer a major issue.
I know that Joshua Bloch is an advocate of restrictive use of inheritance, especially across libraries, and he certainly seems to be a Java authority.
I know how to make oligomorphy happen by creating a set of final inner classes that inherit from a class with a private constructor, but this seems a bit inappropriate somehow.
Is the sealing proposed basically similar to making classes default/package-private, or is there actually some kind of class sealing mechanism in Java today?
However, most of the material that I have found about the FBC problem dates back to the late 90s, early 00s, so it makes me wonder if the 'problem' is no longer a major issue.
I think it's more that the issue is now well-understood. Similarly, you won't find too many recent papers discussing problems with GOTO and how to address them, not because these problems no longer exist, but because people now know how to avoid them.
Is [the proposed class sealing mechanism] not basically the same thing as making classes default/package-private?
No. Package-private classes and "sealed" classes are similar in that both cannot be extended by classes outside the package, but they differ in that the former also cannot be used by classes outside the package. That is — if a class X is package-private, then a class outside its package can't even refer to X: no extends X, no X x = new X(), no Class<X> clazz = X.class. But if it's merely sealed, then a class in a different package cannot write extends X, but can still write X x = new X() and Class<X> clazz = X.class and so on. (Just as important, it can still write X x = new Y(), if Y is a subclass. So it can still take advantage of X's type hierarchy, even though it itself can't extend X.)
I know how to make oligomorphy happen by creating a set of final inner classes that inherit from a class with a private constructor, but this seems a bit inappropriate somehow.
I wouldn't say this technique is inappropriate - the real problem is that mainstream OOP languages lack a mechanism to define a type by cases. Doing this conflates cases with types (unless you hide the subclasses by making them private) but it's the only option you have in Java.
ruakh's answer addresses your question about the sealing mechanism so I'll skip that. As for avoiding the Fragile Base Class Problem, this paper presents a solution that currently works in Java. The idea is to make all public methods final and implement them in terms of protected methods. This ensures that subclasses can only override those methods you deem safe.
The problem with inheritance as implemented in mainstream OOP languages is that it's something you have to opt out of when it should be something you have to opt into. That said, other than defining a type by cases I'm not sure what other use inheritance has that's not better replaced with aggregation/composition.
There is actually no such thing as the Fragile Base Class Problem despite the fact that there's a Wikipedia page for it and even a StackOverflow question about it. The reason you cannot find any recent references to it is because it was renamed in the mid 80s to The Incompetent Programmer Problem.
The reason it was renamed was because someone finally realised the problem it describes, where changing some seemingly insignificant method in a base class has far-reaching consequences in all the inherited sub classes, is actually not a problem with oop, it is a problem of putting the wrong code in your base class.
If you want to code oop properly and you wish to make use of inheritance it surely must be completely and utterly obvious that you make sure your base classes only ever contains totally stable and totally reliable code. Because once you start deriving from it you're stuck. If you find you are tempted to change your base class once you have derived from it a few times you have essentially already shot yourself in the foot.
Faffing around with strange private hierarchies is not the answer.
Related
Java 17 has introduced sealed classes which can permit only specific classes to extend them and would otherwise be final
I understand the technical use-case, but can't think of any real life use cases where this would be useful?
When would we want only specific classes to be able to extend a particular class?
In our own projects, if we want a new class to extend the sealed class can't we just add it to the permitted classes? Wouldn't it be better to just not make the class final or sealed in that case to avoid the slight overhead?
On the other hand, while exposing a library for external use how would a sealed class know beforehand which classes it should permit for extension?
sealed classes provide the opposite guarantee to open classes (the default in Java). An open class says to implementors "Hey, you can subclass me and override my non-final methods", but it says to users "I have no idea what subclasses look like, so you can only use my own methods directly". On the flipside, sealed classes are very restrictive to implementors "You cannot subclass me, and you can only use me in these prescribed ways", but very powerful to users: "I know in advance all of my subclasses, so you know that if you have an instance of me, then it must be one of X, Y, or Z". Consequently, adding a subclass to a sealed class is a breaking change.
It may be helpful to think of sealed classes less as "restricted classes" and more as "enums with superpowers". An enum in Java is a finite set of data values, all constructed in advance. A sealed class is a finite set of classes that you set forth, but those classes may have an infinite number of possible instances.
Here's a real-world example that I wrote myself recently. This was in Kotlin, which also has sealed classes, but it's the same idea. I was writing a wrapper for some Minecraft code and I needed a class that could uniformly represent all of the ways you can die in Minecraft. Long story short, I ended up partitioning the death reasons into "killed by another living thing" and "all other death reasons". So I wrote a sealed interface CauseOfDeath. Its two implementors were VanillaDeath (which took a "cause of damage" as its constructor argument, basically an enum listing all of the possible causes) and VanillaMobDeath (which took the specific entity that killed you as its constructor argument).
Since this was clearly exhaustive, I made it sealed. If Minecraft later adds more death reasons, they will fit into one of the two categories ("death by entity" or "death by other causes"), so it makes no sense for me or anyone else to ever subclass this interface again.
Further, since I'm providing very strong guarantees about the type of death reason, it's reasonable for users to discriminate based on type. Downcasting in Java has always been a bit of a code smell, on the basis that it can't possibly know every possible subclass of a class. The logic is "okay, you've handled cases X and Y, but what if someone comes along and writes class Z that you've never heard of". But that can't happen here. The class is sealed, so it's perfectly reasonable for someone to write a sort of pseudo-visitor that does one thing for "death by entity" and another for "death by other", since Java (or Kotlin, in my case) can be fully confident that there are not, and never will be, any other possibilities.
This makes more sense as well if you've used algebraic data types in Haskell or OCaml. The sealed keyword originated in Scala as a way to encode ADTs, and they're exactly what I just described: a type defined as the (tagged) union of a finite number of possible collections of data. And in Haskell and OCaml, it's entirely normal to discriminate on ADTs as well using match (or case) expressions.
One of the most useful features of Java 8 are the new default methods on interfaces. There are essentially two reasons (there may be others) why they have been introduced:
Providing actual default implementations. Example: Iterator.remove()
Allowing for JDK API evolution. Example: Iterable.forEach()
From an API designer's perspective, I would have liked to be able to use other modifiers on interface methods, e.g. final. This would be useful when adding convenience methods, preventing "accidental" overrides in implementing classes:
interface Sender {
// Convenience method to send an empty message
default final void send() {
send(null);
}
// Implementations should only implement this method
void send(String message);
}
The above is already common practice if Sender were a class:
abstract class Sender {
// Convenience method to send an empty message
final void send() {
send(null);
}
// Implementations should only implement this method
abstract void send(String message);
}
Now, default and final are obviously contradicting keywords, but the default keyword itself would not have been strictly required, so I'm assuming that this contradiction is deliberate, to reflect the subtle differences between "class methods with body" (just methods) and "interface methods with body" (default methods), i.e. differences which I have not yet understood.
At some point of time, support for modifiers like static and final on interface methods was not yet fully explored, citing Brian Goetz:
The other part is how far we're going to go to support class-building
tools in interfaces, such as final methods, private methods, protected
methods, static methods, etc. The answer is: we don't know yet
Since that time in late 2011, obviously, support for static methods in interfaces was added. Clearly, this added a lot of value to the JDK libraries themselves, such as with Comparator.comparing().
Question:
What is the reason final (and also static final) never made it to Java 8 interfaces?
This question is, to some degree, related to What is the reason why “synchronized” is not allowed in Java 8 interface methods?
The key thing to understand about default methods is that the primary design goal is interface evolution, not "turn interfaces into (mediocre) traits". While there's some overlap between the two, and we tried to be accommodating to the latter where it didn't get in the way of the former, these questions are best understood when viewed in this light. (Note too that class methods are going to be different from interface methods, no matter what the intent, by virtue of the fact that interface methods can be multiply inherited.)
The basic idea of a default method is: it is an interface method with a default implementation, and a derived class can provide a more specific implementation. And because the design center was interface evolution, it was a critical design goal that default methods be able to be added to interfaces after the fact in a source-compatible and binary-compatible manner.
The too-simple answer to "why not final default methods" is that then the body would then not simply be the default implementation, it would be the only implementation. While that's a little too simple an answer, it gives us a clue that the question is already heading in a questionable direction.
Another reason why final interface methods are questionable is that they create impossible problems for implementors. For example, suppose you have:
interface A {
default void foo() { ... }
}
interface B {
}
class C implements A, B {
}
Here, everything is good; C inherits foo() from A. Now supposing B is changed to have a foo method, with a default:
interface B {
default void foo() { ... }
}
Now, when we go to recompile C, the compiler will tell us that it doesn't know what behavior to inherit for foo(), so C has to override it (and could choose to delegate to A.super.foo() if it wanted to retain the same behavior.) But what if B had made its default final, and A is not under the control of the author of C? Now C is irretrievably broken; it can't compile without overriding foo(), but it can't override foo() if it was final in B.
This is just one example, but the point is that finality for methods is really a tool that makes more sense in the world of single-inheritance classes (generally which couple state to behavior), than to interfaces which merely contribute behavior and can be multiply inherited. It's too hard to reason about "what other interfaces might be mixed into the eventual implementor", and allowing an interface method to be final would likely cause these problems (and they would blow up not on the person who wrote the interface, but on the poor user who tries to implement it.)
Another reason to disallow them is that they wouldn't mean what you think they mean. A default implementation is only considered if the class (or its superclasses) don't provide a declaration (concrete or abstract) of the method. If a default method were final, but a superclass already implemented the method, the default would be ignored, which is probably not what the default author was expecting when declaring it final. (This inheritance behavior is a reflection of the design center for default methods -- interface evolution. It should be possible to add a default method (or a default implementation to an existing interface method) to existing interfaces that already have implementations, without changing the behavior of existing classes that implement the interface, guaranteeing that classes that already worked before default methods were added will work the same way in the presence of default methods.)
In the lambda mailing list there are plenty of discussions about it. One of those that seems to contain a lot of discussion about all that stuff is the following: On Varied interface method visibility (was Final defenders).
In this discussion, Talden, the author of the original question asks something very similar to your question:
The decision to make all interface members public was indeed an
unfortunate decision. That any use of interface in internal design
exposes implementation private details is a big one.
It's a tough one to fix without adding some obscure or compatibility
breaking nuances to the language. A compatibility break of that
magnitude and potential subtlety would seen unconscionable so a
solution has to exist that doesn't break existing code.
Could reintroducing the 'package' keyword as an access-specifier be
viable. It's absence of a specifier in an interface would imply
public-access and the absence of a specifier in a class implies
package-access. Which specifiers make sense in an interface is unclear
- especially if, to minimise the knowledge burden on developers, we have to ensure that access-specifiers mean the same thing in both
class and interface if they're present.
In the absence of default methods I'd have speculated that the
specifier of a member in an interface has to be at least as visible as
the interface itself (so the interface can actually be implemented in
all visible contexts) - with default methods that's not so certain.
Has there been any clear communication as to whether this is even a
possible in-scope discussion? If not, should it be held elsewhere.
Eventually Brian Goetz's answer was:
Yes, this is already being explored.
However, let me set some realistic expectations -- language / VM
features have a long lead time, even trivial-seeming ones like this.
The time for proposing new language feature ideas for Java SE 8 has
pretty much passed.
So, most likely it was never implemented because it was never part of the scope. It was never proposed in time to be considered.
In another heated discussion about final defender methods on the subject, Brian said again:
And you have gotten exactly what you wished for. That's exactly what
this feature adds -- multiple inheritance of behavior. Of course we
understand that people will use them as traits. And we've worked hard
to ensure that the the model of inheritance they offer is simple and
clean enough that people can get good results doing so in a broad
variety of situations. We have, at the same time, chosen not to push
them beyond the boundary of what works simply and cleanly, and that
leads to "aw, you didn't go far enough" reactions in some case. But
really, most of this thread seems to be grumbling that the glass is
merely 98% full. I'll take that 98% and get on with it!
So this reinforces my theory that it simply was not part of the scope or part of their design. What they did was to provide enough functionality to deal with the issues of API evolution.
It will be hard to find and identify "THE" answer, for the resons mentioned in the comments from #EJP : There are roughly 2 (+/- 2) people in the world who can give the definite answer at all. And in doubt, the answer might just be something like "Supporting final default methods did not seem to be worth the effort of restructuring the internal call resolution mechanisms". This is speculation, of course, but it is at least backed by subtle evidences, like this Statement (by one of the two persons) in the OpenJDK mailing list:
"I suppose if "final default" methods were allowed, they might need rewriting from internal invokespecial to user-visible invokeinterface."
and trivial facts like that a method is simply not considered to be a (really) final method when it is a default method, as currently implemented in the Method::is_final_method method in the OpenJDK.
Further really "authorative" information is indeed hard to find, even with excessive websearches and by reading commit logs. I thought that it might be related to potential ambiguities during the resolution of interface method calls with the invokeinterface instruction and and class method calls, corresponding to the invokevirtual instruction: For the invokevirtual instruction, there may be a simple vtable lookup, because the method must either be inherited from a superclass, or implemented by the class directly. In contrast to that, an invokeinterface call must examine the respective call site to find out which interface this call actually refers to (this is explained in more detail in the InterfaceCalls page of the HotSpot Wiki). However, final methods do either not get inserted into the vtable at all, or replace existing entries in the vtable (see klassVtable.cpp. Line 333), and similarly, default methods are replacing existing entries in the vtable (see klassVtable.cpp, Line 202). So the actual reason (and thus, the answer) must be hidden deeper inside the (rather complex) method call resolution mechanisms, but maybe these references will nevertheless be considered as being helpful, be it only for others that manage to derive the actual answer from that.
I wouldn't think it is neccessary to specify final on a convienience interface method, I can agree though that it may be helpful, but seemingly the costs have outweight the benefits.
What you are supposed to do, either way, is to write proper javadoc for the default method, showing exactly what the method is and is not allowed to do. In that way the classes implementing the interface "are not allowed" to change the implementation, though there are no guarantees.
Anyone could write a Collection that adheres to the interface and then does things in the methods that are absolutely counter intuitive, there is no way to shield yourself from that, other than writing extensive unit tests.
We add default keyword to our method inside an interface when we know that the class extending the interface may or may not override our implementation. But what if we want to add a method that we don't want any implementing class to override? Well, two options were available to us:
Add a default final method.
Add a static method.
Now, Java says that if we have a class implementing two or more interfaces such that they have a default method with exactly same method name and signature i.e. they are duplicate, then we need to provide an implementation of that method in our class. Now in case of default final methods, we can't provide an implementation and we are stuck. And that's why final keyword isn't used in interfaces.
Say I am using a Java library that has the following method
public static SomeInterface foo();
The interface SomeInterface has multiple implementations, some of which are protected within the library's package. One of these implementation is TheProtectedClass
What would be the best way to check if the object returned by foo() is an instance of TheProtectedClass?
My current plan is to create an Utils class that lives within my project but in the same package as the protected class. This Utils can refer to TheProtectedClass since it is in the same package and thus it can check if an object is instanceof TheProtectedClass.
Any other ideas?
EDIT: Some people are asking "why" so here is more context.
I am using jOOQ and in some part of my code, I want to know if the Field instance that I have is an instance of Lower.
Currently, I use field.getName().equals("lower") but this isn't as robust as I'd like it to be.
I realize that since Lower is a protected class, it isn't part of the API and that it can change but I am ok with that.
Class.forName("TheProtectedClass").isAssignableFrom(foo())
although it is a bad idea for many reasons. You're breaking the encapsulation and the abstraction here. If it's package-private, you shouldn't have to concern with it outside. If it's protected, you should explicitly inherit from it and use the API provided by class for this case.
The less obvious but more correct solution is to get an instance of TheProtectedClass, and compare it by
guaranteedTPCInstance.getClass().isAssignableFrom(foo())
, while still being kind of hacky, at least is more portable and OOPy IMO.
As to your idea of creating a class in the same package as TheProtectedClass to avoid being package-private - it's a viable solution, but a) it breaks the basic principle of encapsulation and the programming contract of the TPC class; packaging is done by library/class authors for a reason - to prevent irresponsible data access and using private API or undocumented proprietary methods, b) it's not always possible (and shouldn't be possible in case of properly designed library classes), since those classes can be not only package-private, but final or effectively final (anonymous inner classes etc) - for the reasons described by Bloch in EJ 2nd, "favor composition over inheritance" item, see also Good reasons to prohibit inheritance in Java? Use of final class in Java etc c) you can't do it with some Java library classes, as you can't define your class to be and use e.g. java.lang package. As such, the only "portable" solution is through reflection and through what I described.
tl;dr The fact you can piggyback another package by mimicking its package definition is an obvious C-style deficiency of Java's syntax (allowing programmer to do what he shouldn't be able to normally do; same goes with some specific reflection methods); hacks made this way are neither maintainable nor safe.
NOTE: If you you expect to do something in a internal implementation-dependent and, at the same time, portable and maintainable (e.g. impervious to implementation changes/class name changes etc) way, you're obviously expecting the impossible.
It appears that the best solution is to create a package in your project that has the same package as the package-private class and either expose TheProtectedClass.class as a Class<?> or simply add a simple method that checks if your Object is instanceof TheProtectedClass.
This does not require reflection, it is fast and relatively safe (compilation will break if the package-private class changes name).
Is it wrong to have static and non-static methods in the same class?
Not really in regular java programming.
But if you're working extensively with dependency injection you probably have few or no static methods at all. In such a context it's fairly common to have only a few utility classes with static methods, and no other static methods.
No, it's not wrong. For example, a common use is to have static factory methods in a class definition.
I think it's fine to create static utils classes, especially when you're not really sure (yet) what the design should be, because you're still learning about the problem domain.
Staticness is a "yet to designed properly" marker. Often the static solution is perfectly adequate; but sometimes, as the project progresses, you find you do have to rewrite that "whole part", but you have (at that later stage) a far more complete understanding of the problem domain, and are therefore in a position to actually design a "proper solution" to those problems.
I think us programmers hammer ourselves unfairly about "rework". You need to do the work in order to understand the work well enough to do the work properly. I see no way past this catch 22;
I can cite many examples of static from the core API. java.lang.Math, java.util.Arrays, java.util.Collections. BUT please note that these classes are "utils classes" which exist only to provide a bunch of static methods. IMHO, The presence of static methods in a "stateful object" is just begging to be refactored.
I'll betcha that todays API designers would love to be able to split-down Integer (and the other wrapper classes)... BUT they're well and truly stuck with what they've got. Which is a warning in itself... that static implies final, and there's a darn good reason that (unlike C++) java methods can be overridden by default. Static is inherently more "binding" than non-static... down the trick you CAN NOT adapt implementations to different situations, contexts, etc, etc, etc.
Cheers. Keith.
I believe its a bad idea to have static and non-static methods in the same class with the same name. This can be very confusing. I would suggest trying to make the names different in this case.
Similarly, don't make methods with the same name but different case, or methods with the same name as the class in the same class. Both are legal, and even occur in the JDK, but are confusing IHMO.
I guess not especially when dealing with singletons.
In java, is there ever a case for allowing a non-abstract class to be extended?
It always seems to indicate bad code when there are class hierarchies. Do you agree, and why/ why not?
There are certainly times when it makes sense to have non-final concrete classes. However, I agree with Kent - I believe that classes should be final (sealed in C#) by default, and that Java methods should be final by default (as they are in C#).
As Kent says, inheritance requires careful design and documentation - it's very easy to think you can just override a single method, but not know the situations in which that method may be called from the base class as part of the rest of the implementation.
See "How do you design a class for inheritance" for more discussion on this.
I agree with Jon and Kent but, like Scott Myers (in Effective C++), I go much further. I believe that every class should be either abstract, or final. That is, only leaf classes in any hierarchy are really apt for direct instantiation. All other classes (i.e. inner nodes in the inheritance) are “unfinished” and should consequently be abstract.
It simply makes no sense for usual classes to be further extended. If an aspect of the class is worth extending and/or modifying, the cleaner way would be to take that one class and separate it into one abstract base class and one concrete interchangeable implementation.
there a good reasons to keep your code non-final. many frameworks such as hibernate, spring, guice depend sometimes on non-final classes that they extends dynamically at runtime.
for example, hibernate uses proxies for lazy association fetching.
especially when it comes to AOP, you will want your classes non-final, so that the interceptors can attach to it.
see also the question at SO
This question is equally applicable to other platforms such as C# .NET. There are those (myself included) that believe types should be final/sealed by default and need to be explicitly unsealed to allow inheritance.
Extension via inheritance is something that needs careful design and is not as simple as just leaving a type unsealed. Therefore, I think it should be an explicit decision to allow inheritance.
Your best reference here is Item 15 of Joshua Bloch's excellent book "Effective Java", called "Design and document for inheritance or else prohibit it". However the key to whether extension of a class should be allowed is not "is it abstract" but "was it designed with inheritance in mind". There is sometimes a correlation between the two, but it's the second that is important. To take a simple example most of the AWT classes are designed to be extended, even those that are not abstract.
The summary of Bloch's chapter is that interaction of inherited classes with their parents can be surprising and unpredicatable if the ancestor wasn't designed to be inherited from. Classes should therefore come in two kinds a) classes designed to be extended, and with enough documentation to describe how it should be done b) classes marked final. Classes in (a) will often be abstract, but not always. For
I disagree. If hierarchies were bad, there'd be no reason for object oriented languages to exist. If you look at UI widget libraries from Microsoft and Sun, you're certain to find inheritance. Is that all "bad code" by definition? No, of course not.
Inheritance can be abused, but so can any language feature. The trick is to learn how to do things appropriately.
In some cases you want to make sure there's no subclassing, in other cases you want to ensure subclassing (abstract). But there's always a large subset of classes where you as the original author don't care and shouldn't care. It's part of being open/closed. Deciding that something should be closed is also to be done for a reason.
I couldn't disagree more. Class hierarchies make sense for concrete classes when the concrete classes know the possible return types of methods that they have not marked final. For instance, a concrete class may have a subclass hook:
protected SomeType doSomething() {
return null;
}
This doSomething is guarenteed to be either null or a SomeType instance. Say that you have the ability to process the SomeType instance but don't have a use case for using the SomeType instance in the current class, but know that this functionality would be really good to have in subclasses and most everything is concrete. It makes no sense to make the current class an abstract class if it can be used directly with the default of doing nothing with its null value. If you made it an abstract class, then you would have its children in this type of hierarchy:
Abstract base class
Default class (the class that could have been non-abstract, only implements the protected method and nothing else)
Other subclasses.
You thus have an abstract base class that can't be used directly, when the default class may be the most common case. In the other hierarchy, there is one less class, so that the functionality can be used without making an essentially useless default class because abstraction just had to be forced onto the class.
Default class
Other subclasses.
Now, sure, hierarchies can be used and abused, and if things are not documented clearly or classes not well designed, subclasses can run into problems. But these same problems exist with abstract classes as well, you don't get rid of the problem just because you add "abstract" to your class. For instance, if the contract of the "doSomething()" method above required SomeType to have populated x, y and z fields when they were accessed via getters and setters, your subclass would blow up regardless if you used the concrete class that returned null as your base class or an abstract class.
The general rule of thumb for designing a class hierarchy is pretty much a simple questionaire:
Do I need the behavior of my proposed superclass in my subclass? (Y/N)
This is the first question you need to ask yourself. If you don't need the behavior, there's no argument for subclassing.
Do I need the state of my proposed superclass in my subclass? (Y/N)
This is the second question. If the state fits the model of what you need, this may be a canidate for subclassing.
If the subclass was created from the proposed superclass, would it truly be an IS-A relation, or is it just a shortcut to inherit behavior and state?
This is the final question. If it is just a shortcut and you cannot qualify your proposed subclass "as-a" superclass, then inheritance should be avoided. The state and logic can be copied and pasted into the new class with a different root, or delegation can be used.
Only if a class needs the behavior, state and can be considered that the subclass IS-A(n) instance of the superclass should it be considered to inherit from a superclass. Otherwise, other options exist that would be better suited to the purpose, although it may require a little more work up front, it is cleaner in the long run.
There are a few cases where we dont want to allow to change the behavior. For instance, String class, Math.
I don't like inheritance because there's always a better way to do the same thing but when you're making maintenance changes in a huge system sometimes the best way to fix the code with minimum changes is to extend a class a little. Yes, it's usually leads to a bad code but to a working one and without months of rewriting first. So giving a maintenance man as much flexibility as he can handle is a good way to go.