4 votes

Combien de points de la définition d'un élément de première classe les fonctions de Java satisfont-elles ?

_Structure et interprétation des programmes informatiques_ donne les conditions suivantes pour qu'un élément d'un langage de programmation soit considéré comme étant de première classe :

  1. Ils peuvent être nommés par des variables.

  2. Ils peuvent être transmis comme arguments aux procédures.

  3. Ils peuvent être renvoyés en tant que résultats de procédures.

  4. Ils peuvent être inclus dans des structures de données

Combien d'entre elles les fonctions de Java satisfont-elles ? En cas d'ambiguïté, par exemple " Le fait de placer une fonction dans un objet compte-t-il pour le numéro 4 ? "Dans ce cas, veuillez le mentionner dans votre réponse.

4voto

kaya3 Points 17037

Ce point est discutable. Il est certainement possible d'écrire quelque chose comme cela :

Function<Integer, Integer> times2 = x -> x * 2;

S'agit-il de nommer une fonction par une variable ? Oui et non. La variable times2 contient une référence à une "fonction", donc la "fonction" est une valeur et est nommée par la variable. D'autre part, la spécification du langage Java dit ceci :

Une variable d'un type d'interface peut contenir une référence nulle ou une référence à n'importe quelle instance de n'importe quelle classe qui implémente l'interface.

En d'autres termes, la variable ne contient pas directement une référence à une fonction ; elle contient une référence à un objet doté d'une fonction apply méthode. Le JLS ne permet pas de contenir "une fonction" ou "une référence à une fonction", mais seulement "une référence à une instance" qui satisfait à l'interface. L'instance possède une méthode nommée apply qui exécute la fonction lorsqu'elle est appelée.

Peut-on dire que l'objet es la fonction ? Oui et non, c'est une question d'interprétation. Mais je pencherais pour le "non", car le nom de la méthode apply est spécifique à la Function et si vous écrivez la même fonction lambda dans un contexte différent, l'"objet fonction" peut avoir une méthode portant un nom différent. Si la même fonction lambda est écrite dans deux contextes différents, s'agit-il de la "même fonction" ? Je dirais que oui, mais il est clair qu'il ne s'agit pas de la même fonction. objets parce qu'ils ont des noms de méthodes différents. J'en conclus donc que s'il s'agit de la même fonction mais pas du même objet, alors la fonction et l'objet sont des choses différentes.


Il convient de noter que Réponse de @rzwitserloot défendre l'interprétation contraire est également correcte La question de l'interprétation n'est pas discutable, sauf dans la mesure où ils affirment que leur interprétation est la seule possible. Comme je l'ai dit, c'est discutable. Je pense que le fait que nous soyons en train d'en débattre est une preuve suffisante de cette proposition...

2voto

rzwitserloot Points 434
  1. Ils peuvent être nommés par des variables.

Oui, clairement :

Runnable r = () -> System.out.println("Hello");
Runnable r2 = System.out::println;

J'ai des fonctions / références de méthodes, et je peux avoir des variables nommées qui s'y réfèrent.

AMBIGUITÉ :

Ces variables pourraient également pointer sur des objets réels à la place :

r = new Runnable() {
    public void run() {
        System.out.println("Goodbye!");
    }
};

Contrairement à l'exemple de la syntaxe de la fonction, l'exemple ci-dessus signifie que r c'est vraiment pointer un objet défini avec des caractéristiques de type objet définies. Il existe un objet réel en mémoire et la spécification java lang le garantit.

Mais cela n'a pas d'importance. Dans un langage comme, disons, javascript ou python, je peux assigner n'importe quoi à une variable (les variables ne sont pas typées dans ces langages). Cela ne rend pas toutes les variables booléennes simplement parce que je peux assigner true y false à n'importe quelle variable.

De même, en Java, pour invoquer la fonction, par exemple pour imprimer Hello donné : Runnable r = () -> System.out.println("Hello"); Je devrais écrire r.run() et non r() .

C'est un débat sur la syntaxe, ce n'est pas quelque chose de fondamental. En python, r() prendra la variable r Le logiciel de gestion de l'interface utilisateur, le déréférence (suit le pointeur), essaie d'interpréter ce qu'il trouve comme une fonction, et l'exécute, sans passer d'arguments. S'il n'est pas possible de l'interpréter comme une fonction, une erreur se produit.

Java n'est pas différent. r.run() prendra la variable r Le programme d'exécution, lui, le déréférence (suit le pointeur), essaie d'interpréter ce qu'il y trouve comme une fonction de type Runnable, et l'exécute, sans passer d'arguments. Si ce qu'il trouve n'est pas un Runnable (par exemple, il s'agit de null ), une erreur se produit.

Vous voyez ? c'est identique. Essayer de définir les choses en fonction de la manière dont d'autres langues font les choses conduirait à tort à dire que ce qui précède est en quelque sorte du "sucre syntaxique" qui "ne compte pas", ce qui est une conclusion erronée.

  1. Ils peuvent être transmis comme arguments aux procédures.

Oui, sans ambiguïté.

public void runTwice(Runnable r) {
    r.run();
    r.run();
}

Runnable r = () -> System.out.println("Hello");
runTwice(r);
runTwice(System.out::println);
runTwice(() -> System.out.println("Goodbye!"));

Je transmets ces fonctions à Java.

  1. Ils peuvent être renvoyés en tant que résultats de procédures.

Oui, sans ambiguïté :

public Runnable printMeTwice(String text) {
    return () -> {
        System.out.println(text);
        System.out.println(text);
    };
}

Runnable r = printMeTwice();
r.run();
  1. Ils peuvent être inclus dans des structures de données

Oui, sans ambiguïté :

class Animal {
    String name;
    Function<Food, Excrement> eat;
}

Animal animal = new Animal();
animal.eat = food -> return food.extractNutrients();

En cas d'ambiguïté.

En utilisant la formulation spécifique telle que vous l'avez collée, il n'y a aucune ambiguïté, ce qui est assez intéressant, étant donné que cette question a déjà reçu des réponses qui ont soit mal compris, soit utilisé des interprétations particulièrement bizarres de ces mots.

L'une des bizarreries de Java par rapport à la plupart des autres langages est que, dans la plupart des langages, si une variable r contient une fonction, pour faire le travail de "déréférencer le pointeur et d'exécuter la fonction que vous trouvez lorsque vous le faites", vous écririez r() . En Java, on ne fait pas cela ; on fait plutôt r.run() ou r.apply(t) en Java, les fonctions ont des types nommés, ce qui n'est pas le cas dans la plupart des langages. Par exemple, en Java, je peux avoir le concept d'une "opération de calculatrice sur des nombres entiers uniquement" :

IntCalcOp plusButton = (a, b) -> a + b;

et je peux avoir le concept d'une "fonction de comparaison de nombres entiers" :

IntComparator highestIsEarlier = (a, b) -> b - a;

et ces deux choses sont fondamentalement différentes. Je ne peux pas passer une variable de type IntCalcOp à une méthode qui nécessite un IntComparator, même si les signatures sont entièrement identiques sur le plan fonctionnel - prennent tous deux 2 ints et renvoient un int.

Notez qu'il ne s'agit pas d'une situation intrinsèquement inférieure ; en fait, elle est probablement intrinsèquement supérieure. Il s'agit d'une différence d'opinion dans la conception du langage. Pour montrer les avantages du typage nominal et de l'absence totale de conversion implicite, voici deux définitions d'objets très simples qui sont fonctionnellement identiques :

interface Camera {
    void shoot(Person p);
}

interface Gun {
    void shoot(Person p);
}

Dans certains langages comme python et javascript, si je vous donne un pistolet et que vous attendez une caméra, vous tuerez quelqu'un. En Java, cela ne peut pas arriver.

Le fait de dire "mais c'est du sucre de syntaxe" ne change rien aux 4 extraits ci-dessus qui montrent clairement que Les fonctions de Java sont des éléments de premier ordre, conformément à la définition de la structure et de l'interprétation des programmes d'ordinateur. .

Je ne pense pas que ce livre ait une petite * avec une note de bas de page qui précise "Le sucre syntaxique ne compte pas". Si vous souhaitez opérer avec cette mise en garde, il nous faut d'abord définir ce que signifie le sucre syntaxique.

Par exemple, la spécification du langage Java ne prévoit pas dire que ceux-ci sont transformés en littéraux anonymes de classe intérieure. Les implémentations ne le font même plus (d'autres réponses le mentionnent et sont incorrectes à cet égard).

Un compilateur C peut fonctionnent en émettant d'abord un code assembleur, puis en le faisant passer par un assembleur pour produire un exécutable. Cela signifie-t-il que tout le code C n'est que du "sucre syntaxique" ? Si c'est le cas, le C n'a même pas de boucles.

Ce type de raisonnement est intéressant, mais pour déterminer ce qu'un langage peut faire, je pense qu'il est complètement inutile. Essentiellement, tous les langages de programmation sont à 100 % du sucre syntaxique si l'on creuse suffisamment.

1voto

Turing85 Points 5992

En Java, les fonctions n'ont pas de type défini. Nous pouvons écrire des choses comme Objects::nonNull mais il s'agit en fait d'un sucre syntaxique pour l'expression ou (foo) -> Objects.nonNull(foo) qui produira une instance d'une interface fonctionnelle (voir JLS, §15.13 y §15.27 respectivement). Les méthodes sont donc toujours enveloppées dans une implémentation d'interface (éventuellement anonyme).

Si l'on compare avec des langages comme le C++, où chaque fonction a un type défini Nous constatons que les lambdas Java ne satisfont à aucun de ces critères.

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