19

I.e. why is the following "cyclic dependency" not possible?

public class Something implements Behavior {
    public interface Behavior {
        // ...
    }
}

Since interfaces don't reference the outer class this should be allowed; however, the compiler is forcing me to define those interfaces outside the class. Is there any logical explanation for this behavior?

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Philip Kamenarsky
  • 2,757
  • 2
  • 24
  • 30
  • Sounds like the classloader would have to read the class first to know the interface which it needs to define the class in the first place... I don't know the details of classloading, but it seems pretty obvious. –  Nov 03 '11 at 10:52
  • 2
    @donneo: since the compiler complains about a "cyclic dependency" I imagine it already knows which types are defined in the inner class. It just seems like an arbitrary restriction to me. – Philip Kamenarsky Nov 03 '11 at 10:59
  • @PhilipK: what compiler are you using? Mine (Oracle JDK 6 and 7) only complain that they "cannot find symbol". Apart from that: good question, since a nested interface doesn't really *rely* on the outer class in any technical way, this *could* be legal. – Joachim Sauer Nov 03 '11 at 12:21
  • @JoachimSauer: I'm also using the standard Oracle JDK 6; however, in the example above you need to actually write ``... implements Something.Behavior`` - my IDE automatically included the necessary import statement. When you do this the compiler should bomb out with a cyclic dependency error. – Philip Kamenarsky Nov 03 '11 at 13:10

3 Answers3

11

Relevant rules in spec:

http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.4

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 of a superclass or superinterface name.

http://java.sun.com/docs/books/jls/third_edition/html/interfaces.html#9.1.3

An interface I directly depends on a type T if T is mentioned in the extends clause of I either as a superinterface or as a qualifier within a superinterface name.

Therefore if A extends|implements B.C, A depends on both C and B. Spec then forbids circular dependencies.

The motivation of including B in the dependency is unclear. As you mentioned, if B.C is promoted to top level C2, not much is different as far as the type system is concerned, so why A extends C2 is ok, but not A extends B.C? Granted a nested type B.C does have some prviledged access to B's content, but I can't find anything in spec that makes A extends B.C troublesome.

The only problem is when C is an inner class. Suppose B=A, A extends A.C should be forbidden, because there's a circular dependency of "enclosing instance". That is probably the real motivation - to forbid outer class from inheriting inner class. The actual rules are more generalized, because they are simpler, and make good sense anyway even for non-inner classes.

irreputable
  • 44,725
  • 9
  • 65
  • 93
9

Imagine you are the compiler.

We are saying you to create a class Something. This class implements Behavior... But Behavior does not exist yet because Something is not already registered...

Do you understand the problem ?

See class as box which contains things. Behavior is contained in the box Something. But Something does not exist.

Jerome Cance
  • 8,103
  • 12
  • 53
  • 106
  • 3
    This would be a valid answer if the question was about C++. – Luchian Grigore Nov 03 '11 at 10:53
  • 1
    Yes, but Behavior is an interface and thus doesn't depend upon the creation of Something. – Philip Kamenarsky Nov 03 '11 at 11:03
  • Yes it does, because that interface is part of Something. Something needs to exist before you can reference Behavior, but to create Something you need to reference Behavior. – Mark Rotteveel Nov 03 '11 at 11:43
  • @MarkRotteveel: Actually no, you don't need an instance of Something in order to reference Behavior, since Behavior is an interface. – Philip Kamenarsky Nov 03 '11 at 13:14
  • 2
    In your case you obviously don't have imported "Behaviour" from somewhere. Lets assume your class Something is in the package "my.fancy.pack" then the full name of Somthing is "my.fancy.pack.Something". So, in your "implements" part you basically write: class my.fancy.pack.Something implements my.fancy.pack.Behaviour". However your interface is: "my.fancy.pack.Something.Behaviour"! Hence as Jeromy correctly pointed out the compiler can not find your behaviour with the "implied" name. – Angel O'Sphere Nov 03 '11 at 14:02
  • 3
    @PhilipK I wasn't talking about instances: I was talking about classes. An inner-interface is in the namespace of its enclosing class, so to be able to reference it, the enclosing class needs to have been loaded, and it can't be loaded because it is referencing its enclosed interface. – Mark Rotteveel Nov 03 '11 at 14:14
  • This doesn't really matter to a Java compiler. You can have circular references all over the place; as long as you provide the source code for the complete circuit to the compiler, it can progressively define the necessary types. – erickson Nov 03 '11 at 16:08
0

The simple fact that the language specs forbid it should be enough.

Some reasons I could think of:

  • It wouldn't be useful.

  • For whatever reasons you might want to use this, I'm sure there exist better options.

  • Child classes should extend base classes, so why would you declare a base class inside its own child?

  • It would be counter-intuitive having a separate class extend your inner-class.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625