2009-05-18 29 views
18

In einem Versuch zu sehen, ob ich etwas von meinem Math-Code, meist Matrix-Zeug, aufräumen kann, versuche ich, einige Java-Generics zu verwenden. Ich habe die folgende Methode:Java Generics und Zahlen

private <T> T[][] zeroMatrix(int row, int col) { 
    T[][] retVal = (T[][])new Object[row][col]; 
    for(int i = row; i < row; i++) { 
     for(int j = col; j < col; j++) { 
      retVal[i][j] = 0; 
     } 
    } 
    return retVal; 
} 

Die Linie retVal [i] [j] = 0 ist derjenige mir Kopfschmerzen verursacht. Das Ziel der Linie ist das Array mit der T Darstellung von 0. Ich habe versucht, zu initialisieren, alle möglichen Dinge mit ihm zu tun: (T in der Klasse definiert ist als T Nummer erstreckt)

retVal[i][j] = (T)0; 
retVal[i][j] = new T(0); 

Die einzige Sache, die funktioniert, ist

retVal[i][j] = (T)new Object(0); 

Welches ist nicht was ich will.

Ist das möglich? Gibt es einen einfacheren Weg, um eine NxM-Matrix irgendeiner Art von Nummer (einschließlich möglicherweise BigDecimal) darzustellen, oder stehe ich fest?

+1

Ich habe nicht viel zu dieser Diskussion hinzufügen, aber danke für die Bereitstellung einer Frage, die so große Antworten provoziert. Ich bin nicht glücklich mit der Implementierung von Generika von Java, aber ich verstehe sie jetzt besser. –

Antwort

1

Ye olde (Referenz) Arrays spielen nicht gut mit Generika. In diesem Fall sind Arrays wahrscheinlich auch ineffizient. Sie erstellen ein Array von Arrays, so dass unnötige Richtungs- und Begrenzungsüberprüfungen erforderlich sind. Besser, eine Klasse Matrix<T> zu machen. Sie können auch der Matrix einen Verweis auf eine Instanz T hinzufügen, die eine Null darstellt.

+1

Das ist eigentlich Code aus meiner Matrix Klasse. –

+0

Ich empfehle die Verwendung von Number [] oder List als "Array" -Typ. java.util bringt sich gerade in ein bisschen Chaos, indem es mit gefälschten generischen Arrays geschrieben wird. Sie benötigen eine Methode wie ('private Number [] [] zeroMatrix (int row, int col, Nummer null)' oder 'private Liste zeroMatrix (int row, int col, T zero)'). –

1

Generika und Arrays stimmen nicht sehr gut überein. Das Erstellen eines generischen Arrays ist nicht zulässig, da es nicht typsicher wäre. Es stammt aus der Tatsache, dass, wenn Sub ein Untertyp von Super ist, Sub [] ein Untertyp von Super [] ist, was bei generischen Typen nicht der Fall ist; Für zwei unterschiedliche Typen Type1 und Type2 ist List weder ein Subtyp noch ein Supertyp von List. (Effektives Java deckt dies in Kapitel 5, Punkt 25 ab).

-1

Die Verwendung von Löschen durch Java zur Implementierung von Generika bedeutet, dass Sie Schwierigkeiten beim Generieren eines generischen Typs haben werden.

Wie wäre es null mit darzustellen 0

retVal[i][j] = null; 

Anschließend können Sie jede Art Sie später auf dem Array wollen zuweisen.

+0

Ich mag diese Lösung nicht, da Null in Java – dfa

2

In Java wird der Typ zur Laufzeit gelöscht. Sie müssen also ein anderes Argument übergeben, um den Typ zur Laufzeit zu erhalten.

Das könnte entweder der Wert sein, mit dem die Arrays initialisiert werden, oder die zu verwendende Klasse.

Wenn Sie die Klasse übergeben möchten, müssen Sie eine Map of class to value erstellen, um für jeden Typ einen Nullwert zu speichern.

Sie können dann java.util.Arrays.fill verwenden das Array zu füllen:

private static HashMap<Class<?>, Object> ZEROS = new HashMap<Class<?>,Object>(); 

static { 
    ZEROS.put(Integer.class, Integer.valueOf(0)); 
    ... 
} 

private static <T extends Number> T[][] zeroMatrix (Class<T> type, int rows, int cols) { 
    @SuppressWarnings("unchecked") 
    T[][] matrix = (T[][]) java.lang.reflect.Array.newInstance(type, rows, cols); 
    Object zero = ZEROS.get(type); 

    for (T[] row : matrix) 
     java.util.Arrays.fill(row,zero); 

    return matrix; 
} 

Integer[][] matrix = zeroMatrix (Integer.class, 10, 10); 

Wenn jedoch die Leistung der Ferne ist ein Anliegen wollen Sie nicht für numerischen Code boxed Werte.

Sie wollen wirklich nicht versuchen, Null für Null zu verwenden - es wird die Komplexität aller anderen Pfade in Ihrem Code verdreifachen. Obwohl Sie vielleicht mit einer numerischen Unterstützungsklasse durchkommen, die die Addition und Multiplikation der verschiedenen Boxed-Zahlentypen ermöglicht, ist die Menge an Komplexität, die Sie sparen, sehr gering, verglichen mit der Bereitstellung von zwei oder drei primitiven Matrizen und einigen großen Zahlen. insbesondere, wenn Sie ein Template-System verwenden (zB ant's replace task oder XSLT), um den Quellcode zu generieren.

+0

nicht @Bill zur Kompilierzeit der generische Typ für die Typprüfung verfügbar ist - daher wird es nicht zur Kompilierzeit, sondern nach der Kompilierzeit gelöscht. Der Compiler löscht es aus dem Code, der an die JVM übergeben wird, daher wird es zur Laufzeit gelöscht. –

1

Ich denke, Sie kämpfen einen verlorenen Kampf. Selbst wenn Sie das lösen, wie planen Sie, Addition, Subtraktion usw. zu lösen? Die Zahlenklasse ist keine sehr nützliche Oberklasse und die einzige nützliche Methode ist doubleValue().

Null kann als die Identität zusätzlich oder eine Null in der Multiplikation definiert werden, aber ohne eine generische Definition von Addition oder Multiplikation ist eine generische Definition von Null unwahrscheinlich.

Wenn Sie das wollen, dann ist es vielleicht besser, nur mit BigDecimal für alles zu kleben, aber natürlich wird das mit Leistungseinbußen verbunden sein.

Die andere offensichtliche Option wäre, das Array mit Null-Initialisierung zu belassen und dann Ihren anderen Code so zu ändern, dass null als Null behandelt wird.

3

sollte es Null statt Null sein.

Wenn Sie wirklich wollen, in setzen die äquivalente 0 für Objekt T benötigen Sie eine Fabrik von T. etwas zu bieten:

interface Factory<T> { 
    T getZero();  
} 

und Sie sollten die Methode wie folgt machen:

private <T> T[][] zeroMatrix(int row, int col, Factory<T> factory) { 
    T[][] retVal = (T[][])new Object[row][col]; 
    for(int i = row; i < row; i++) { 
     for(int j = col; j < col; j++) { 
      retVal[i][j] = factory.getZero(); 
     } 
    } 

    return retVal; 
} 

Sie sollten auch geeignete Implementierungen für die Fabrik haben:

class IntegerFactory implements Factory<Integer> { 
    Integer getZero() { 
     return new Integer(0); 
    } 
} 

Nor Normalerweise würden Sie die getMatrix(int row, int column) in der Factory-Implementierung auch setzen, um tatsächlich ein richtiges typisiertes Array zurückzugeben.

+0

Dies bricht offensichtlich zur Laufzeit ab. Sie können ein Objekt [] [] nicht auf Integet [] [] umwandeln. –

+0

Versuchen Sie, Vorlagengrenzen hinzuzufügen, z.

+0

getZero() sollte zwischengespeichert werden oder einfach Integer.valueOf() – dfa

1

Sie müssen berücksichtigen, dass Generika nur zur Kompilierzeit für die Typsicherheitsprüfung verwendet werden. Diese Information ist zur Laufzeit verloren, so dass Sie das automatische Boxen auf retVal [i] [j] = 0 nicht verwenden können; Java kann nicht automatisch die Nummer oder das Objekt eingeben.

Wenn Sie den Wert übergeben, den Sie festlegen möchten, wird es funktionieren. Hier ist eine kurze Probe:

private <T> T[][] fillMatrix(int row, int col, T value) { 
    T[][] retVal = (T[][])new Object[row][col]; 
    for(int i = 0; i < row; i++) { 
     for(int j = 0; j < col; j++) { 
      retVal[i][j] = value; 
     } 
    } 
    return retVal; 
} 

Btw, for (int i = Zeile; i < Reihe; i ++) und für (int j = col; j < col; j ++) wird nie Schleife so gibt es ein anderes Problem mit Ihrem Code.

edit: Sie können das Ergebnis jedoch nicht auf etwas anderes als Object [] [] anwenden, da dies der tatsächliche Array-Typ ist.

+0

Vielleicht ein << für (T [] t1: retVal) für (T t2: t1) t2 = Wert; >> könnte funktionieren. – ATorras

+2

es löst aus: java.lang.ClassCastException: [[Ljava.lang.Object; kann nicht in [[Ljava.lang.Integer; – dfa

+0

+1 Autsch! Sie haben Recht – ATorras

12
<T extends Number> T[][] zeroMatrix(Class<? extends Number> of, int row, int col) { 
    T[][] matrix = (T[][]) java.lang.reflect.Array.newInstance(of, row, col); 
    T zero = (T) of.getConstructor(String.class).newInstance("0"); 
    // not handling exception  

    for (int i = 0; i < row; i++) { 
     for (int j = 0; j < col; 
      matrix[i][j] = zero; 
     } 
    } 

    return matrix; 
} 

Nutzung:

BigInteger[][] bigIntegerMatrix = zeroMatrix(BigInteger.class, 3, 3); 
    Integer[][] integerMatrix = zeroMatrix(Integer.class, 3, 3); 
    Float[][] floatMatrix = zeroMatrix(Float.class, 3, 3); 
    String[][] error = zeroMatrix(String.class, 3, 3); // <--- compile time error 
    System.out.println(Arrays.deepToString(bigIntegerMatrix)); 
    System.out.println(Arrays.deepToString(integerMatrix)); 
    System.out.println(Arrays.deepToString(floatMatrix)); 

EDIT

eine generische Matrix:

public static <T> T[][] fillMatrix(Object fill, int row, int col) { 
    T[][] matrix = (T[][]) Array.newInstance(fill.getClass(), row, col); 

    for (int i = 0; i < row; i++) { 
     for (int j = 0; j < col; j++) { 
      matrix[i][j] = (T) fill; 
     } 
    } 

    return matrix; 
}  

Integer[][] zeroMatrix = fillMatrix(0, 3, 3); // a zero-filled 3x3 matrix 
String[][] stringMatrix = fillMatrix("B", 2, 2); // a B-filled 2x2 matrix 
+0

das klingt richtig, ich weiß Generika nicht gut genug, um sicher zu sagen, aber wenn es nur richtig funktioniert, dann muss es korrekt sein. :) –

+0

Umm .... warten Sie eine Minute. Ich habe kein Problem mit der Korrektheit, aber ich habe ein Problem mit der Effizienz. Sie sollten die newInstance ("0") am Anfang, z. T zero = .... newInstance ("0"), und matrix [i] [j] = Null in der Schleife zuweisen. –

+0

(nur problematisch, wenn T eine veränderbare Klasse ist und ich denke nicht, dass das sind) –

8

Arrays und Generics do nicht gut zusammen spielen:

"Arrays sind kovariant, was bedeutet, dass ein Array von Supertype-Referenzen ein Supertyp eines Arrays von Subtypreferenzen ist.Das heißt, Object[] ist ein übergeordneter Typ von String[] und ein String-Array kann Object[] durch eine Referenzvariable vom Typ zugegriffen werden „

siehe Java Generics FAQ.

1

Wenn Sie wirklich Generika verwenden möchten, könnten Sie so etwas tun

private <T extends Number> T[][] zeroMatrix(int row, int col, Class<T> clazz) throws InstantiationException, IllegalAccessException, 
     IllegalArgumentException, InvocationTargetException 
{ 
    T[][] retVal = (T[][]) Array.newInstance(clazz, new int[] { row, col }); 
    for (int i = 0; i < row; i++) 
    { 
     for (int j = 0; j < col; j++) 
     { 
      Constructor<T> c = clazz.getDeclaredConstructors()[0]; 
      retVal[i][j] = c.newInstance("0"); 
     } 
    } 

    return retVal; 
} 

Beispiel:

zeroMatrix(12, 12, Integer.class);