Java: Comment puis-je remplacer une méthode d'une classe dynamiquement (classe est finalement PAS dans le classpath)?
Comment dois-je appeler une méthode d'une classe dynamiquement + conditionnellement?
(Classe est finalement pas dans le 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:
- Connaître cette classe est disponible (à l'exécution),
- Si ce n'est pas le cas, sauter l'ensemble de la chose.
- Comment puis-je gérer à remplacement d'une méthode de dynamique-classe chargée
(ainsi, la création d'un anonyme intérieur de la sous-classe de celle-ci)?
Exemple de Code
public static void setNimbusUI(final IMethod<UIDefaults> method)
throws UnsupportedLookAndFeelException {
//NimbusLookAndFeel may be now available
UIManager.setLookAndFeel(new NimbusLookAndFeel() {
@Override
public UIDefaults getDefaults() {
UIDefaults ret = super.getDefaults();
method.perform(ret);
return ret;
}
});
}
MODIFIER:
Maintenant, j'ai édité mon code, comme cela a été suggéré, pour intercepter NoClassDefFoundError
utilisation de try-catch. Il échoue. Je ne sais pas, si c'est OpenJDK est la faute de. Je reçois InvocationTargetException
, causée par NoClassDefFoundError
. Drôle, que je ne peut pas attraper InvocationTargetException
: Elle a jeté de toute façon.
EDIT2::
Provoquer trouvé: j'ai été habillage SwingUtilities.invokeAndWait(...)
autour de la méthode éprouvée, et que très invokeAndWait
appel jette NoClassDefFoundError
lors du chargement Nimbus échoue.
EDIT3::
Quelqu'un peut-il préciser où NoClassDefFoundError
peut se produire à tout? Parce qu'il semble que c'est toujours l'appel de la méthode, pas la méthode qui utilise le non-existants de la classe.
NoClassDefFoundError
se produit pendant le chargement d'une classe qui était présent dans compile-time classpath, mais il est absent lors de l'exécution de chemin de classe. ClassNotFoundException
se produit pendant le chargement d'une classe qui est absente dans les runtime classpath, mais il n'a pas besoin d'être présents dans compile-time classpath.@BalusC
: Ma question dans le EDIT3 a été lié à une EDIT2: Est est quelque part précisé que NoClassDefFoundError
se produit, disons, au cours de la construction d'une classe, qui tente d'appeler un autre non-existants de la classe, ou qu'il ne se produit que lorsque le méthode qui appelle à la non-existants de la classe est appelée, " Plus générale: où est spécifié quand une certaine classe est chargé?OriginalL'auteur java.is.for.desktop | 2010-08-07
Vous devez vous connecter pour publier un commentaire.
Connaître cette classe est disponible (à l'exécution)
Mettre de l'utilisation dans un bloc try ...
Si ce n'est pas le cas, passez le tout
... et de laisser le bloc catch vide (odeur de code?!).
Comment puis-je gérer de remplacer une méthode de dynamique-classe chargée
Suffit de le faire et assurez-vous que le moment de la compilation de la dépendance est satisfait. Vous mélangez tout jusqu'ici. Primordial prend place au moment de la compilation, tandis que la classe de chargement est un runtime chose.
Pour être complet, chaque classe que vous écrivez est chargé dynamiquement par l'environnement d'exécution lorsque cela est nécessaire.
De sorte que votre code peut ressembler à:
OpenJDK 6
, donc je me suis confondu par des erreurs de compilation.... bien sûr, la meilleure solution me permettrait de compiler le cas échéant, les non-existants de la classe en option.
En supposant que ce code a été compilé, il ne sera pas jeter un
ClassNotFoundException
mais unNoClassDefFoundError
siNimbusLookAndFeel
n'est pas là au moment de l'exécution.Thivent: Droite! "exception ClassNotFoundException n'est jamais jeté dans le corps de correspondant instruction try". Mais êtes-vous sûr que
NoClassDefFoundError
est jeté à l'intérieur de cette méthode? (Et non, par exemple, à la classe de ctor, statique ctor, ... ou ce que jamais...)merci de souligner ce point, il fixe
OriginalL'auteur whiskeysierra
Utilisation BCEL pour générer votre sous-classe dynamique à la volée.
http://jakarta.apache.org/bcel/manual.html
Je ne le pense pas. La chose la plus proche construit dans le JDK est la classe de Proxy, mais qui ne sera pas une sous-classe afin de ne pas les aider. En fin de compte, une nouvelle Classe a à être créé à la volée, ce qui signifie générer du bytecode de la classe, et de le charger par l'intermédiaire d'un chargeur de classe. Java ne fournit pas beaucoup d'options pour ce sans qu'un tiers de la bibliothèque comme BCEL.
Oui, les procurations sont malheureusement là que pour la mise en œuvre des interfaces.
OriginalL'auteur Kirk Woll
Le code ci-dessous devrait résoudre votre problème. Le
Main
classe simule votre classe principale. ClasseA
simule la classe de base que vous voulez étendre (et vous n'avez pas le contrôle). ClasseB
est la classe dérivée de la classeA
. InterfaceC
simule le "pointeur de fonction" fonctionnalité que Java n'a pas. Nous allons voir le premier code...Ce qui suit est la classe
A
, la classe que vous souhaitez étendre, mais n'ont aucun contrôle de:Ce qui suit est la classe
B
, le mannequin de la classe dérivée. Notez que, puisqu'il s'étendA
, il doit importerpackageA.A
et de la classeA
doit être disponible au moment de la compilation de la classeB
. Un constructeur avec paramètre C est essentielle, mais la mise en œuvre de l'interfaceC
est facultatif. SiB
implémenteC
, vous bénéficiez de la commodité de l'appel à la méthode(s) sur une instance deB
directement (sans réflexion). DansB.doSomething()
, appelantsuper.doSomething()
est facultative et dépend de si vous voulez, mais en l'appelantc.doSomething()
est essentiel (expliqué ci-dessous):Suivantes est la plus délicate de l'interface
C
. Il suffit de mettre tous les méthodes que vous souhaitez remplacer dans cette interface:Ce qui suit est la classe principale:
Pourquoi faut-il compiler et exécuter?
Vous pouvez voir que dans le
Main
de classe, seulementpackageC.C
est importé, et il n'y a aucune référence àpackageA.A
oupackageB.B
. Si il y a de tout, le chargeur de classes, une exception sera levée sur les plates-formes qui n'ont paspackageA.A
lorsqu'il essaie de charger l'un d'entre eux.Comment faire?
Dans la première
Class.forName()
, il vérifie si la classeA
est disponible sur la plate-forme. Si c'est, demandez à la classe loader pour charger la classeB
, et de stocker leClass
objet dansclassB
. Sinon,ClassNotFoundException
est jeté parClass.forName()
, et le programme va sans classeA
.Puis, si
classB
n'est pas null, obtenir le constructeur de la classeB
qui accepte un seulC
objet comme paramètre. Stocker lesConstructor
objet dansconstructorB
.Puis, si
constructorB
n'est pas null, invoquerconstructorB.newInstance()
pour créer unB
objet. Puisqu'il y a uneC
objet en tant que paramètre, vous pouvez créer une classe anonyme qui implémente l'interfaceC
et de passer une instance comme la valeur du paramètre. C'est juste comme ce que vous faites lorsque vous créez un anonymeMouseListener
.(En fait, vous n'avez pas à séparer les ci-dessus
try
blocs. Il est fait de sorte à rendre clair ce que je suis en train de faire.)Si vous faites
B
implémenteC
, vous pouvez lancer leB
objet comme unC
référence à ce moment, et puis vous pouvez appeler les méthodes de remplacement directement (sans réflexion).Que si la classe
A
n'est pas une "aucun paramètre de constructeur"?Il suffit d'ajouter les paramètres requis pour la classe
B
, commepublic B(int extraParam, C c)
, et d'appelersuper(extraParam)
au lieu desuper()
. Lors de la création de laconstructorB
, ajoutez le paramètre supplémentaire, commeclassB.getConstructor(Integer.TYPE, C.class)
.Ce qui se passe à la Chaîne
s
et Chaînet
?t
est utilisé par la classe anonyme directement. LorsqueobjectB.doSomething("World");
est appelé,"World"
est les
fourni à la classeB
. Depuissuper
ne peut pas être utilisé dans la classe anonyme (pour des raisons évidentes), tout le code qui utilisentsuper
sont placés dans la classeB
.Que faire si je veux me référer
super
plusieurs fois?Il suffit d'écrire un modèle dans
B.doSomething()
comme ceci:Bien sûr, vous devez modifier l'interface
C
incluredoSomethingAfter1()
etdoSomethingAfter2()
.Comment compiler et exécuter le code?
Dans la première manche, la classe
packageB.B
n'est pas compilé (depuisMain.java
n'a pas de référence). Dans la deuxième manche, la classe est compilés de manière explicite, et donc vous obtenez le résultat que vous attendiez.Pour vous aider à côté d'ma solution à votre problème, voici un lien vers la bonne manière de définir l'Apparence "Nimbus":
Nimbus Regarder et se Sentir
OriginalL'auteur Siu Ching Pong -Asuka Kenji-
Vous pouvez utiliser Classe classe de le faire.
I. E.:
La phrase ci-dessus va lancer une ClassNotFoundException si elles ne figurent pas sur l'actuel chemin de classe. Si l'exception n'est pas jeté, alors vous pouvez utiliser
newInstance()
méthode dansc
pour créer des objets de votre.package.YourClass classe. Si vous avez besoin d'appeler un constructeur, vous pouvez utilisergetConstructors
méthode pour l'obtenir et l'utiliser pour créer une nouvelle instance.Je vois... Bonne question. Laissez-moi réfléchir...
OriginalL'auteur Pablo Santa Cruz
Gre, ne pouvez-vous pas mettre la classe que vous souhaitez prolonger dans le temps de compilation du chemin de classe, écrire votre sous-classe, comme d'habitude, et au moment de l'exécution, de manière explicite à déclencher le chargement de la sous-classe, et traiter toutes les exceptions lancées par l'éditeur de liens qui indique que la super-classe est manquant?
OriginalL'auteur meriton