2009-09-01 6 views
5

Meine Frage ist unheimlich ähnlich wie "Writing a generic class to handle built-in types" einschließlich inspiriert von der Tatsache, an einer Klasse zu arbeiten, um Operationen auf Matrizen zu behandeln. Diese Frage wurde zwar mit C# gestellt und auf einen Artikel unter Generic Operators verwiesen.Wie behandelt man Zahlen in generischer Form?

Ich verstehe es nicht. Java Nummer keine Add-Methode, so dass Sie eine Methode, wie haben:

public Number myAdd(Number a, Number b){ 
    return a.add(b); 
} 

So, wie Sie einen Fall behandeln kann, wo man mehrere Arten von Zahlen in Java zu handhaben zu können wollen?

Antwort

4

Das grundlegende Problem ist mit dem Java-Typ-System, das sehr primitiv ist.

Da es in Java keine gesiegelte Gruppe von Typen gibt (und es ist auch nicht möglich, dass Java die Typen wie Haskell ableitet), gibt es keine Möglichkeit, eine allgemeine Zahl + Nummer -> Nummer ohne Tricks zu machen.

Für Primitive (und solche Objekte wie Integer, die ihnen automatisch zugeordnet werden können) Typen Promotion und die Operation + ist Teil der Sprache. (Und das ist ein tatsächlicher Teil des Problems: Was soll die Zahl a + Zahl b zurückgeben, wo a und b verschiedene Typen sind?)

Wenn Sie dieses Verhalten wirklich wollen, müssen Sie Ihre eigene benutzerdefinierte Klasse finden (oder erstellen), die entweder Reflektion oder eine Reihe (von Checks und) Umwandlungen und dergleichen verwendet. Selbst wenn Sie Generika verwenden (denken Sie daran, dass Generika vom Typ gelöscht werden), muss das Casting durchgeführt werden.

Ich kann mir vorstellen, dass diese Probleme Teil des Grundes sind, warum Number so langweilig ist wie es ist.

+0

"was sollte Zahl a + Zahl b zurück, wo a und b sind von verschiedenen Typen" ist ziemlich _all_ des Problems. :) –

-1

Persönlich verwende ich BigDecimals für fast alles (aber das liegt hauptsächlich daran, dass ich mit Währungswerten arbeite). Sie behandeln alle numerischen Werte jeder Größe. Aus diesem Grund sind sie meiner Meinung nach ein generischer Wert und könnten in Ihrem hypothetischen Beispiel anstelle der abstrakten Klasse Number verwendet werden. Alles kann in ein BigDecimal umgewandelt werden, warum nicht verwenden?

public BigDecimal myAdd(BigDecimal a, BigDecimal b) { 
    return a.add(b); 
} 

EDIT: Um Adresse BigBrothers Kommentar unten, immer die doublevalue() Methode verwenden, um Ihre eigene generische Methode zu erstellen. Das einzige Problem dabei ist, dass Sie Präzision in einigen seltenen Fällen verlieren können, wenn jemand in einem BigDecimal vorbei und es ist größer als ein Double.maxValue

public Number myAdd(Number a, Number b) { 
    return new BigDecimal(a.doubleValue() + b.doubleValue()); 
} 

A BigDecimal eine Zahl ist, so Rückkehr eines nicht ist Folge.

+0

Während ich sehe was du sagst, habe ich auf etwas eleganteres gehofft, denke ich. –

3

Ich verstehe es nicht. Java Nummer nicht eine Add-Methode hat ...

Angenommen, java.lang.Number ein add Verfahren oder Methoden hätten, wie würden Sie ihre Signatur definieren? Wie würdest du seine Semantik definieren? Wie würden Sie mit "Mischmodus" -Arithmetik umgehen?

Während es zweifellos möglich ist, diese Fragen zu beantworten und eine API zu entwerfen, ist das Ergebnis wahrscheinlich schwierig, richtig zu verwenden. Darüber hinaus ist es sehr ungewöhnlich, dass eine Anwendung eine arithmetische "Repräsentationsanalyse" durchführen muss. Normalerweise möchten/brauchen Sie explizite Kontrolle über die Art, wie Arithmetik ausgeführt wird und Konvertierungen stattfinden. (Die Java primitive Art Förderung Regeln sind schon schwierig genug für die Menschen ihre Köpfe herum zu erhalten !!)

Alles in allem denke ich, dass Sun hat getan, um uns einen guten Service von nicht versuchen Arithmetik zu unterstützen, die Nummer API.

3

Wie gut soll das Ergebnis sein? Wenn die Antwort „gut genug, vor allem“, dann sollte dies sufficent sein:

public Number myAdd(Number a, Number b){ 
    return a.doubleValue() + b.doubleValue(); 
} 

Aber wenn Sie wollen etwas, das, sagen wir, die Förderung Semantik von Java Primitive übereinstimmt, sind Sie wahrscheinlich schreiben gehen zu müssen, es selbst. Und dann müssen Sie herausfinden, was die Regeln für alle Kombinationen von "nicht standardmäßigen" Number Implementationen sind, einschließlich BigDecimal, , AtomicLong, alles in org.apache.commons.lang.mutable, und irgendeine zufällige Implementierung, die jemand beschließen könnte, nächsten Dienstag zu schreiben .

Es ist nicht klar, was das Richtige in den meisten dieser Fälle zu tun ist - das Konvertieren von allem in BigDecimal zum Beispiel ist keine Option, wenn eines der Argumente Apache Commons Fraction.ONE_THIRD ist; und außerdem stellt die allgemeine Umwandlung die gleichen Probleme dar wie die allgemeine Addition. Aber mit einer add() Methode auf Number würde jede Number Implementierung erforderlich, um alle diese Fälle zu behandeln - und das ist wahrscheinlich, warum es nicht da ist.

0

Sie können aus den angegebenen Gründen keine zwei Zahlen hinzufügen, aber Sie können Nummern desselben Typs hinzufügen, und das Ergebnis wird auch vom selben Typ sein. Sie können generische Arithmetik in Java erstellen, mit so etwas wie folgt aus:

interface Arithmetics<T> { 
    T add(T val1, T val2); 
} 

class IntegerArithmetics implements Arithmetics<Integer> { 
    Integer add(Integer val1, Integer val2) { return val1 + val2; } 
} 

//similarly DoubleArithmetics, BigIntegerArithmetics, ... 

Generic Java Math Bibliothek tut genau das für Sie.

1

Eine Möglichkeit, eine generische Add-Methode zu implementieren, besteht darin, das Argument der linken Hand auf den Rückgabetyp schließen zu lassen.

package mixins; 

import java.math.BigDecimal; 

public class Numbers { 

    public static boolean isZ(Number n) { 
     return n instanceof Integer || n instanceof Long || n instanceof Short || n instanceof Byte; 
    } 

    public static boolean isR(Number n) { 
     return n instanceof Double || n instanceof Float; 
    } 

    public static BigDecimal add(BigDecimal a, Number b) { 
     if (b instanceof BigDecimal) { 
      return a.add((BigDecimal) b); 
     } else if (isZ(b)) { 
      return a.add(new BigDecimal(b.longValue())); 
     } else if (isR(b)) { 
      return a.add(new BigDecimal(b.doubleValue())); 
     } 
     throw new IllegalArgumentException("No valid big decimal translation for " + b.getClass()); 
    } 

    public static Integer add(Integer a, Number b) { 
     return a + b.intValue(); 
    } 

    public static Long add(Long a, Number b) { 
     return a + b.longValue(); 
    } 

    public static Float add(Float a, Number b) { 
     return a + b.floatValue(); 
    } 

    public static Double add(Double a, Number b) { 
     return a + b.doubleValue(); 
    } 
} 

Wenn dies als statische Methoden implementiert ist, können Sie statische Importe verwenden.

import static mixins.Numbers.*; 

public class Example { 

    public static void main(String[] args) { 
     BigDecimal fortytwo = new BigDecimal(42); 
     BigDecimal fiftyfive = add(fortytwo, 13); 
     System.out.println(fiftyfive); 
    } 

} 
Verwandte Themen