58 votes

Fonctions imbriquées en Java

Existe-t-il des extensions pour le langage de programmation Java qui permettent de créer des fonctions imbriquées ?

Il existe de nombreuses situations où je dois créer des méthodes qui ne sont utilisées qu'une seule fois dans le contexte d'une autre méthode ou d'une boucle for. Jusqu'à présent, je n'ai pas réussi à le faire en Java, même si cela peut être fait facilement en JavaScript.

Par exemple, cela ne peut pas être fait en Java standard :

for(int i = 1; i < 100; i++){
    times(2); // Multiply i by 2 and print i
    times(i); // Square i and then print the result

    public void times(int num){

        i *= num;
        System.out.println(i);
    }
}

65voto

Neal Ehardt Points 1259

Java 8 introduit les lambdas.

java.util.function.BiConsumer<Integer, Integer> times = (i, num) -> {
    i *= num;
    System.out.println(i);
};
for (int i = 1; i < 100; i++) {
    times.accept(i, 2); //multiply i by 2 and print i
    times.accept(i, i); //square i and then print the result
}

El () -> fonctionne sur toute interface qui définit exactement une méthode. Vous pouvez donc l'utiliser avec Runnable mais cela ne fonctionne pas avec List .

BiConsumer est l'une des nombreuses interfaces fonctionnelles fournies par java.util.function .

Il est intéressant de noter que, sous le capot, cela définit une classe anonyme et l'instancie. times est une référence à l'instance.

0 votes

Cela doit être intégré d'une manière ou d'une autre dans la réponse principale, car c'est maintenant la solution idiomatique à mon avis. Il est intéressant de voir comment elle est mise en œuvre avec une déclaration de classe interne.

1 votes

Note : ce programme java ne fait pas la même chose que le programme javascript. Je ne suis pas sûr que c'était intentionnel, mais le programme javascript times accède et modifie i. La sortie est 2, 4, 10, 100. En Java, vous ne pouvez accéder qu'aux variables finales, c'est-à-dire que vous ne pouvez pas les modifier.

0 votes

@FlorianF : Tout à fait exact. Les lambdas ne peuvent pas modifier leur portée. Les variables locales référencées doivent être finales ou "effectivement finales". Plus d'informations ici stackoverflow.com/questions/25055392/

30voto

Jon Skeet Points 692016

La réponse ci-dessous parle de ce qui se rapproche le plus des fonctions imbriquées en Java avant Java 8. Ce n'est pas nécessairement la façon dont je traiterais les mêmes tâches qui pourraient être traitées avec des fonctions imbriquées en JavaScript. Souvent, une méthode d'aide privée fera tout aussi bien l'affaire - peut-être même une méthode d'aide privée type dont vous créez une instance dans la méthode, mais qui est disponible pour toutes les méthodes.

Dans Java 8, bien sûr, il existe des expressions lambda qui constituent une solution beaucoup plus simple.


Ce qui s'en rapproche le plus, c'est une classe interne anonyme. Pour l'instant, Java ne s'approche pas plus des fermetures que cela, même si nous espérons qu'il y aura plus de support dans Java 8.

Les classes internes anonymes ont diverses limitations - elles sont évidemment plutôt verbeuses par rapport à votre exemple JavaScript (ou tout ce qui utilise des lambdas) et leur accès à l'environnement environnant est limité aux variables finales.

Donc pour (horriblement) pervertir votre exemple :

interface Foo {
    void bar(int x);
}

public class Test {
    public static void main(String[] args) {
        // Hack to give us a mutable variable we can
        // change from the closure.
        final int[] mutableWrapper = { 0 };

        Foo times = new Foo() {
            @Override public void bar(int num) {
                mutableWrapper[0] *= num;
                System.out.println(mutableWrapper[0]);
            }
        };

        for (int i = 1; i < 100; i++) {
            mutableWrapper[0] = i;
            times.bar(2);
            i = mutableWrapper[0];

            times.bar(i);
            i = mutableWrapper[0];
        }
    }
}

Sortie :

2
4
10
100

Est-ce le résultat que vous obtenez avec le code JavaScript ?

0 votes

Classe intérieure anonyme ? Vraiment ? Je dirais que la manière idiomatique est d'utiliser simplement une private méthode d'aide...

0 votes

@Matt : Mais comment déclarer cela dans une autre méthode ? Je ne dis pas que je n'utiliserais pas de méthodes d'aide privées - je dis que ce ne sont pas des fonctions imbriquées. Je vais éditer pour parler de cela cependant.

11 votes

Le seul problème des méthodes d'aide privées (par opposition aux fonctions internes) est qu'elles encombrent le code source en rendant les fonctions disponibles en dehors de leur portée prévue.

26voto

deinocheirus Points 570

Je pense que le moyen le plus proche d'avoir des fonctions imbriquées en Java 7 n'est pas d'utiliser une classe interne anonyme ( Réponse de Jon Skeet ), mais en utilisant les classes locales, par ailleurs très rarement utilisées. De cette façon, même l'interface de la classe imbriquée n'est pas visible en dehors de son champ d'application et c'est aussi un peu moins long.

L'exemple de Jon Skeet mis en œuvre avec une classe locale se présenterait comme suit :

public class Test {
    public static void main(String[] args) {
        // Hack to give us a mutable variable we can
        // change from the closure.
        final int[] mutableWrapper = { 0 };

        class Foo {
            public void bar(int num) {
                mutableWrapper[0] *= num;
                System.out.println(mutableWrapper[0]);
            }
        };

        Foo times = new Foo();

        for (int i = 1; i < 100; i++) {
            mutableWrapper[0] = i;
            times.bar(2);
            i = mutableWrapper[0];

            times.bar(i);
            i = mutableWrapper[0];
        }
    }
}

Sortie :

2
4
10
100

0 votes

C'est plus expressif et plus idiomatique, selon l'IMO, pour Java 7.

0 votes

Cette façon de faire s'étend à plusieurs fonctions internes assez facilement en ajoutant simplement plus de méthodes dans la classe interne.

0 votes

Très intelligent d'utiliser la classe interne pour faire cela.

2voto

dma_k Points 3567

Ces méthodes sont parfois appelées fermetures. Jetez un coup d'œil à Groovy - vous le préférerez peut-être à Java. Dans Java 8, il y aura probablement aussi des fermetures (cf. JSR335 y liste différée ).

0 votes

Je suppose qu'il est un peu tard pour que Java 7 ait des fermetures :)

0 votes

Je suis d'accord (coché aquí ) :) Il y avait tellement d'attentes.

0 votes

Que peut-on faire pour résoudre ce problème ? Quelqu'un pourrait probablement écrire un préprocesseur pour Java afin de "simuler" cela, mais cela dépasse légèrement mon niveau de compétence.

1voto

NM Naufaldo Points 86

Pour les méthodes sans argument, vous pouvez créer Runnable objet

private static void methodInsideMethod(){
    Runnable runnable = new Runnable(){
        @Override
        public void run(){
            System.out.println("Execute something");
        }
    };

    for(int i = 0; i < 10; i++){
        runnable.run();
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X