Why does java use an @Override annotation, instead of a modifier?

What were the motivations for java to use the @Override annotation, instead of creating a new override modifier?

@Override
public String toString() {
   return "";
}

vs.

public override String toString() {
   return "";
}

1

@Override was originally not in the language. When the need was felt to add it, it was easier to use a generic mechanism (annotations) than to add a new keyword to the language. Adding a new keyword is always an incompatible change since it can break programs which use that words as an identifier.

In languages which add an override marker right from day one, it is quite often a keyword (examples are Kotlin and Scala), in Java it was a matter of staying backwards-compatible with older Java version which did not have any override marker.

7

You don’t need the @Override annotation to actually override behavior; It’s an annotation because it’s simply adding some context of the method’s intent for the compiler, not changing the method itself.

Without the annotation, you may intend to override functionality, but accidentally fail to do so (by using a slightly different signature). Adding the annotation tells the compiler to generate an error if this method isn’t actually overriding behavior.

As such, it makes perfect sense as an annotation.

1

Very good question!

In general, as we can see from other programming languages, the intention of overriding a class member is expressed via a keyword. This is logical, because keywords are intended to be processed by the compiler, and their effect is manifested only during compilation. At runtime, the program has the keyword effect already baked in. This type of behavior establishes that a keyword has a stronger and more persistent effect on a program’s behavior.

On the other hand, we have ways to add metadata to the code we write. In java and other JVM languages, we use annotations. In .NET the same things are called attributes. Nevertheless, in the majority of cases, annotations are intended to be interpreted at runtime, where they may introduce additional behavior to a program. This type of behavior is weaker than the compiled code, as it could be turned on or off (let’s say via configuration), or its implementation changed — all of which can be decided at runtime. Also, those type of behaviors are not always transparent of what actually happens behind the scenes (AOP anyone?), compared to the degree of clarity we get with a keyword.

Still, in Java we see a fundamental operation in the object-oriented design, such as overriding an inherited member, to be expressed in a weaker manner than let’s say the transient keyword which says whether a field will be serialized or not. While we can argue that serialization is more of a runtime concern, we can all agree that overriding is a job for the compiler. If you always felt that an annotation is not quite right for the job this means you are making good sense of things. So, how did we end up here anyway?

Well, the @Override annotation in java is an example of an elegant workaround. The folks who created the language allowed implicit overriding of class members by design (without either a keyword or annotation), but as different APIs started to pop out taking on the object-oriented features of the language, soon major bugs appeared because people did not really understood when they were overriding a parent class’s member. Unintended overrides would fail to call the base member defined in the super class, and programs would therefore misbehave.

Today, we deem inheritance as a bad OOP design practice, but in the earlier years of OOP it was quite abused — no wonder we came to the conclusion it was a bad idea. Anyway, in those confusing times, the language designers understood the need to make overriding more noticeable among developers, and also understandable by the compiler. Introducing a keyword would have been a working solution, except for breaking backwards compatibility. And the Java language is known for having a very very strong backwards compatibility policy, on which the reliability and scalability of the Java platforms stands. The only acceptable solution — that would not break existing code and still accomplish the effect of a keyword — was to introduce an annotation, and make the compiler understand it. Until this day, Java stays true to its backwards compatibility covenant, sticking with that same annotation. We can get slightly irritated when we have to use it instead of a regular keyword, but it is a good example of an elegant workaround (don’t we programmers pride ourselves on those?), and a good reminder of how a good intention (as was the implicit overriding at first) could pave a road to hell.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *