4

Java allows me to define local abstract classes, like in this example:

public class Foo {

    public void foo() {
        abstract class Bar {          // Bar is a local class in foo() ...
            abstract void bar();
        }

        new Bar() {                   // ... and can be anonymously instantiated
            void bar() {
                System.out.println("Bar!");
            }
        }.bar();
    }
}

For some reason, when I try to define a "local interface" instead of the local class, like this:

public class Foo {

    public void foo() {
        interface Bar {           // Bar was supposed to be a local interface...
            void bar();
        }

        new Bar() {               // ... to be anonymously instantiated
            void bar() {
                System.out.println("Bar!");
            }
        }.bar();
    }
}

Java complains that "The member interface Bar can only be defined inside a top-level class or interface". Is there a reason for this? Or am I missing a mistake I made?

Markus A.
  • 12,349
  • 8
  • 52
  • 116
  • 1
    Why doesn't a local abstract class do the job? – Ted Hopp Feb 02 '14 at 21:27
  • @TedHopp It does. An interface would simply be "cleaner" (in my opinion). The only reason for the local class in my actual code is to have it implement two other interfaces (see http://stackoverflow.com/questions/21515693/instantiating-anonymous-inner-classes-in-java-with-additional-interface-implemen). – Markus A. Feb 02 '14 at 21:28
  • Wow, I didn’t know that it’s even possible to define a local class inside a method. I’m just curious, does anyone know about some real-world example where this construct is actually useful? – Jakub Jirutka Feb 02 '14 at 21:29
  • @JakubJirutka I use them for this: http://stackoverflow.com/questions/21515693/instantiating-anonymous-inner-classes-in-java-with-additional-interface-implemen – Markus A. Feb 02 '14 at 21:30
  • @JakubJirutka You can reference (final) variables from inside the local class. @ markus-a This is probably also the reason you can't define an interface there, as you might as well do it on class-level, or in a separate file, it adds nothing to define it in a function – Philipp Gayret Feb 02 '14 at 21:31
  • Note that having a local interface is not very useful from a caller point of view. It doesn't serve any purpose, see my edit. – Sotirios Delimanolis Feb 02 '14 at 21:32
  • @user1066946,SotiriosD. It would keep my code cleaner as it prevents exposing the interface definition beyond the scope where it's useful/needed. – Markus A. Feb 02 '14 at 21:35
  • But were you planning on using that interface for many local classes? Just declare it as a private member of your top level class. – Sotirios Delimanolis Feb 02 '14 at 21:36
  • @SotiriosDelimanolis As per your idea regarding my earlier question (http://stackoverflow.com/questions/21515693/instantiating-anonymous-inner-classes-in-java-with-additional-interface-implemen), I only want to use this local interface once to allow me to instantiate an anonymous inner class that implements two interfaces. – Markus A. Feb 02 '14 at 21:37
  • 1
    I think you're trying too hard to restrict the scope of the interface. Just make it a private member of the class. It's not bad design. – Sotirios Delimanolis Feb 02 '14 at 21:39
  • @SotiriosDelimanolis Probably true. Just makes Eclipse's auto-complete-suggestions longer... And I feel that being a bit "anally retentive" about these sorts of things is just one more thing that helps me write bug-free code even when projects get huge. :) – Markus A. Feb 02 '14 at 21:42
  • Then I'd have to say don't write code to make it easier to work with your IDE. Write your code (following good design patterns) and choose an IDE that works well with it. – Sotirios Delimanolis Feb 02 '14 at 21:44
  • @MarkusA. If you want bug free code, you probably want things to be testable, hidden inner classes won't help you with that. For making autocompletion feel fast in Eclipse, did you already change [this](http://3.bp.blogspot.com/-ARB9Q7jpoXI/UPWTf9iDUiI/AAAAAAAAAJk/Qcmm-mkWTog/s640/Setting.png)? – Philipp Gayret Feb 02 '14 at 21:46
  • @user1066946 I shouldn't have brought up Eclipse... That really wasn't the reason behind this at all... What I meant to say was: "Trying to limit access to any resources in my code to only the relevant scope helps me..." And this local interface really just looks like `interface A extends B, C {}`. So there's nothing to test. I'll just use `abstract class A implements B, C {}` instead and done... :) – Markus A. Feb 02 '14 at 21:51
  • How are you going to use the interface elsewhere? – Behe Feb 02 '14 at 22:46
  • @Behe I won't and I shouldn't. That's exactly the point. ;) I only want to use it inside that method once to create an anonymous class instance of it. Then it'll never be used again. – Markus A. Feb 02 '14 at 23:23

4 Answers4

5

The Java Language Specification doesn't tell you why it was designed the way it was, but it does describe what is and what isn't allowed.

A method body has the following form

MethodBody:
    Block 
    ;

where Block is

Block:
    { BlockStatementsopt }

BlockStatements:
    BlockStatement
    BlockStatements BlockStatement

BlockStatement:
    LocalVariableDeclarationStatement
    ClassDeclaration
    Statement

So a class declaration is allowed, but an interface isn't.


We can argue that having a local interface is not very useful from a caller point of view. It doesn't serve any purpose. An interface is meant to describe behavior, but since the interface would be local, no caller could make use of it. You could just as well define and implement the behavior in a class.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • I guess the interface's purpose to "define behavior for an outside caller", which wouldn't make sense in this context, is probably the reason why they decided to dis-allow it. – Markus A. Feb 02 '14 at 22:03
  • I think Radiodef found the real reason (see below). – Markus A. Feb 02 '14 at 23:20
  • @MarkusA. Well as far as the *real* reason, I think that's just "it doesn't exist". It's just not in the spec for an interface to be local. – Radiodef Feb 02 '14 at 23:27
  • @Radiodef That's the real reason for why it throws an exception, but not the real reason for why it's not in the spec. ;) Overall, I don't think I've ever come across an example in Java where I don't end up finding a reason for why something should be disallowed (and it's not for a lack of digging deeper). For the most part, I've found the JLS to make an impressive amount of sense. I'm always surprised how many totally obscure constructs they managed to consider. The only thing I can remember right now that still does NOT make sense to me is the auto-boxing behavior of the ternary operator. :) – Markus A. Feb 02 '14 at 23:44
5

There simply isn't a definition for it in the JLS. It just doesn't exist.

As for a weak reason, according to the JLS 14.3:

All local classes are inner classes (§8.1.3).

An interface can't be inner (JLS 8.1.3):

Member interfaces (§8.5) are implicitly static so they are never considered to be inner classes.

So we can't have a local interface.

This is, I guess, in addition to what @SotiriosDelimanolis has found that InterfaceDeclaration is not a BlockStatement.

Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • Funny thing is: If you look at the "official" example for "inner classes" (http://docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html) it shows the inner class definition (EvenIterator) right next to a corresponding interface definition (DataStructureIterator), which, I guess by symmetry should be called "inner" as well... :) – Markus A. Feb 02 '14 at 21:58
  • 1
    @MarkusA. *Nested* interfaces are implicitly static. You can't have an inner interface. – Radiodef Feb 02 '14 at 22:42
  • Ahhh! NOW it makes sense! Interfaces are implicitly static! That's the reason! It also complains if I try to define the local class as static (`... static abstract class Bar ...` in my first example raises an error). And, of course, it makes no sense to define anything as "static" inside a method... – Markus A. Feb 02 '14 at 23:22
  • @MarkusA. Careful with the connections you are trying to make. [`A local class is a nested class (§8) that is not a member of any class and that has a name (§6.2, §6.7).`](http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.3). A local class is not a member of the class it is defined in, so why would an interface be? – Sotirios Delimanolis Feb 03 '14 at 04:58
  • @SotiriosDelimanolis Point taken... I just usually think of interfaces as programmatically (although, not logically) mostly equivalent to abstract classes (without fields and only abstract methods). So the implicit "static" on the interface effectively restores that correspondence again for this case... PS: Gotta love that Example 14.3-1. (Local Class Declarations) in your link. What a mess! :) But looking at it twice, pretty much all the scoping is perfectly consistent with the way that the declaration of local variables works... Deep down inside Java just makes sense (to me at least)... – Markus A. Feb 03 '14 at 06:06
3

Local interfaces (and enums) have been introduced along record classes feature:

Unfortunately this feature is a little bit obscured in the documentation - but it works.

Both versions allow to write code like below:

public class Main {

    public int foo() {

        interface Experimentable {
            int bar();
        }

        Experimentable e = new Experimentable() {
            @Override
            public int bar() {
                return 0;
            }
        };

        return e.bar();
    }

    public static void main(String[] args) {
        System.out.println(new Main().foo());
    }

}
chrosciu
  • 304
  • 3
  • 9
0

Java now (starting from 16) supports local interface.

A local interface is a nested interface (§9 (Interfaces)) whose declaration is immediately contained by a block.

And your (slightly corrected because bar implmentation must be public) code compiles just fine.

public class Foo {

    public void foo() {
        interface Bar {           // Bar was supposed to be a local interface...
            void bar();
        }

        new Bar() {               // ... to be anonymously instantiated
            public void bar() {
                System.out.println("Bar!");
            }
        }.bar();
    }
}

Compilation:

~/tmp $ /usr/local/opt/java/bin/javac -version
javac 17
~/tmp $ /usr/local/opt/java/bin/javac Foo.java
wlnirvana
  • 1,811
  • 20
  • 36