web-dev-qa-db-fra.com

Java: Déclarations de classe multiples dans un fichier

En Java, vous pouvez définir plusieurs classes de niveau supérieur dans un seul fichier, à condition qu'au moins une d'entre elles soit publique (voir JLS §7.6 ). Voir ci-dessous par exemple.

  1. Existe-t-il un nom propre pour cette technique (analogue à inner, nested, anonymous)?

  2. JLS indique que le système peut appliquer la restriction selon laquelle ces classes secondaires ne peuvent pas être referred to by code in other compilation units of the package, par exemple, elles ne peuvent pas être traitées comme des paquets privés. Est-ce vraiment quelque chose qui change entre les implémentations Java?

par exemple, PublicClass.Java:

package com.example.multiple;

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

class PrivateImpl {
    int implementationData;
}
218
Michael Brewer-Davis

Le nom suggéré pour cette technique (y compris plusieurs classes de niveau supérieur dans un seul fichier source) serait "désordre". Sérieusement, je ne pense pas que ce soit une bonne idée - j'utiliserais plutôt un type imbriqué dans cette situation. Ensuite, il est encore facile de prédire dans quel fichier source il se trouve. Je ne crois cependant pas qu'il existe un terme officiel pour cette approche.

Quant à savoir si cela change réellement entre les implémentations - j'en doute fortement, mais si vous évitez de le faire en premier lieu, vous n'aurez jamais besoin de vous en soucier :)

116
Jon Skeet

javac n'interdit pas activement cela, mais il comporte une limitation qui signifie que vous ne voudrez jamais faire référence à une classe de niveau supérieur à partir d'un autre fichier, sauf si elle porte le même nom que le fichier dans lequel elle se trouve.

Supposons que vous ayez deux fichiers, Foo.Java et Bar.Java.

Foo.Java contient:

  • classe publique Foo

Bar.Java contient:

  • bar public
  • classe baz

Disons également que toutes les classes sont dans le même package (et les fichiers sont dans le même répertoire).

Que se passe-t-il si Foo.Java fait référence à Baz mais pas à Bar et que nous essayons de compiler Foo.java? La compilation échoue avec une erreur comme celle-ci:

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

Cela a du sens si vous y réfléchissez. Si Foo.Java fait référence à Baz, mais qu’il n’existe pas de Baz.Java (ou Baz.class), comment javac peut-il savoir dans quel fichier source se placer?

Si vous demandez à javac de compiler simultanément Foo.Java et Bar.Java, ou même si vous aviez déjà compilé Bar.Java (en laissant la classe Baz. où javac peut le trouver), cette erreur disparaît. Cela rend cependant votre processus de construction très peu fiable et floconneux.

Parce que la limitation réelle, qui s'apparente davantage à "ne fait pas référence à une classe de niveau supérieur à partir d'un autre fichier, sauf si elle porte le même nom que le fichier dans lequel elle se trouve ou si vous faites également référence à une classe qui se trouve dans le même fichier nommé La même chose que le fichier "est un peu difficile à suivre, les gens optent généralement pour la convention beaucoup plus simple (bien que plus stricte) de simplement mettre une classe de niveau supérieur dans chaque fichier. C'est également mieux si vous changez d'avis quant à savoir si une classe doit être publique ou non.

Parfois, il y a vraiment une bonne raison pour que tout le monde fasse quelque chose d'une manière particulière.

122
Laurence Gonsalves

Je crois que vous appelez simplement PrivateImpl ce que c'est: un non-public top-level class. Vous pouvez également déclarer non-public top-level interfaces.

par exemple, ailleurs sur SO: Classe de niveau supérieur non publique vs classe imbriquée statique

En ce qui concerne les changements de comportement entre les versions, il y avait cette discussion sur quelque chose qui "fonctionnait parfaitement" dans 1.2.2. mais a cessé de fonctionner dans la version 1.4 du forum de Sun: Java Compiler - incapable de déclarer des classes de niveau supérieur non publiques dans un fichier .

22
polygenelubricants

Vous pouvez avoir autant de cours que vous le souhaitez

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");
    }
}
6
Denis

1.Y at-il un nom propre pour cette technique (analogue à interne, imbriqué, anonyme)?

Démo mono-fichier multi-classes.

2.Le JLS indique que le système peut imposer la restriction selon laquelle ces classes secondaires ne peuvent pas être référencées par code dans d'autres unités de compilation du package, par exemple, elles ne peuvent pas être traitées comme des packages privés. Est-ce vraiment quelque chose qui change entre les implémentations Java?

Je ne suis au courant d'aucun de ceux qui n'ont pas cette restriction - tous les compilateurs basés sur des fichiers ne vous permettront pas de faire référence aux classes de code source dans des fichiers qui ne portent pas le même nom que le nom de la classe. (Si vous compilez un fichier multi-classes et que vous les mettez sur le chemin de la classe, tous les compilateurs les trouveront)

4
Pete Kirkham

Selon Effective Java 2nd edition (élément 13):

"Si une classe (ou interface) de niveau supérieur privée du paquet est utilisée par une seule classe, envisagez de faire de la classe de niveau supérieur une classe imbriquée privée De la classe unique qui l'utilise (élément 22). Cela réduit son accessibilité de toutes les classes de son paquet à la seule classe Qui l'utilise. Mais il est bien plus important de réduire l'accessibilité D'une classe gratuite à un public que d'un paquet top-private classe de niveau: ... "

La classe imbriquée peut être statique ou non statique selon que la classe membre a besoin d'accéder à l'instance englobante (élément 22).

1
rezzy

Oui, vous pouvez, avec des membres statiques publics sur une classe publique externe, comme ceci:

public class Foo {

    public static class FooChild extends Z {
        String foo;
    }

    public static class ZeeChild extends Z {

    }

}

et un autre fichier faisant référence à ce qui précède:

public class Bar {

    public static void main(String[] args){

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

    }
}

mettez-les dans le même dossier. Compiler avec:

javac folder/*.Java

et courir avec:

 Java -cp folder Bar
0
Alexander Mills