3

I have a rather technical question concerning the definition of override-equivalent (JLS 8.4.2) in the context of receiver parameters.

Edit: After posting this question, there was a lot of confusion in the comments about receiver parameters. Many people seemed to believe that having a parameter named this in the code below is illegal and therefore misunderstood the question. Please refer to https://docs.oracle.com/javase/specs/jls/se9/html/jls-8.html#jls-8.4.1 if you do not know this feature. A perhaps more easily understandable explanation can be found here: http://blog.joda.org/2015/12/explicit-receiver-parameters.html. This question is very technical and addresses rather experienced Java developers who are familiar with receiver parameters and know the Java Language Specification (JLS) well.

The term override-equivalent is defined as follows:

The signature of a method m1 is a subsignature of the signature of a method m2 if either:

  • m2 has the same signature as m1, or
  • the signature of m1 is the same as the erasure (§4.6) of the signature of m2.

Two method signatures m1 and m2 are override-equivalent iff either m1 is a subsignature of m2 or m2 is a subsignature of m1.

Thus, if I understand correctly, the two methods in the following class are not override-equivalent, even though, intuitively, I would have expected them to be:

class A {
    void foo(A this) { /* ... */ }
    void foo() { /* ... */ }
}

Nevertheless, obviously I cannot declare both methods in the same class, and when I try to do so, the compiler rightfully complains that foo() is already defined.

My question consists of two parts:

  1. Do I understand correctly that the above two methods are not override-equivalent?
    • If no: What part of the definition did I miss?
    • If yes: What rule in the JLS formally forbids the above two method definitions to be simultaneously present in the same class, since in this case it cannot be the rule "It is a compile-time error to declare two methods with override-equivalent signatures in a class." (JLS 8.4.2)
Malte Skoruppa
  • 1,232
  • 1
  • 19
  • 25
  • 2
    if they don't have the same parameters, it's not overriding, it's overloading. you can't override a method from within the same class, only in a subclass – Stultuske Mar 28 '18 at 11:59
  • What do you mean, "I cannot declare both methods in the same class", you very well can. Have you tried to? – daniu Mar 28 '18 at 12:00
  • @daniu he means when they both have the same parameters – Stultuske Mar 28 '18 at 12:01
  • 1
    @daniu Of course I tried, and no, it is not possible. :) – Malte Skoruppa Mar 28 '18 at 12:04
  • 2
    @Stultuske I am not sure what you mean by "he means when they both have the same parameters". What I mean is the two exact methods posted above. They cannot co-exist, obviously. Therefore I would also not call this method "overloaded". – Malte Skoruppa Mar 28 '18 at 12:05
  • 1
    The problem is that you do not understand what “this” means in this context. Both signatures have no arguments. – cpp beginner Mar 28 '18 at 12:06
  • The simple answer to your question is No, but there is no precise question, and I clearly don''t understand If you are trying to override, overload, or if you don't know the differences, – pdem Mar 28 '18 at 12:08
  • 2
    @MalteSkoruppa you can't use 'this' as a variable name. the above code wont' compile. give it a valid name, and it will work – Stultuske Mar 28 '18 at 12:08
  • 1
    @Stultuske you can write void foo(A this). Try it – cpp beginner Mar 28 '18 at 12:11
  • @cppbeginner Yes, you can, but after that you cannot write `void foo()`; – zlakad Mar 28 '18 at 12:13
  • 2
    Your question should draw more attention to the fact that it’s about receiver parameters. 99% of Java developers don’t know they exist. – cpp beginner Mar 28 '18 at 12:14
  • @cppbeginner Indeed, I noticed so too while reading all these comments. Most commenters seem to not know receiver parameters. I have updated my post accordingly. – Malte Skoruppa Mar 28 '18 at 12:15
  • maybe this shines a bit more light on it: https://stackoverflow.com/questions/24291091/why-can-we-use-this-as-an-instance-method-parameter – Stultuske Mar 28 '18 at 12:17
  • @cppbeginner you're right. it is possible, but void foo(A this) is equal to void foo() . It just allows the developer to annotate the A-type. – Stultuske Mar 28 '18 at 12:19
  • @cppbeginner You said "The problem is that you do not understand what “this” means in this context. Both signatures have no arguments." First: I am unsure if you meant me when you said "you do not understand what "this" means in this context.", but, just in case you did, please rest assured I do understand. :-) In fact, receiver parameters are the very point of this question. Second: When you say "both signatures have no arguments", do you mean "both signatures have no *parameters*" (I would disagree: the first method does have a parameter, even if it is a special one), or do you mean – Malte Skoruppa Mar 28 '18 at 12:49
  • "calling either method requires no arguments" (I agree of course, and it is the reason why I said both methods cannot co-exist)? The second point is 100% right, but it still does not answer the question whether these two methods are to be considered override-equivalent. – Malte Skoruppa Mar 28 '18 at 12:50
  • @MalteSkoruppa I did mean you, but I wrote that comment before I saw you’d mentioned receiver parameters in the title. As soon as I noticed I suggested you updated the question, but I did not delete the comment. I generally use the terms argument and parameter interchangeably, but this is probably wrong. You can definitely override a method without a receiver parameter with one with a receiver parameter, so yes they can be override equivalent. I don’t know where exactly JLS covers this. – cpp beginner Mar 28 '18 at 14:26
  • @cppbeginner Yes, it's a bit confusing to mix up the terms *argument* and *parameter* in my opinion. :) Anyway, thanks a lot! Your comments pointed me in the right direction and helped me find the relevant parts of the JLS; I have posted an answer with the relevant quotes. – Malte Skoruppa Mar 28 '18 at 20:50
  • 2
    It’s a good answer. +1 I was planning to find the relevant quotes in the JLS when I got in, but now I don’t need to! – cpp beginner Mar 28 '18 at 20:55
  • @Stultuske for the future, I STRONGLY suggest that you, at the very least, attempt to validate your claim before making it in a comment. (Online compilers are popular now, so this should be very easy.) Receiver parameters are not a variable declaration and have been allowed for a while. Comments that are wholly fallacious are misleading to the many people that trust them. :( – Kröw Apr 17 '23 at 02:28
  • @Kröw meaning -> if you have both those methods, you have "overloaded" (since that is what actually happens) methods with identical signatures. See why that might be a problem? Could you please point out where, even if my comment may not have addressed everything, was incorrect? Also: it was a comment, not an answer. Comments are not meant to answer everything, just to make a note about something. – Stultuske Apr 17 '23 at 10:06
  • Hey @Stultuske, just to clarify, the above two methods are not in fact "overloaded" as you say. To quote from the [JLS, 8.4.9](https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.4.9): "If two methods of a class (...) have the same name but _signatures that are not override-equivalent_, then the method name is said to be overloaded." The two methods in my post do have signatures that _are_ override-equivalent (see accepted answer), therefore they do not fulfill this definition and hence are not overloaded. I fully understand it can be confusing and I hope this clarifies. – Malte Skoruppa Apr 17 '23 at 11:53
  • @Stultuske Again, please try the code. Those two methods are **not** overloads. Your claim that `this` is not a valid variable name was extremely misleading. Please see the answer to this question for details. To answer your question, the whole of your comment was incorrect. I understand that comments are not meant to answer everything (usually they are used to ask for clarification), but stating something wrong is a different matter. – Kröw Apr 17 '23 at 18:40
  • @Kröw my mistake. so, my first comment, which you also objected to, was correct then? " if they don't have the same parameters, it's not overriding, it's overloading. you can't override a method from within the same class, only in a subclass" granted, the first sentence was a bit unnecessary, but "you can't override a method from within the same class". How was that incorrect, again? – Stultuske Apr 18 '23 at 07:08
  • @Stultuske The answer to this question describes it in more detail, but to summarize, the two methods in the question **do have the same parameters**. `A this` is **not** a formal parameter. (It is more like auxiliary syntax.) Your first comment was wrong to claim that the code would not compile. It was *also* misleading in saying that you can't use `this` as a variable name, because the question is *not* using `this` as a variable name. – Kröw Apr 18 '23 at 20:29
  • @Kröw quite a bit depends on what version of Java is being used. For instance, you comment on my first comment, but that wasn't my first comment. What is asked about here is (relatively) new in Java, and OP didn't specify which version is being used. I can guarantee you, though, that if I add it in the version I currently write in, it'll block compilation. – Stultuske Apr 19 '23 at 06:41
  • @Stultuske It was added in Java 8. However, the fact that your comment is misleading does *not* depend on the Java version. I think a more appropriate question that could have been asked at the time is "What version of Java is this?" or the like. – Kröw Apr 20 '23 at 02:55
  • @Kröw odd. When I (try to) compile it on jdk11 set to Java 8 restrictions, it fails. Perhaps you 're right, and should I have stated for version X it doesn't work. Then again, instead of immediately stating that I'm absolutely wrong, maybe you could have pointed out that while it used to be right, I was referring to an out of date version. – Stultuske Apr 20 '23 at 07:27
  • @Stultuske I do not know what to tell you. Receiver parameters are still valid in version 8 and above. In fact, when I test them in Java 8 and in Java 18, they work fine. – Kröw Apr 20 '23 at 18:38

1 Answers1

4

Ok, I found out the answer, and I'll just leave it here in the hopes it may someday help someone.

The answer to my two-part question would be:

  1. No, the two methods in the original post are override-equivalent.

  2. The relevant part of the definition is the first sentence of JLS 8.4.2:

Two methods or constructors, M and N, have the same signature if they have the same name, the same type parameters (if any) (§8.4.4), and, after adapting the formal parameter types of N to the the type parameters of M, the same formal parameter types.

I had read that part, but I assumed that a receiver parameter, while special, is also to be considered as (a special kind of) formal parameter. Thus I assumed that receiver parameters were implicitly included in the sentence "(...) after adapting the formal parameter types (...)" (emphasis mine). However, that is not the case. Receiver parameters are not also formal parameters; rather, the definitions of receiver parameter and formal parameter are mutually exclusive. Therefore, receiver parameters are not part of a method signature as defined in the above quote. Since the definition of override-equivalent uses the definition of a method signature, this also means that the two methods in my original post are in fact override-equivalent (because they do have the same signature!).

The relevant section that clarifies that receiver parameters are indeed not also formal parameters comes slightly later in section JLS 8.4.2:

The receiver parameter is an optional syntactic device for an instance method or an inner class's constructor. For an instance method, the receiver parameter represents the object for which the method is invoked. For an inner class's constructor, the receiver parameter represents the immediately enclosing instance of the newly constructed object. Either way, the receiver parameter exists solely to allow the type of the represented object to be denoted in source code, so that the type may be annotated. The receiver parameter is not a formal parameter; more precisely, it is not a declaration of any kind of variable (§4.12.3), it is never bound to any value passed as an argument in a method invocation expression or qualified class instance creation expression, and it has no effect whatsoever at run time.

(emphasis mine)

The statement that the receiver parameter is not a formal parameter got a bit lost in that big blob of text there.

Malte Skoruppa
  • 1,232
  • 1
  • 19
  • 25
  • 1
    Excellent answer (and question)! Both are abundantly clear and well-researched, and quote the specification to answer the question in a very straightforward manner. This is exactly what questions on this site should be like! – Kröw Apr 17 '23 at 02:25