37 votes

La duplication de code causée par des types primitifs: Comment éviter la folie?

Dans un de mes projets Java, je suis rongé par la répétition de code, en raison de la façon dont Java poignées (pas) primitives. Après avoir copier manuellement le même changement à quatre endroits différents (int, long, float, double) de nouveau, pour la troisième fois, encore et encore , je suis venu très près (?) pour l'accrochage.

Sous des formes diverses, cette question a été soulevée maintenant et puis sur StackOverflow:

Le consensus semblait converger vers deux solutions possibles:

  • Utiliser une sorte de générateur de code.
  • Que pouvez-vous faire? C est la vie!

Eh bien, la deuxième solution est ce que je fais maintenant et il est en train de devenir dangereux pour ma santé mentale, comme la bien connue technique de torture.

Deux ans ont passé depuis que ces questions ont été posées et Java 7 est arrivé. Je suis, par conséquent, de l'espoir pour une plus facile et/ou plus de solution standard.

  • Java 7 ont tout changement qui pourrait faciliter la souche dans de tels cas? Je ne pouvais pas trouver quoi que ce soit dans le résumé de modification des résumés, mais peut-être il ya quelque obscure nouvelle fonctionnalité quelque part?

  • Tandis que la source de génération de code est une alternative, je préfère une solution soutenue à l'aide de la norme JDK ensemble des fonctionnalités. Bien sûr, à l'aide de cpp ou un autre générateur de code fonctionnerait, mais il ajoute plus dépendances et nécessite des modifications du système de construction.

    Le seul code du système de production de toutes sortes qui semble être pris en charge par le JDK est via les annotations mécanisme. J'imagine un processeur qui permettrait d'étendre le code source comme ceci:

    @Primitives({ "int", "long", "float", "double" })
    @PrimitiveVariable
    int max(@PrimitiveVariable int a, @PrimitiveVariable int b) {
        return (a > b)?a:b;
    }
    

    L'idéal du fichier de sortie contient les quatre demandé des variantes de cette méthode, de préférence associés à des commentaires Javadoc e.t.c. Est-il quelque part un processeur d'annotation pour gérer ce cas? Si non, que faudrait-il faire pour en construire un?

  • Peut-être quelques autres truc qui a surgi récemment?

EDIT:

Une remarque importante: je ne voudrais pas être à l'aide de types primitifs, à moins que j'ai eu raison. Même maintenant, il ya une très réelle de la performance et de l'impact de mémoire par l'utilisation de la boîte de types dans certaines applications.

EDIT 2:

À l'aide de max() comme un exemple permet l'utilisation de l' compareTo() méthode qui est disponible en numérique tous types de boîte. C'est un peu plus compliqué:

int sum(int a, int b) {
    return a + b;
}

Comment pourrait-on aller sur l'appui de cette méthode numérique pour tous en boîte sans l'écrire six ou sept fois?

18voto

Peter Lawrey Points 229686

J'ai tendance à utiliser un "super type" comme long ou double si je veux encore une primitive. La performance est généralement très proche et évite de créer beaucoup de variations. BTW: les registres dans une machine virtuelle Java 64 bits seront tous 64 bits de toute façon.

15voto

Bohemian Points 134107

Pourquoi êtes-vous accroché sur les primitives? Les wrappers sont extrêmement léger et auto-boxing et des génériques fait le reste:

public static <T extends Number & Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) > 0 ? a : b;
}

Tout cela compile et s'exécute correctement:

public static void main(String[] args) {
    int i = max(1, 3);
    long l = max(6,7);
    float f = max(5f, 4f);
    double d = max(2d, 4d);
    byte b = max((byte)1, (byte)2);
    short s = max((short)1, (short)2);
}

Édité

L'OP a demandé au sujet d'un générique, auto-coffret solution pour sum(), et elle est ici.

public static <T extends Number> T sum(T... numbers) throws Exception {
    double total = 0;
    for (Number number : numbers) {
        total += number.doubleValue();
    }
    if (numbers[0] instanceof Float || numbers[0] instanceof Double) {
        return (T) numbers[0].getClass().getConstructor(String.class).newInstance(total + "");
    }
    return (T) numbers[0].getClass().getConstructor(String.class).newInstance((total + "").split("\\.")[0]);
}

C'est un peu boiteux, mais pas aussi boiteux que de faire une grande série de instanceof et de déléguer entièrement tapé méthode. L' instanceof est nécessaire parce que, si tous Numbers ont un String constructeur, Numbers autres qu' Float et Double ne peut analyser un nombre entier (sans virgule), bien que le montant total est un nombre entier, il faut enlever le point décimal de l' Double.toString() avant de l'envoyer dans le constructeur pour ces autres types.

5voto

Stephen C Points 255558

Java 7 ont tout changement qui pourrait soulager la contrainte dans de tels cas?

Pas de.

Est-il quelque part un processeur d'annotation pour gérer ce cas?

Non pas que je suis au courant de.

Si non, que faudrait-il faire pour en construire un?

En temps ou en argent. :-)

Il me semble que cela ressemble à un problème d'espace où il serait difficile de trouver une solution générale qui fonctionne bien ... au-delà de cas triviaux. Classiques de génération de code source ou d'un (textuel) de préprocesseur semble plus prometteur pour moi. (Je ne suis pas un processeur d'Annotation experte cependant).

4voto

Alex D Points 14591

Si l'extraordinaire niveau de verbosité de Java est d'arriver à vous, regardez dans de nouveaux langages de niveau supérieur qui s'exécutent sur la JVM et peut interagir avec Java, comme Clojure, JRuby, Scala, et ainsi de suite. Votre out-of-contrôle de la primitive répétition de devenir un non-problème. Mais les avantages vont bien plus loin que ce que -- il y a toutes sortes de façons dont les langues mentionnées vous permettent de faire plus avec moins détaillée, répétitive, le risque d'erreur de code (par rapport à Java).

Si la performance est un problème, vous pouvez retomber en Java pour les performances sont critiques bits (à l'aide de types primitifs). Mais vous pourriez être surpris de voir combien souvent vous pouvez toujours obtenir un bon niveau de performance dans le langage de niveau plus élevé.

Personnellement, j'utilise les deux JRuby et Clojure; si vous venez de Java/C/C#/C++ arrière-plan, les deux ont le potentiel de changer la façon dont vous pensez de programmation.

3voto

bharal Points 3003

Heh. Pourquoi ne pas faire sournois? Avec réflexion, vous pouvez tirer les annotations pour une méthode (annotations similaires à l'exemple que vous avez posté). Vous pouvez ensuite utiliser la réflexion pour obtenir les noms des membres, et de les mettre dans les types appropriés... Dans un système.out.println déclaration.

Vous devez exécuter cette fois, ou chaque fois que vous modded la classe. La sortie peut ensuite être copié-collé dans. Ce serait sans doute vous faire économiser beaucoup de temps, et ne pas être trop difficile à développer.

Hm ,pour le contenu des méthodes... je veux dire, si tous vos méthodes sont triviales, vous pouvez coder en dur le style (c'est à dire si methodName.equals("max"). imprimer retour a>b:a:b etc. Où methodName est déterminé par réflexion), ou vous pourriez, ummmmm... Hm. J'imagine le contenu peut être facilement copier collé, mais il semble tout simplement plus de travail.

Oh! Whty pas faire un autre annotation appelé " contenu ", de lui donner une valeur de chaîne de la méthode de contenu, d'ajouter que, pour le membre, et maintenant, vous pouvez imprimer le contenu trop.

Au moins, le temps passé à le codage de cette aide, même si à peu près aussi long que de faire le travail pénible, eh bien, il serait plus intéressant, riiiight?

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