2

I'm not able to wrap my head around this. I am at dis-ease with this type declaration I came across in our codebase. Can someone please help me understand this?

public interface CodeReviewRequest<Q extends CodeReviewRequest<Q>> {
....
}
YaMiN
  • 3,194
  • 2
  • 12
  • 36
Shaw Ankush
  • 113
  • 6
  • Take a look at [this comment](https://stackoverflow.com/questions/7385949/what-does-recursive-type-bound-in-generics-mean/64901718#64901718) and this article could also be helpful http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ106 – dshelya Dec 28 '21 at 14:46

1 Answers1

0

This is actually a common tactic in the Java language. For example, look at the class definition for Enum.

Whenever you have an Interface or an Abstract Class in Java, you can use this circular declaration to assert the following claim: Any class that directly implements/extends me must use either themselves as the type parameter or another type that implements/extends me.

This is particularly useful because it limits what can be placed into the type parameter, while still leaving it open for extension.

For example, consider the following code.

public class ExtendsTest
{

   public interface User<Q extends User<Q>> { ... }
   
   public class ReadUser implements User<ReadUser> { ... }
   
   public class ReadWriteUser implements User<ReadWriteUser> { ... }
   
   public class Admin extends ReadWriteUser { ... }
   
   public class StringUser implements User<String> { ... } //this fails because String is not an acceptable cornerstone type
   
}

By using this tactic, you are essentially creating a gated community of possible types that can be used in the type parameter. That gated community can expand as freely as you like, but they all must implement the interface using either themselves, or another class that implements the interface. And you might notice - the type that sort of acts like the cornerstone (besides the interface) is CommonUser. When using this gated community tactic, you will always end up with one or more types at the top that use this circular declaration to define themselves. Thus, you can have several mini communities within your gated community. It's a powerful way to set boundaries through typing and extension, while still letting things be fairly free and open ended.

EDIT - After a good discussion with @newacct, I should really mention that interface User<Q extends User<Q>> is (as far as I can tell) equivalent to interface User<Q extends User<?>>. So either/or on the syntax. My points about the benefits still stand, but it's important to clarify the syntax.

davidalayachew
  • 1,279
  • 1
  • 11
  • 22
  • 1
    "you can use this circular declaration to assert the following claim: Any class that directly implements/extends me must use either themselves as the type parameter or another type that implements/extends me." But it's rare to have a situation that would require a type parameter that is "another type that implements/extends me" – newacct Jan 09 '22 at 07:02
  • @newacct Agreed, however, it has its uses. For example, if I was coding for a hotel, having a cornerstone class for the most basic room features (water, electricity, internet access, etc.), and then letting children extend from that (suite room w/jacuzzi) would be useful. But what if I want to be able to manage the non-guest rooms as well? Maybe they would only have water and electricity, or only electricity and internet, and so on. Then, I could have another cornerstone class for non-guest rooms, then later give it children like boiler room or IT Office. It's a useful, but rare abstraction. – davidalayachew Jan 09 '22 at 09:05
  • 1
    In your case above, it would compile if `User` were declared as `public interface User`, so it does not demonstrate a case where this bound is useful. The only case where I can think of this bound being useful is if `User` had a method that returned the type `Q` (or returned a type that allowed you to get `Q`), in which case if you have a `User`, you can call the method to get a `User`, and call the method again to get a `User`, etc., without knowing what `Q` is. – newacct Jan 09 '22 at 18:11
  • @newacct I have updated my example to better explain its intent. Yes, as you mentioned, you can compile with just `User`, but the goal is to restrict the types of potential child classes that implement from this interface. By limiting what can be considered a User, we have put up walls that prevent logical bugs further down the line. Using my updated example, you can see how just using `User` as opposed to `User>` can allow for functionality that we would not otherwise want. – davidalayachew Jan 09 '22 at 22:28
  • 1
    But what is the benefit to disallowing a class from implementing `User`? If someone wants to write a class that implements `User`, and do it in a type-safe way (type safety is enforced by the compiler), why shouldn't they be able to? It doesn't affect the type safety of classes that implement `User`, `User`, etc. Generic bounds should be as wide as possible. If the code can compile without the bound, that's a sign that the bound is unnecessary. – newacct Jan 10 '22 at 05:37
  • @newacct While I understand and appreciate where you are coming from, I don't think that your rule can be applied freely to all bounds. The real benefit that comes from restrictions such as these is that you can maintain some specific invariants while allowing your code to be free for extension. For example, let's say that I want the user interface to have a `Q getMyManager()` method. If I allow all types for Q, I actually offer myself less guarantees about the return type of the method. Alternatively, if I just set the return type to ``, then..... (1/2) – davidalayachew Jan 13 '22 at 00:33
  • (2/2)......the method either forces you to figure out exactly what type of user it is, or it forces you to play with only the methods guaranteed by `User` until you cast. My solution allows you to know exactly the type of the `User` that you are getting, while also restricting the return type to only be of types `User`. – davidalayachew Jan 13 '22 at 00:34
  • 1
    If you have an `interface User>` which has a method `Q getMyManager()`, then the `Q extends User` part is indeed useful as it allows you to use the result of `getMyManager()` as a `User`, without knowing what `Q` is (although `User>` would also achieve that). So in this case, it is possible to write code that compiles with the bound that would not compile without it. But in the example in your answer, `User` doesn't have a method that returns `Q`, so I don't think it's possible to write code that compiles with the bound that would not compile without it. – newacct Jan 13 '22 at 19:18
  • 1
    But even with `interface User>` and method `Q getMyManager()`, you would either have a value of a specific type of user, or you have a `User>` without knowing what the type of user is (because if you knew what `Q` is, then why not just use `Q` instead of `User`?). So in the latter case it would still be true that "it forces you to play with only the methods guaranteed by `User`" on the result of `getMyManager()`. – newacct Jan 13 '22 at 19:25
  • @newacct Apologies for the delayed response. After having looked at this for a while, I concede that `User>` is about as useful as my original idea of `User>`. I mistakenly assumed that the question mark would allow undesirable types to be used there. And yes, my example given wasn't great, but the point I meant to make was that you can better restrict the class type parameter if you use `interface User>` as opposed to just `interface User`. Thank you for catching and pointing that out, I have edited my answer now to reflect this fact. – davidalayachew Apr 06 '22 at 04:50