2009-04-25 9 views
6

Ich spiele mit Code-Katas herum und versuche, Java-Generika gleichzeitig besser zu verstehen. Ich habe diese kleine Methode, die Arrays ausgibt, wie ich sie gerne sehe, und ich habe ein paar Hilfsmethoden, die ein Array von 'Dingen' und einem Index akzeptieren und das Array der 'Dinge' über oder unter dem Index zurückgeben (es ist ein binärer Suchalgorithmus).Können Sie ein int-Array an eine generische Methode in Java übergeben?

Zwei Fragen,

# 1 Kann ich die Besetzung zu T in splitBottom und splitTop vermeiden? Es fühlt sich nicht richtig an, oder ich gehe in die falsche Richtung (sag mir nicht, ob ich Python benutzen soll ...;))

# 2 Muss ich separate Methoden schreiben, um mit Primitiven umzugehen Arrays oder gibt es eine bessere Lösung?

public class Util { 

    public static <T> void print(T[] array) { 
     System.out.print("{"); 
     for (int i = 0; i < array.length; i++) { 
      System.out.print(array[i]); 
      if (i < array.length - 1) { 
       System.out.print(", "); 
      } 
     } 
     System.out.println("}"); 
    } 

    public static <T> T[] splitTop(T[] array, int index) { 
     Object[] result = new Object[array.length - index - 1]; 
     System.arraycopy(array, index + 1, result, 0, result.length); 
     return (T[]) result; 
    } 

    public static <T> T[] splitBottom(T[] array, int index) { 
     Object[] result = new Object[index]; 
     System.arraycopy(array, 0, result, 0, index); 
     return (T[]) result; 
    } 

    public static void main(String[] args) { 

     Integer[] integerArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
     print(integerArray); 
     print(splitBottom(integerArray, 3)); 
     print(splitTop(integerArray, 3)); 

     String[] stringArray = {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"}; 
     print(stringArray); 
     print(splitBottom(stringArray, 3)); 
     print(splitTop(stringArray, 3)); 

     int[] intArray = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
     // ??? 
    } 
} 

Antwort

9

Generika behandeln Primitive nicht konsistent. Dies liegt daran, dass Generika nicht wie Vorlagen in C++ sind, sondern nur eine Kompilierungszeit für eine einzelne Klasse.

Wenn generische kompiliert werden, erhalten Sie im obigen Beispiel Object [] als den implementierenden Typ. Wie int [] und byte [] usw., erweitern Sie nicht Object [] Sie können sie nicht interchangeable verwenden, auch wenn der Code identisch wäre (wieder Generics sind keine Vorlagen)

Die einzige Klasse int [] und Object [] share ist Objekt. Sie können die obigen Methoden Objekt als den Typ schreiben (siehe System.arraycopy, Array.getLength, Array.get, Array.set)

1

Frage 1: Casting-Arrays funktioniert nicht wie erwartet. Ein String ist ein Objekt, aber ein String-Array ist kein Objekt-Array.

Versuchen verwenden so etwas wie:

public static <T> T[] splitTop(T[] array, int index) { 
    T[] result = Arrays.copyOfRange(array, index + 1, array.length); 
    return result; 
} 

Frage 2: Für Arrays von Primitiven meine Funktion offensichtlich funktioniert auch nicht. Es gibt keine elegante Lösung dafür - siehe zum Beispiel die Array-Bibliothek, die mehrere Kopien von im Wesentlichen derselben Methode für jeden primitiven Array-Typ aufweist.

+0

Das können Sie nicht mit Generics machen - das führt zu einem inkompatiblen Typfehler. – hbw

+0

@htw können Sie mehr erklären? – blank

3

1 Kann ich die Umwandlung in T in splitBottom und splitTop vermeiden? Es spielt keine fühlen, oder ich werde diese die falsche Art und Weise (sagen Sie mir nicht Python oder etwas .. zu verwenden;))

Nicht nur können Sie es nicht vermeiden, aber du solltest es nicht tun. In Java sind verschiedene Arten von Arrays tatsächlich unterschiedliche Laufzeittypen. Ein Array, das als Object[] erstellt wurde, kann keiner Variablen von AnythingElse [] zugewiesen werden. Die Besetzung wird nicht sofort fehlschlagen, da in Generika der Typ T gelöscht wird, aber später wird eine ClassCastException ausgelöst, wenn der Code versucht, sie als Something [] zu verwenden, wie Sie es versprochen haben, aber nicht.

Die Lösung besteht darin, entweder die Methoden Arrays.copyOf... in Java 6 und höher zu verwenden oder, wenn Sie eine frühere Version von Java verwenden, Reflection zu verwenden, um den richtigen Array-Typ zu erstellen. Beispiel:

T [] Ergebnis = (T []) Array.newInstance (array.getClass().getComponentType(), Größe);

2 Muss ich separate Methoden schreiben, um mit primitiven Arrays zu arbeiten, oder ist dort eine bessere Lösung?

Es ist wahrscheinlich am besten, separate Methoden zu schreiben. In Java sind Arrays primitiver Typen vollständig von Arrays von Referenztypen getrennt. und es gibt keine gute Möglichkeit, mit beiden zu arbeiten.

Es ist möglich, Reflection zu verwenden, um beide gleichzeitig zu behandeln. Reflection verfügt über Array.get() und Array.set() Methoden, die auf primitiven Arrays und Referenzarrays gleichermaßen funktionieren. Sie verlieren jedoch die Typensicherheit, indem Sie dies tun, da der einzige Supertyp von primitiven Arrays und Referenzarrays Object ist.

1

Java erlaubt keine typsichere Erstellung generischer Arrays. Verwenden Sie stattdessen einen generischen Sequenztyp (z. B. java.util.List).

Hier ist, wie ich Ihr Testprogramm schreiben würde, eine generische Container-Klasse mit fj.data.Stream:

import fj.data.Stream; 
import static fj.data.Stream.range; 

// ... 

public int[] intArray(Stream<Integer> s) { 
    return s.toArray(Integer.class).array() 
} 

public static void main(String[] args) { 
    Stream<Integer> integerStream = range(1, 10); 
    print(intArray(integerStream)); 
    print(intArray(integerStream.take(3))); 
    print(intArray(integerStream.drop(3))); 

    // ... 
} 
0

Sie haben zwei Probleme mit dem, was Sie versuchen zu erreichen.

Zuerst versuchen Sie, primitive Typen zu verwenden, die nicht tatsächlich von Object erben. Dies wird Dinge vermasseln. Wenn Sie dies wirklich tun müssen, verwenden Sie explizit Integer anstelle von int usw.

Das zweite und größere Problem ist, dass Java-Generika Typ löschen haben. Dies bedeutet, dass Sie zur Laufzeit nicht auf den Typ des Generics verweisen können. Dies wurde gemacht, um Ihnen zu erlauben, generisch-unterstützenden und nicht-generisch-unterstützenden Code zu mischen und endete (IMHO) eine Hauptquelle von Kopfschmerzen für Java-Entwickler und ein weiterer Beweis, dass Generika vom ersten Tag an in Java sein sollten. Ich schlage vor Lesen Sie den Teil in der tutorial darüber, es wird dieses Problem klarer machen.

0

Sie müssen die Primitive wahrscheinlich in entsprechende Sammlungen verpacken.

Ich schlage auch vor, um primitive Sammlungen Trove (http://trove.starlight-systems.com) zu betrachten. Dies steht in keinem Zusammenhang mit Ihrer Generikafrage, kann aber durchaus interessant sein.

Verwandte Themen