Java: Multiple class declarations in one file

Solution 1:

javac doesn't actively prohibit this, but it does have a limitation that pretty much means that you'd never want to refer to a top-level class from another file unless it has the same name as the file it's in.

Suppose you have two files, Foo.java and Bar.java.

Foo.java contains:

  • public class Foo

Bar.java contains:

  • public class Bar
  • class Baz

Let's also say that all of the classes are in the same package (and the files are in the same directory).

What happens if Foo.java refers to Baz but not Bar and we try to compile Foo.java? The compilation fails with an error like this:

Foo.java:2: cannot find symbol
symbol  : class Baz
location: class Foo
  private Baz baz;
          ^
1 error

This makes sense if you think about it. If Foo.java refers to Baz, but there is no Baz.java (or Baz.class), how can javac know what source file to look in?

If you instead tell javac to compile Foo.java and Bar.java at the same time, or if you had previously compiled Bar.java (leaving the Baz.class where javac can find it), or even if Foo.java happens to refer to Bar in addition to Baz, then this error goes away. This makes your build process feel very unreliable and flaky, however.

Because the actual limitation, which is more like "don't refer to a top-level class from another file unless it either has the same name as the file it's in or you're also referring to another class that's named the same thing as that file that's also in that file" is kind of hard to follow, people usually go with the much more straightforward (though stricter) convention of just putting one top-level class in each file. This is also better if you ever change your mind about whether a class should be public or not.

Newer versions of javac can also produce a warning in this situation with -Xlint:all:

auxiliary class Baz in ./Bar.java should not be accessed from outside its own source file

Sometimes there really is a good reason why everybody does something in a particular way.

Solution 2:

My suggested name for this technique (including multiple top-level classes in a single source file) would be "mess". Seriously, I don't think it's a good idea - I'd use a nested type in this situation instead. Then it's still easy to predict which source file it's in. I don't believe there's an official term for this approach though.

As for whether this actually changes between implementations - I highly doubt it, but if you avoid doing it in the first place, you'll never need to care :)

Solution 3:

I believe you simply call PrivateImpl what it is: a non-public top-level class. You can also declare non-public top-level interfaces as well.

e.g., elsewhere on SO: Non-public top-level class vs static nested class

As for changes in behavior between versions, there was this discussion about something that "worked perfectly" in 1.2.2. but stopped working in 1.4 in sun's forum: Java Compiler - unable to declare a non public top level classes in a file.

Solution 4:

You can have as many classes as you wish like this

public class Fun {
    Fun() {
        System.out.println("Fun constructor");
    }
    void fun() {
        System.out.println("Fun mathod");
    }
    public static void main(String[] args) {
        Fun fu = new Fun();
        fu.fun();
        Fen fe = new Fen();
        fe.fen();
        Fin fi = new Fin();
        fi.fin();
        Fon fo = new Fon();
        fo.fon();
        Fan fa = new Fan();
        fa.fan();
        fa.run();
    }
}

class Fen {
    Fen() {
        System.out.println("fen construuctor");

    }
    void fen() {
        System.out.println("Fen method");
    }
}

class Fin {
    void fin() {
        System.out.println("Fin method");
    }
}

class Fon {
    void fon() {
        System.out.println("Fon method");
    } 
}

class Fan {
    void fan() {
        System.out.println("Fan method");
    }
    public void run() {
        System.out.println("run");
    }
}

Solution 5:

1.Is there a tidy name for this technique (analogous to inner, nested, anonymous)?

Multi-class single-file demo.

2.The JLS says the system may enforce the restriction that these secondary classes can't be referred to by code in other compilation units of the package, e.g., they can't be treated as package-private. Is that really something that changes between Java implementations?

I'm not aware of any which don't have that restriction - all the file based compilers won't allow you to refer to source code classes in files which are not named the same as the class name. ( if you compile a multi-class file, and put the classes on the class path, then any compiler will find them )