Cyclic inheritance and interfaces - class A can

2020-02-11 06:17发布

问题:

I have:

public class A implements BListener {
    public interface AListener {}
}

public class B implements AListener {
    public interface BListener {}
}

So, if I understand correctly the cyclic inheritance happens because:

The compiler goes to A and says "hey, A implements BListener, let's go find BListener!"

Then when it tries to find BListener, it eventually gets to B, which it says:

"Hey, BListener, needed for A is inside B! BUT WAIT! B needs AListener! Let's go find AListener!"

And then it gets to A, repeat. Did I get this right?

By the way, this compilation error happened to me on Android development.

回答1:

It may help to draw it out.

              >A 
is part of  /     \    inherits
                   V             
   AListener        BListener
           ^
  inherits  \     /    is part of 
               B<

A lovely circle. You can't create one of them without the others already existing.

Is the compiler a squirrel with ADHD high on coffee chasing it's own tail?

Nope because a the squirrel would not stop (until the caffeine ran out). The compiler looks for this and then gives up.

Note: Eclipse has a bug which allows this setup.



回答2:

After further investigation, I was initially wrong.

The technical explanation for the behavior you are noticing is the following

From the Java Language Specification chapter on Superclasses and subclasses

A class C directly depends on a type T if T is mentioned in the extends or implements clause of C either as a superclass or superinterface, or as a qualifier in the fully qualified form of a superclass or superinterface name.

A class C depends on a reference type T if any of the following is true:

  • C directly depends on T.
  • C directly depends on an interface I that depends (§9.1.3) on T.
  • C directly depends on a class D that depends on T (using this definition recursively).

It is a compile-time error if a class depends on itself.

Let's take your code, with fully qualified names for type uses, assuming the classes were declared in package com.example:

public class A implements com.example.B.BListener {
    public interface AListener {}
}

public class B implements com.example.A.AListener {
    public interface BListener {}
}

Following the rules from the JLS above

  • A directly depends on BListener, because it's mentioned in its implements clause.
  • A directly depends on B, because it's mentioned as a qualifier in the fully qualified name of a superinterface (BListener is com.example.B.BListener)
  • B directly depends on AListener, because it's mentioned in its implements clause.
  • B directly depends on A, because it's mentioned as a qualifier in the fully qualified name of a superinterface (AListener is com.example.A.AListener)
  • A directly depends on B that depends on A.

Therefore A depends on A and a compilation error should occur.


In Eclipse, the error occurs if you qualify the names

class A implements B.BListener {
    public static interface AListener {
    }
}

class B implements A.AListener {
    public static interface BListener {
    }
}

If you use import statements, however, it doesn't. I'll be opening a bug with them.