10
votes

Java: Comment puis-je remplacer une méthode d'une classe de manière dynamique (la classe n'est finalement pas dans la classe de classe)?

Comment appeler une méthode d'une classe dynamique + conditionnelle?
(la classe est finalement pas in classpath)

disons, j'ai besoin de la classe nimbuslookandfeel , mais sur certains systèmes, il n'est pas disponible (c'est-à-dire openjdk-6 ).

Donc, je dois être capable de:

  • Apprenez à le savoir que la classe est disponible (au moment de l'exécution),
  • Si ce n'est pas le cas, Skip Toute la chose.
  • Comment réussir à remplacer une méthode d'une classe chargée de manière dynamique
    (créant ainsi une sous-classe interne anonyme de celui-ci)?

    Exemple de code xxx

    EDIT :
    Maintenant, j'ai édité mon code, comme il a été suggéré, d'intercepter noclassduffoundEfferror en utilisant try-capture. Il échoue. Je ne sais pas, si c'est la faute de l'openjdk. I Obtenir InvocationTargetException , causé par NOCLASSDEffondError . Drôle, que je ne peut pas attraper InvocationTargetException : C'est jeté quand même de toute façon.

    EDIT2: :
    Cause trouvée: j'étais enveloppé oscillabilité.invokeandwait (...) autour de la méthode testée, et que très invokeantwait appelant noclassdefferror lors du chargement du nimbus échoue.

    Edit3: :
    Quelqu'un peut-il s'il vous plaît clarifier noclassduffounderror peut survenir du tout? Parce qu'il semble que c'est toujours la méthode d'appel, pas la méthode réelle qui utilise la classe non existante.


2 commentaires

NOCLASSDEFFUTRORROR Pendant le chargement d'une classe présente dans CompiléTime ClassPath, mais est absent dans la classe de classe d'exécution. CLASSNOTFOUNDEXCEPTION Pendant le chargement d'une classe qui est absent dans la classe d'exécution, mais n'a pas besoin d'être présent dans Compilétime ClassPath.


@Ballusc : Ma question dans Edit3 était liée à EDIT2 : Est quelque part spécifié que NOCLASSDEffondError se produit, disons , pendant la construction de classe qui tente d'appeler une autre classe non existante ou ne se produit que lorsque la méthode qui appelle la classe non existante est appelée, ... plus général: où est spécifié quand une certaine classe est chargé?


5 Réponses :


0
votes

1 commentaires

Je pensais à cela, mais comment puis-je gérer une méthode d'une classe chargée de manière dynamique (créant ainsi une sous-classe interne anonyme de celle-ci)?



1
votes

Utilisez BCEC pour générer votre sous-classe dynamique à la volée.

http://jakarta.apache.org/bcel/manual.html


3 commentaires

Bonne idée, mais n'est-ce pas possible d'une manière plus simple?


Je ne le pense pas. La chose la plus proche intégrée à la JDK est la classe de proxy, mais cela ne sera pas une sous-classe, alors ne vous aide pas. En fin de compte, une nouvelle classe doit être créée à la volée, ce qui signifie générer le bytecode de cette classe et le charger via un chargeur de classe. Java ne fournit pas de nombreuses options à cela sans une bibliothèque tierce partie telle que BCEL.


Oui, les procurations ne sont trompées trompeuses que là-bas pour la mise en œuvre d'interfaces.



-1
votes

ERM, vous ne pouvez pas mettre la classe que vous souhaitez étendre dans le chemin de la classe d'heure de compilée, écrivez votre sous-classe comme d'habitude et à l'exécution, déclencher explicitement la chargement de la sous-classe et gérer toute exception projetée par la liaison qui indique que La superclasse est manquante?


0 commentaires

4
votes

Apprenez à le savoir que la classe est disponible (au moment de l'exécution)
Mettez l'utilisation dans un bloc d'essai ...

Si ce n'est pas le cas, sautez le tout
... et laissez le bloc de prise vide (odeur de code?!).

Comment réussir à remplacer une méthode d'une classe chargée de manière dynamique
Faites-le simplement et assurez-vous que la dépendance du temps compilé est satisfaite. Vous mélangez des choses ici. Le dépassement a lieu au moment de la compilation, tandis que la classe Chargement est une chose d'exécution.

Pour la complétude, chaque classe que vous écrivez est chargée de manière dynamique par l'environnement d'exécution lorsqu'il est requis.

Donc, votre code peut ressembler à: xxx


8 commentaires

Bonne idée! Je n'étais pas seulement exécutant, mais aussi compilation Le code sous OpenJDK 6 , donc je suis confus par des erreurs de compilation-temps.


... Bien sûr, la solution optimale me permettrait de compiler éventuellement la classe non existante éventuellement.


En supposant que ce code a été compilé, il ne lancera pas un classnotfoundexception mais un NOCLASSDEffondError si nimbuslookandfeel n'est pas là au moment de l'exécution.


@ Pascal thivent : Droite! "Exception ClassNotFoundException n'est jamais lancée dans le corps de la déclaration d'essai correspondante". Mais êtes-vous sûr que noclassduffounderror est lancé à l'intérieur de cette méthode ? (Pas, par exemple, à la classe 'CTOR, Statique CTOR, ... ou quelle que soit ...)


@ java.is.for.desktop avez-vous essayé?


Oui, comme mentionné dans la question modifiée, quelque chose semble avoir tort avec OpenJDK-6: Je reçois une invocationTargetException (causée par NOCLASSDEFFUDEError), qui, étrangement, ne peuvent pas être attrapées.


Ah, n'a pas vu la nouvelle partie dans la question. La stacktrace indique-t-elle où vient l'exception?


Maintenant, j'ai trouvé la cause, ajoutée à la question ( Edit2 ).



1
votes

Le code suivant doit résoudre votre problème. La classe principale simule votre classe principale. Classe A simule la classe de base que vous souhaitez étendre (et vous n'avez aucun contrôle). Classe B est la classe dérivée de la classe A . Interface C simule la fonctionnalité "Fonction Pointer" que Java n'a pas. Voyons le code d'abord ...

Ce qui suit est la classe A , la classe que vous souhaitez prolonger, mais sans contrôle de: xxx P> Ce qui suit est la classe B , la classe dérivée mannequin. Notez que, puisqu'il étend a , il doit importer packagea.a et classe A doit être disponible à l'heure de la compilation de la classe B . Un constructeur avec paramètre C est essentiel, mais la mise en œuvre d'interface C est facultative. Si B implémente C , vous gagnez la commodité pour appeler la méthode (s) sur une instance de B directement (sans réfléchir). Dans b.dosomething () , appeler super.dosomething () est facultatif et dépend de ce que vous voulez, mais appelez c.dosomething () est essentiel (expliqué ci-dessous): xxx

Ce qui suit est l'interface délicate c . Il suffit de mettre toutes les méthodes que vous souhaitez remplacer dans cette interface: xxx

Ce qui suit est la classe principale: xxx

Pourquoi cela compile et courait-il?
Vous pouvez voir que dans la classe principale , uniquement packagec.c est importé et il n'y a aucune référence à packagea.a ou ou ou ou paquetb.b . S'il y en a un, le chargeur de classe lancera une exception sur les plates-formes qui n'ont pas packagea.a quand il essaie de charger l'un d'entre eux.

Comment Ça marche?
Dans la première class.forname () , il vérifie si la classe A est disponible sur la plate-forme. Si c'est le cas, demandez au chargeur de classe de charger la classe B et stockez l'objet de classe résultant dans classb . Sinon, classnotfoundexception est lancé par class.forname () , et le programme va sans classe a .

alors, Si Classb n'est pas NULL, obtenez le constructeur de la classe B qui accepte un seul objet c comme paramètre. Stockez le Constructeur Objet dans Constructorb .

Puis, si constructurb n'est pas null, invoquer constructorb.newinstance () pour créer un objet B . Comme il existe un objet C comme paramètre, vous pouvez créer une classe anonyme qui implémente l'interface C et transmet l'instance comme valeur de paramètre. Ceci est juste comme ce que vous faites lorsque vous créez un Mouselistener anonyme .

(En fait, vous n'avez pas à séparer le Essayer ci-dessus Blocks . Il est fait afin de préciser ce que je fais.)

Si vous avez formulé B implémente C , vous pouvez lancer le B objet sous forme de référence C en ce moment, puis vous pouvez appeler les méthodes remplacées directement (sans réflexion).

Que si la classe A n'a pas de "no paramètre constructeur"?
Ajoutez simplement les paramètres requis à la classe B , comme Public B (int extraparam, c) et appelez super (extraparam) au lieu de < Code> super () . Lors de la création du constructorb , ajoutez également le paramètre supplémentaire, comme classb.getconstructor (INTEGER.TYPE, C.Class) .

arrive à la chaîne s et chaîne t ?

t est utilisé par la classe anonyme directement. Lorsque objetb.dosomething ("world"); est appelé, "world" est le s fourni à la classe b . Étant donné que Super ne peut pas être utilisé dans la classe anonyme (pour des raisons évidentes), tout le code qui utilise Super est placé dans la classe B .

Et si je veux faire référence à Super plusieurs fois?

Il suffit d'écrire un modèle dans b.dosomething () comme ceci: xxx

Bien sûr, vous devez modifier l'interface C Pour inclure DOSOMODOASHTER1 () et DOSOMODOASTER2 () .

Comment compiler et exécuter le code?
xxx

dans la première exécution, la classe packageb.b n'est pas compilée (puisque main.java n'a pas toute référence à elle). Dans la deuxième exécution, la classe est explicitement compilée et vous obtenez ainsi le résultat que vous attendez.

Pour vous aider à définir ma solution à votre problème, voici un lien vers la bonne façon de la bonne façon de définir le look Nimbus et ressentez:

Nimbus Look and Sentence


0 commentaires