290

In Java, you can define multiple top level classes in a single file, providing that at most one of these is public (see JLS §7.6). See below for example.

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

  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?

e.g., PublicClass.java:

package com.example.multiple;

public class PublicClass {
    PrivateImpl impl = new PrivateImpl();
}

class PrivateImpl {
    int implementationData;
}
Victor Dombrovsky
  • 2,955
  • 3
  • 21
  • 33
Michael Brewer-Davis
  • 14,018
  • 5
  • 37
  • 49

9 Answers9

156

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 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 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 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.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
Laurence Gonsalves
  • 137,896
  • 35
  • 246
  • 299
  • Does Maven do anything to make compilation reliable? – Aleksandr Dubinsky Jan 18 '19 at 10:28
  • @Laurence You said _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?_ but even if I create a Baz.java, also then compiling Foo.java fails. It still does not know what source file to look in. – Kushal Kumar Sep 29 '21 at 08:44
  • 1
    @KushalKumar javac will search the sourcepath, which defaults to the user classpath (which in turn defaults to the current directory). If your source is not rooted from a directory in your classpath, you'll need to specify your sourcepath. – Laurence Gonsalves Oct 01 '21 at 18:28
  • @LaurenceGonsalves Thanks. After reading a few times, I can understand it well. Except for when you say what the actual limitation is, *"don't refer to a top-level class from another file unless it has the same name as the file it's in or you're also referring to a class that's in that same file that's named the same thing as the file"* Could you please also explain this line? – Kushal Kumar Oct 04 '21 at 18:18
  • @KushalKumar The point there is that the actual limitation is hard to follow. I reworded it slightly to try and make it more clear, but it really is a weird rule, and that's why in practice people use a variation that is easier to understand, but more strict. – Laurence Gonsalves Oct 04 '21 at 20:10
  • 1
    @KushalKumar javac starts with the set of source files you specifically ask it to compile. When you refer to a class not in those sources it checks the classpath for compiled classes, and the source path *for source files with a matching name*. If it finds the latter, it adds it to the set of source files it's working on. So if you refer to Baz, and it's in the same file as Bar, javac will find Baz only because it pulled in Bar.java due to the reference to Bar. – Laurence Gonsalves Oct 04 '21 at 20:25
  • @tapavko You say "this part is not true", but then quote a question, so I'm not sure what you think is "not true". The rest of what you said is correct, but none of it conflicts with what was said in the answer. I suggest you try creating the `Foo.java` and `Bar.java` as described in the answer, and then try compiling with the command `javac -d . Foo.java` without first compiling `Bar.java`. `javac` won't know where to find `Baz`, because there is no source or class file with that name. – Laurence Gonsalves Jun 01 '22 at 18:17
130

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 :)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 87
    I'm not the downvoter, but the fact this answer is something that could be called "normative" (ie. "you should" instead of "in fact ... however ...") is the most likely reason for it to get a downvote I think. It doesn't actually answer any of the questions. Like raising an irrelevant exception instead of returning anything / raising an exception that has information about actual facts instead of opinions. – n611x007 Jul 06 '12 at 06:19
  • 6
    I found what I think is a minor exception to @JonSkeet 's suggestion to use a nested type (which I would otherwise agree with): if the main class is generic and the type parameter is the second class, the second class can't be nested. And if the two classes are tightly coupled (like PublicClass and PrivateImpl in the question), I think it's a good idea to put PrivateImpl as a top-level class in the same file. – jfritz42 Nov 16 '12 at 18:13
  • I don't mean to resurrect anything too old but for someone stumbling upon this I want to clarify that its not always bad to have multiple classes within one file. This, as it seems to me, to be the core basis of component based programming. Similar to how Unity works when you call GetComponent, FindComponent, etc. – Boomer Rogers May 22 '14 at 10:47
  • 6
    @BoomerRogers: No, this is definitely *not* the "core basis of component based programming". If you're programming against a component, why would you care how the source code is organized? (Personally I prefer dependency *injection* rather than the service locator pattern, but that's a different matter.) Separate API and source code organization in your mind - they're very different things. – Jon Skeet May 22 '14 at 10:50
  • @JonSkeet Even if it is bad practice, the fact is that java specs allow it. Also, this the topic regulary occurs in OCJP tests. – bvdb Apr 06 '15 at 13:47
  • 3
    @JonSkeet Let me rephrase: Your "answer" is a personal irrelevant opinion. (i.e. answers like "mess" and "i doubt it" have little value.) So, your post does not answer any of the 2 posed questions. Check the answer of polygenelubricants, and you will see that he manages to answer both. – bvdb Apr 06 '15 at 15:08
  • @bvdb: No, polygenelubricants doesn't answer the question of what it's called to specify multiple top-level classes in a single file. He described that `PrivateImpl` is a non-private top-level class, but that's not the same as describing the "technique" of having multiple top-level classes in a single file, which is what I believe the OP was asking. Interesting point about the 1.2.2-1.4 issue, although unfortunately the link is no longer useful. Of course, you're free to downvote (as I assume you did) but I don't see how the OCJP part is relevant at all. – Jon Skeet Apr 06 '15 at 15:48
  • 1
    @bvdb: (And there are *lots* of things which are bad practice but allowed by the spec. I would urge people not to write `public int[] foo(int x)[] { return new int[5][5]; }` as well, even though that's valid.) – Jon Skeet Apr 06 '15 at 15:50
  • @JonSkeet I'm not saying that specs should describe good/bad practices. But the question was not : "can you tell me what is good practice?" If you strip your answer from all personal opinions there's just nothing left. And one reason why your opinion does not matter is the fact that even if we agree never to write this code, other people will (e.g. in an OCJP test). So, that's why we need a term for it, and why the whole matter of good/bad practice is out of scope of this question. So yes, that's why I (and many other people) downvoted. – bvdb Apr 07 '15 at 07:44
  • @bvdb: So what do you believe the term *is* for this? (I don't believe we need a term for everything one could possibly have a term for.) I will clarify my answer to explain that I don't believe there *is* an official term for it. – Jon Skeet Apr 07 '15 at 08:07
  • @JonSkeet There's a simple rule: if you don't know the answer to a question, then don't reply to it. – bvdb Apr 07 '15 at 10:28
  • @bvdb: "There is no such term" is an answer, and I still believe that to be the case. However, I think this conversation is unlikely to produce any further positive effects; this is my last comment in it. – Jon Skeet Apr 07 '15 at 10:33
  • In C# multiple types per file are allowed. This is not inherently messy at all. Do it when it makes sense and otherwise not. It makes sense for example for many trivial enum types or DTOs, JSON classes, generated code, etc. – usr Mar 21 '18 at 14:28
25

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.

Community
  • 1
  • 1
polygenelubricants
  • 376,812
  • 128
  • 561
  • 623
  • 1
    My only issue with this is that you can have a `non-public top level class` be the only class in a file, so it doesn't address the multiplicity. – Michael Brewer-Davis Feb 25 '10 at 19:47
  • I understand the concern, but as you can see this is a terminology that others have historically used. If I have to make up my own term, I'll probably call it `secondary top level types`. – polygenelubricants Feb 25 '10 at 20:48
  • this is really a link-only answer, and now that link to sun forum doesn't work, there isn't much left – eis Feb 16 '21 at 14:23
19

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");
    }
}
Tom
  • 16,842
  • 17
  • 45
  • 54
Denis
  • 390
  • 3
  • 14
5

Just FYI, if you are using Java 11+, there is an exception to this rule: if you run your java file directly (without compilation). In this mode, there is no restriction on a single public class per file. However, the class with the main method must be the first one in the file.

ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155
4

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 )

Pete Kirkham
  • 48,893
  • 5
  • 92
  • 171
2

Yes you can, with public static members on an outer public class, like so:

public class Foo {

    public static class FooChild extends Z {
        String foo;
    }

    public static class ZeeChild extends Z {

    }

}

and another file that references the above:

public class Bar {

    public static void main(String[] args){

        Foo.FooChild f = new Foo.FooChild();
        System.out.println(f);

    }
}

put them in the same folder. Compile with:

javac folder/*.java

and run with:

 java -cp folder Bar
Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • 2
    That example does not answer the question. You are giving an example of nested static classes, which is not the same as having two top level classes defined in the same file. – Pedro García Medina Nov 27 '19 at 23:29
0

According to Effective Java 2nd edition (Item 13):

"If a package-private top-level class (or interface) is used by only one class, consider making the top-level class a private nested class of the sole class that uses it (Item 22). This reduces its accessibility from all the classes in its package to the one class that uses it. But it is far more important to reduce the accessibility of a gratuitously public class than a package-private top-level class: ... "

The nested class may be static or non-static based on whether the member class needs access to the enclosing instance (Item 22).

rezzy
  • 41
  • 2
  • 9
-14

No. You can't. But it is very possible in Scala:

class Foo {val bar = "a"}
class Bar {val foo = "b"}
DexterHaxxor
  • 901
  • 6
  • 15