méthode de package-privé provoque l'échec de Overloaded compilation - Est-ce une bizarrerie JLS ou bug javac?

StackOverflow https://stackoverflow.com/questions/1852939

Question

Je suis venu à travers une bizarrerie de la JLS, ou un bug JavaC (pas sûr). S'il vous plaît lire ce qui suit et fournir une explication, citant le passage JLS ou Sun BugID, selon le cas.

Supposons que j'ai un projet avec le code dans arrangé trois "modules" -

  1. API - définit l'API cadre - pensez API Servlet
  2. Impl - définit la mise en œuvre de l'API - pensez Tomcat Servlet
  3. App - l'application que j'ai écrit

Voici les classes dans chaque module:

API - MessagePrinter.java

package api;

public class MessagePrinter {

    public void print(String message) {
        System.out.println("MESSAGE: " + message);
    }
}

API - MessageHolder.java (oui, il fait référence à une classe "impl" - plus tard)

package api;

import impl.MessagePrinterInternal;

public class MessageHolder {

    private final String message;

    public MessageHolder(String message) {
         this.message = message;
    }

    public void print(MessagePrinter printer) {
        printer.print(message);
    }

    /**
     * NOTE: Package-Private visibility.
     */ 
    void print(MessagePrinterInternal printer) {
        printer.print(message);    
    }

}

Impl - MessagePrinterInternal.java - Cette classe dépend d'une classe API. Comme son nom l'indique, il est destiné à être utilisé « interne » ailleurs dans mon petit cadre.

package impl;

import api.MessagePrinter;

/**
 * An "internal" class, not meant to be added to your
 * application classpath. Think the Tomcat Servlet API implementation classes.
 */
public class MessagePrinterInternal extends MessagePrinter {

    public void print(String message) {
        System.out.println("INTERNAL: " + message);
    }
}

Enfin, la classe unique dans le module App ... MyApp.java

import api.MessageHolder;
import api.MessagePrinter;

public class MyApp {

    public static void main(String[] args) {
        MessageHolder holder = new MessageHolder("Hope this compiles");
        holder.print(new MessagePrinter());
    }

}

Alors, maintenant, je tente de compiler ma petite application, MyApp.java. Supposons que mes pots API sont exportés par un pot, disent api.jar, et d'être un bon citoyen, je ne referencd que dans mon pot classpath -. Pas la classe Impl dans shiped impl.jar

Maintenant, de toute évidence, il y a une faille dans ma conception-cadre en ce que les classes de l'API ne devrait avoir aucune dépendance à l'égard des cours de mise en œuvre « internes ». Cependant, ce fut une surprise que MyApp.java ne compile pas du tout.

javac -cp api.jar src\MyApp.java
src\MyApp.java:11: cannot access impl.MessagePrinterInternal class file for impl.MessagePrinterInternal not found

    holder.print(new MessagePrinter());
                 ^
      1 error

Le problème est que le compilateur tente de résoudre l'impression de la version () à utiliser, en raison de la surcharge de méthode. Cependant, l'erreur de compilation est quelque peu inattendue, comme l'une des méthodes est paquet-privé, et donc pas visible myApp.

Alors, est-ce un bug javac, ou d'une bizarrerie de la JLS?

Compiler: Sun javac 1.6.0_14

Était-ce utile?

La solution

Il n'y a rien de mal à JLS ou javac. Bien sûr, cela ne compile pas, parce que vos références MessageHolder de classe MessagePrinterInternal qui ne sont pas sur le chemin de classe de compilation si je comprends bien votre explication. Vous devez casser cette référence dans la mise en œuvre, par exemple avec une interface dans votre API.

EDIT 1: Pour plus de précisions: Cela n'a rien à voir avec la méthode de package visible que vous semblez penser. Le problème est que le type MessagePrinterInternal est nécessaire pour la compilation, mais vous ne l'avez pas sur le chemin de classe. Vous ne pouvez pas attendre javac pour compiler le code source quand il n'a pas accès aux classes référencées.

EDIT 2: Je relis à nouveau le code et c'est ce qui semble se produire: Lorsque MyApp est compilé, il tente de charger MessageHolder de classe. Références classe MessageHolder MessagePrinterInternal, il tente de charger aussi que et échoue. Je ne sais pas qui est spécifié dans le JLS, il pourrait aussi dépendre de la machine virtuelle Java. Dans mon expérience avec la machine virtuelle Java de Sun, vous devez avoir au moins toutes les classes statiquement référencés disponibles lorsqu'une classe est chargée; qui comprend les types de champs, quoi que ce soit dans les signatures de méthode, et les interfaces étendues CLASSIFICATION DES mises en œuvre. On pourrait dire que cela est contre-intuitif, mais je répondrais qu'en général il y a très peu que vous faites avec une classe où ces informations sont manquantes: vous ne pouvez pas instancier des objets, vous ne pouvez pas utiliser les métadonnées (l'objet de classe), etc. Avec que les connaissances de base, je dirais que le comportement que vous voyez est prévu.

Autres conseils

Tout d'abord j'attendre les choses dans le paquet api être des interfaces plutôt que des classes (en fonction du nom). Une fois que vous faites cela le problème disparaîtra puisque vous ne pouvez pas avoir accès paquet dans les interfaces.

La prochaine chose est que, autant que je sache, c'est une bizarrerie Java (en ce qu'il ne fait pas ce que vous voulez). Si vous vous débarrasser de la méthode publique et faire le package privé, vous obtiendrez la même chose.

Modification de tout dans le package api pour être interfaces fixeront votre problème et vous donner une séparation plus propre dans votre code.

Je suppose que vous pouvez toujours faire valoir que javac peut être un peu plus intelligent, mais il doit arrêter quelque part. ce n'est pas humain, peut toujours être plus intelligent qu'un compilateur, vous pouvez toujours trouver des exemples qui font sens pour un être humain, mais dumbfound un compilateur.

Je ne sais pas la spécification exacte à ce sujet, et je doute que les auteurs javac ont fait une erreur. mais qui se soucie? pourquoi ne pas mettre toutes les dépendances dans le chemin de classe, même si certains d'entre eux sont superficiels? faire qui fait toujours notre vit beaucoup plus facile.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top