5

I am unable to come up with an explanation for why Java does not permit non-static nested interfaces. It makes a difference in the interaction between nested types and generics, since a non-static nested type would be implicitly generic from its enclosing type. The following uses a nested interface as a supertype of two nested classes, and seems to me to be a reasonable use of a non-static nested interface:

public class BSTMap0<K extends Comparable<K>, V> {

    private interface Node {
        Node put(K key, V value);
        V get(K key);
    }

    private class NullNode implements Node {
        public Node put(K key, V value) {
            return new RealNode(key, value, this, this);
        }
        public V get(K key) { return null; }
    }

    private class RealNode implements Node {
        // details omitted
    }
    private Node root;
    public BSTMap0() { root = new NullNode(); }
    public void put(K key, V value) { root = root.put(key, value); }
    public V get(K key) { return root.get(key); }
}

This is not legal Java because the Node interface is implicitly static and hence is not implicitly generic. There are two simple work-arounds. We could make the interface explicitly generic:

public class BSTMap1<K extends Comparable<K>, V> {

    private interface Node<KK extends Comparable<KK>, VV> {
        Node<KK,VV> put(KK key, VV value);
        VV get(KK key);
    }
    private class NullNode implements Node<K, V> {
        public Node<K,V> put(K key, V value) {
            return new RealNode(key, value, this, this);
        }
        public V get(K key) { return null; }
    }
    // ... etc

This strikes me as unnecessarily verbose and ugly. Alternatively, we could make the interface be an abstract class with all abstract methods:

public class BSTMap2<K extends Comparable<K>,V> {

    private abstract class Node {
        abstract Node put(K key, V value);
        abstract V get(K key);
    }

    private class NullNode extends Node {
        public Node put(K key, V value) {
            return new RealNode(key, value, this, this);
        }
        public V get(K key) { return null; }
    }
    // ... etc

I don't care for this because the Node type exists only for polymorphism, not for implementation inheritance, and thus conceptually it's an interface, not an abstract class.

Can anyone give an explanation why Java disallows the first version, which to me seems like the right solution? A similar question was asked here a few years ago, but wasn't really answered. I'm hoping the example I gave will motivate the question better.

Edit: Since posting, I realized (as a comment below also pointed out) that this rule is older than generics in Java. Without generics there wouldn't be a need for a nested interface to be non-static, and once generics were introduced to Java presumably the rule would need to be preserved for backward compatibility (legacy classes exist that declare implicitly static nested interfaces which would suddenly become non-static if the rule were changed). To refine my question, is this rule only for backward compatibility, or is there an inherent reason for it that I'm missing?

  • Probably because of type erasure. – SLaks Feb 13 '18 at 22:03
  • 1
    Interfaces don't have any member variables; so what would it mean for an interface to be non-static? – Andy Turner Feb 13 '18 at 22:05
  • First of all, I have no "real" idea why, you'd have to go ask the developers of the language. However, in order for a nested `interface` to be "non-static", it would require an instance of the parent class before it could be instantiated, which doesn't really make it useful in a general context. Sure, in your case, you're not intending to have any classes implemented outside of the context of the `BSTMap` parent, but as a general concept, it's not something the compiler can guarantee - unless of course we have "leaked private implementation" compiler error – MadProgrammer Feb 13 '18 at 22:10
  • You could also create a `protected interface` within the same package to achieve a similar result, although you still need to specify the generic constraints for the `interface`, which is then defined by the implementation, but that's a choice for you to make – MadProgrammer Feb 13 '18 at 22:12
  • @Slaks No. The rule has been there from the beginning, long before Generics. – user207421 Feb 13 '18 at 22:13
  • Because ['a member interface is implicitly `static`](https://docs.oracle.com/javase/specs/jls/se9/html/jls-8.html#jls-8.5.1). – user207421 Feb 14 '18 at 03:38
  • @EJP: Until generics, the distinction was meaningless. – SLaks Feb 14 '18 at 15:04
  • Duplicate of https://stackoverflow.com/questions/27096561 IMHO – raner Jul 27 '20 at 06:15

0 Answers0