4

I just started writing java and today I read some code like this:

public interface A extends B<A>{
    ...
}

public interface B<E extends B<E>>{
    ...
}

Of course I can understand the code, but it makes me really confused. It looks like ... I used myself to create myself? How the compiler deal with it?

Syaoran
  • 59
  • 6
  • Yes: it's confusing. But useful. – Maurice Perry Sep 29 '20 at 09:30
  • Can you elaborate Maurice Perry? I have been coding in java for over 13 years and have never seen this idiome. I don't see what this is good for. – TreffnonX Sep 29 '20 at 09:33
  • What exacly is your question? The answer to 'how does the compiler deal with it' is that it compiles it, but that involves lexical and syntactic and semantic analysis, all of which is the subject of long courses and large books. Unclear what you're actually asking. – user207421 Sep 29 '20 at 09:34
  • 1
    @TreffnonX Have a look at `Comparable`. You must have seen that. – user207421 Sep 29 '20 at 09:35
  • I believe this should asnwer your question: https://stackoverflow.com/questions/7282677/the-syntax-t-extends-classt-in-java – Amongalen Sep 29 '20 at 09:35
  • @MarquisofLorne Comparable is just Comparable. I know generics in general, I just don't know what good the above example would do. How would `A extends B` be a useful constraint? What would the implication be? – TreffnonX Sep 29 '20 at 09:42
  • @TreffnonX Any class that implements `Comparable` should be defined as `class X implements Comparable`. Exactly the same as in the question. – user207421 Sep 29 '20 at 09:46
  • @MarquisofLorne *implements* not *extends*! I get how a class implements Comparable where C is the class, but this example is an interface. Taking Comparable as example, it would just guarantee that whatever implementation of that interface existed would also be comparable with any other class implementing that interface. This seems fragile at best to me. I think the implementing class should describe the generic type itself. – TreffnonX Sep 29 '20 at 09:51
  • @MarquisofLorne It works the same, but it means something else, because an interface is usually extended itself. So any backreference on the interface will reference the interface, but not the class implementing it. This *is* a very material difference in semantics. All I am saying is, that this makes a lot of sens with classes, but (imho) little regarding interfaces. (And I don't mean generic interfaces in gereral, but only regarding the above examples). I can't imagine what the 'API developer' intended with this, but then again, the context is missing here. – TreffnonX Sep 29 '20 at 09:57
  • @MarquisofLorne I can't claim comprehensibility for my argument, but relevance is in the eye of the beholder. Whether it is relevant for OP is for them to decide. I am just noting that I cannot imagine an applciation for above pattern regarding an interface. If you have a comprehensible example where it may be useful, I will be taught better. – TreffnonX Sep 29 '20 at 10:02
  • @TreffnonX The beholder is the OP, and that's not what he asked about. You may not be able to think of a use, but the OP has clearly *already* encountered a use, and is asking how the compiler deals with it. Furthermore, you claimed something much stronger than not being able to think of a use: you claimed that you had never encountered it, when you had. – user207421 Sep 29 '20 at 10:17
  • @MarquisofLorne I have not encountered it. And Comparable is not an applicable example. Again, this does not mean, that it makes no sense, just that I can't see it. And I don't intent to stirr dirt here, just mentioning the (to me) exotic character of the examples asked about. – TreffnonX Sep 29 '20 at 10:31

2 Answers2

2

To answer your explicit question, the compiler validates generics by checking constraints against each other, validating that the specific types in question are legal/matching the generic references. After that (for the output code) generic types are erased. This is called 'type erasure'. The explicit steps are explained here: https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

But more interesting are the semantics of what you apparently encountered.

Generic types don't mean 'use to create' but rather 'regarding' (in whatever way). Take List for instance. List<A> means 'List containing instances of class A'. For the code calling an instance of that, it means that all methods referencing the generic type will directly or indirectly regard object(s) of type A.

Any class implementing public interface B<E extends B<E>> will be required to regard themselves in that respect. The point of this might be that at a later point methods or attributes that refer to E will then yield the type implementing them. An example could be:

public interface Chainable<E extends Chainable<E>> {
  public void append(E followup);

  public E getNext();
}

...

public class MyLink implements Chainable<MyLink> {
  public void append(MyLink l) {
    ...
  }

  public MyLink getNext() {
    ...
  }
}

This way, it is ensured, that any class implementing Chainable will have a method taking, and one returning an object of that class, rather than just any object.

While the above alone is useful in terms of constraints I cannot see the better use of your example's public interface A extends B<A>. The reason for this is, that any class now implementing A is just guaranteed to regard A but not the implementing class itself (which would be much more useful). Example:

public class C implements A {
  public A methodDeclaredInB(A someParam) {
    ...
  }
}

The method above only knows A at compile time, but not C. If instead someone wrote:

public class C implements B<C> {
  public C methodDeclaredInB(C someParam) {
    ...
  }
}

then class C could be used instead of 'only' A.

There may be cases where it is sufficient to know type A, but usually you want type C, so making a (itself non-generic) interface A that extends B in such a way seems a fragile shortcut to the more verbose but more useful example above.

TreffnonX
  • 2,924
  • 15
  • 23
  • Your example would compile with `Chainable` declared as `public interface Chainable`, so it doesn't demonstrate any purpose for the `extends Chainable`. – newacct Sep 29 '20 at 23:42
  • "This way, it is ensured, that any class implementing Chainable will have a method taking, and one returning an object of that class" This is not true. A class implementing `Chainable` will only be guaranteed to have one method taking, and one method returning, `E`, not necessarily the type of the implementing class. Only if the implementing class chooses to implement `Chainable` with itself as the type argument will it have one method taking, and one method returning, its own type. – newacct Sep 29 '20 at 23:43
  • @newacct "Only if the implementing class chooses to implement Chainable with itself as the type argument will it have one method taking, and one method returning, its own type." - But that is my example "That way" referrs to the class implementing `Chainable`, which is exactly the point. And as for the earlier comment: `public interface Chainable>` binds `E` to `Chainable`. This is important and makes a difference. You can easiely verify that in an IDE: A type *not* `Chainable` cannot be the generic type `E`, which is the purpose. – TreffnonX Sep 30 '20 at 05:33
2

Yes it is astonishing what the compiler is capable of. As said by the other answer(s), generic parameters are used by reference. But there are compilations (.class) and their dependent relations. Notice: some runtime errors are caused by unsynchronized compilation, and you can store a .java file in a jar - I believe).

That juggling with self-references is often used to pass the class of a child class to a super class for restricting things (methods) to the child class.

But the same trick of cyclic behavior can be done as pure classes:

public interface Foo {
    Foo ZERO = new Bar();
}

public class Bar implements Foo {
    ... ZERO ...
}

So the java compiler solves this chicken/egg problem.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • 1
    Important here is: Fields of interfaces are static final! That should be explicit. Here is an q/a to that: https://stackoverflow.com/questions/9446893/fields-in-interfaces Explanation is correct though :) – TreffnonX Sep 29 '20 at 11:40
  • @TreffnonX indeed, `public static final` even. Maybe I also should have mentioned: Classes Foo and Bar are loaded, On initializing Foo an initialized Bar is required. I would prefer explicit too, where it not that it is so useful for enum like constants for open value domains - without syntactic fluff (like `this.`). – Joop Eggen Sep 29 '20 at 11:56
  • 'Generic parameters are used by reference' is meaningless. – user207421 Oct 12 '20 at 09:03