Heim  >  Artikel  >  Java  >  Zusammenfassung von Java Generics (2) – Generics und Arrays

Zusammenfassung von Java Generics (2) – Generics und Arrays

黄舟
黄舟Original
2017-03-22 10:23:431393Durchsuche

Die Beziehung zwischen Arrays und Generika ist immer noch etwas kompliziert. Die direkte Erstellung generischer Arrays ist in Java nicht zulässig. Dieser Artikel analysiert die Gründe und fasst einige Möglichkeiten zum Erstellen generischer Arrays zusammen. Hat einen sehr guten Referenzwert. Werfen wir einen Blick darauf mit dem Editor unten.

Einführung

Im vorherigen Artikel wurde die grundlegende Verwendung von Generika und das Problem der Typlöschung vorgestellt. Schauen wir uns das nun genauer an Die Beziehung zwischen Generika und Arrays. Im Vergleich zur Containerklasse in Java Klassenbibliothek sind Arrays etwas Besonderes, das sich hauptsächlich in drei Aspekten widerspiegelt:

  • Nachdem das Array erstellt wurde, ist die Größe festgelegt, aber die Effizienz höher

  • Das Array kann den spezifischen Typ der darin gespeicherten Elemente verfolgen, und der eingefügte Elementtyp wird zur Kompilierungszeit überprüft

  • Arrays können primitive Typen (int, float usw.) enthalten, aber mit Autoboxing scheinen Containerklassen in der Lage zu sein, primitive Typen zu speichern

Was passiert also, wenn ein Array auf a trifft? generisch? Kann ich ein generisches Array erstellen? Dies ist der Hauptinhalt dieses Artikels.

Weitere zwei Artikel in dieser Reihe:

  • Zusammenfassung von Java Generics (1): Grundlegende Verwendung und Typlöschung

  • Zusammenfassung von Java Generics (3): Verwendung von Wildcard

Generic Array

Wie um ein generisches Array zu erstellen

Wenn es eine Klasse wie folgt gibt:

 class Generic<T> {
 
}

Wenn Sie ein generisches Array erstellen möchten, sollte es so aussehen: Genericc0f559cc8d56b43654fcbe4aa9df7b4a ga = new Genericc0f559cc8d56b43654fcbe4aa9df7b4a[] Allerdings meldet der Code einen Fehler, was bedeutet, dass ein generisches Array nicht direkt erstellt werden kann.

Was ist, wenn Sie ein generisches Array verwenden möchten? Eine Lösung besteht darin, ArrayList zu verwenden, wie zum Beispiel im folgenden Beispiel:

public class ListOfGenerics<T> {
 private List<T> array = new ArrayList<T>();
 public void add(T item) { array.add(item); }
 public T get(int index) { return array.get(index); }
}

Wie erstelle ich ein echtes generisches Array? Wir können nicht direkt erstellen, aber wir können Referenzen von generischen Arrays definieren. Beispiel:

public class ArrayOfGenericReference {
 static Generic<Integer>[] gia;
}

gia ist ein Verweis auf ein generisches Array, und dieser Code kann kompiliert werden. Wir können jedoch kein Array dieses genauen Typs erstellen, d festgelegt, wenn das Array erstellt wird. Die von new Genericc0f559cc8d56b43654fcbe4aa9df7b4a[]

oben auskommentierte Codezeile:
public class ArrayOfGeneric {
 static final int SIZE = 100;
 static Generic<Integer>[] gia;
 @SuppressWarnings("unchecked")
 public static void main(String[] args) {
 // Compiles; produces ClassCastException:
 //! gia = (Generic<Integer>[])new Object[SIZE];
 // Runtime type is the raw (erased) type:
 gia = (Generic<Integer>[])new Generic[SIZE];
 System.out.println(gia.getClass().getSimpleName());
 gia[0] = new Generic<Integer>();
 //! gia[1] = new Object(); // Compile-time error
 // Discovers type mismatch at compile time:
 //! gia[2] = new Generic<Double>();
 Generic<Integer> g = gia[0];
 }
} /*输出:
Generic[]
*///:~
, das Array ist ein Objektarray, wenn es erstellt wird, und bei der Transformation wird ein Fehler gemeldet. Die einzige Möglichkeit, ein generisches Array erfolgreich zu erstellen, besteht darin, ein typgelöschtes Array zu erstellen und es dann umzuwandeln, wie im Code:

, der Ausgabename des Klassenobjekts von gia ist Generic[]. gia = (Genericc0f559cc8d56b43654fcbe4aa9df7b4a[])new Object[SIZE]gia = (Genericc0f559cc8d56b43654fcbe4aa9df7b4a[])new Generic[SIZE]Mein persönliches Verständnis ist: Aufgrund der Typlöschung entspricht Genericc0f559cc8d56b43654fcbe4aa9df7b4a dem ursprünglichen Typ Generic, sodass die Transformation in tatsächlich in Generic[] umgewandelt wird, was so aussieht, als gäbe es keine Transformation , aber mit der Parameterprüfung und automatischen Transformation des Compilers wird beim Einfügen von und

in das Array ein Fehler gemeldet, und das Herausnehmen von gia[0] nach

erfordert keine manuelle Transformation. gia = (Genericc0f559cc8d56b43654fcbe4aa9df7b4a[])new Generic[SIZE] new Object()new Genericeafb63d086dd6c9bd19609d76bcc2869()Genericc0f559cc8d56b43654fcbe4aa9df7b4aT[]-Array verwenden

Im obigen Beispiel ist der Typ des Elements eine generische Klasse. Schauen wir uns ein Beispiel an, bei dem der Typ des Elements selbst ein generischer Parameter ist:

Im obigen Code besteht die Erstellung eines generischen Arrays darin, ein Objektarray zu erstellen und es dann in T[ umzuwandeln ]. Der eigentliche Typ des Arrays ist jedoch immer noch Object[]. Beim Aufruf der rep()-Methode wird eine ClassCastException gemeldet, da Object[] nicht in Integer[] konvertiert werden kann.

public class GenericArray<T> {
 private T[] array;
 @SuppressWarnings("unchecked")
 public GenericArray(int sz) {
 array = (T[])new Object[sz]; // 创建泛型数组
 }
 public void put(int index, T item) {
 array[index] = item;
 }
 public T get(int index) { return array[index]; }
 // Method that exposes the underlying representation:
 public T[] rep() { return array; } //返回数组 会报错
 public static void main(String[] args) {
 GenericArray<Integer> gai =
 new GenericArray<Integer>(10);
 // This causes a ClassCastException:
 //! Integer[] ia = gai.rep();
 // This is OK:
 Object[] oa = gai.rep();
 }
}

Dann der Code, der ein generisches Array erstellt

Warum meldet er keinen Fehler? Mein Verständnis ähnelt dem, was zuvor eingeführt wurde, und entspricht der Transformation in array = (T[])new Object[sz] . Es scheint jedoch, dass es keine Transformation gibt, aber die Parameterprüfung und automatische Transformation erfolgt. Und wenn Sie den generischen Parameter in ändern, entspricht einer Umwandlung in , da der Typ bis zur ersten Grenze gelöscht wird, was zu einem Fehler führen sollte. Das Folgende ist der Code des Experiments: Object[]ba95df6fbb265e67dd0723036aced316array = (T[])new Object[sz] Im Vergleich zur Originalversion hat der obige Code nur die erste Zeile geändert und Integer[] in

geändert. Dann besteht keine Notwendigkeit, rep( aufzurufen ), in Beim Erstellen eines generischen Arrays wird ein Fehler gemeldet. Das Folgende ist das laufende Ergebnis:
public class GenericArray<T extends Integer> {
 private T[] array;
 @SuppressWarnings("unchecked")
 public GenericArray(int sz) {
 array = (T[])new Object[sz]; // 创建泛型数组
 }
 public void put(int index, T item) {
 array[index] = item;
 }
 public T get(int index) { return array[index]; }
 // Method that exposes the underlying representation:
 public T[] rep() { return array; } //返回数组 会报错
 public static void main(String[] args) {
 GenericArray<Integer> gai =
 new GenericArray<Integer>(10);
 // This causes a ClassCastException:
 //! Integer[] ia = gai.rep();
 // This is OK:
 Object[] oa = gai.rep();
 }
}

 8742468051c85b06f0a0af9e3e506b5cba95df6fbb265e67dd0723036aced316

Use Object[] array
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
at GenericArray.<init>(GenericArray.java:15)

由于擦除,运行期的数组类型只能是 Object[],如果我们立即把它转型为 T[],那么在编译期就失去了数组的实际类型,编译器也许无法发现潜在的错误。因此,更好的办法是在内部最好使用 Object[] 数组,在取出元素的时候再转型。看下面的例子:

public class GenericArray2<T> {
 private Object[] array;
 public GenericArray2(int sz) {
 array = new Object[sz];
 }
 public void put(int index, T item) {
 array[index] = item;
 }
 @SuppressWarnings("unchecked")
 public T get(int index) { return (T)array[index]; }
 @SuppressWarnings("unchecked")
 public T[] rep() {
 return (T[])array; // Warning: unchecked cast
 }
 public static void main(String[] args) {
 GenericArray2<Integer> gai =
 new GenericArray2<Integer>(10);
 for(int i = 0; i < 10; i ++)
 gai.put(i, i);
 for(int i = 0; i < 10; i ++)
 System.out.print(gai.get(i) + " ");
 System.out.println();
 try {
 Integer[] ia = gai.rep();
 } catch(Exception e) { System.out.println(e); }
 }
} /* Output: (Sample)
0 1 2 3 4 5 6 7 8 9
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
*///:~

现在内部数组的呈现不是 T[] 而是 Object[],当 get() 被调用的时候数组的元素被转型为 T,这正是元素的实际类型。不过调用 rep() 还是会报错, 因为数组的实际类型依然是Object[],终究不能转换为其它类型。使用 Object[] 代替 T[] 的好处是让我们不会忘记数组运行期的实际类型,以至于不小心引入错误。

使用类型标识

其实使用 Class 对象作为类型标识是更好的设计:

public class GenericArrayWithTypeToken<T> {
 private T[] array;
 @SuppressWarnings("unchecked")
 public GenericArrayWithTypeToken(Class<T> type, int sz) {
 array = (T[])Array.newInstance(type, sz);
 }
 public void put(int index, T item) {
 array[index] = item;
 }
 public T get(int index) { return array[index]; }
 // Expose the underlying representation:
 public T[] rep() { return array; }
 public static void main(String[] args) {
 GenericArrayWithTypeToken<Integer> gai =
 new GenericArrayWithTypeToken<Integer>(
 Integer.class, 10);
 // This now works:
 Integer[] ia = gai.rep();
 }
}

在构造器中传入了 Class8742468051c85b06f0a0af9e3e506b5c 对象,通过 Array.newInstance(type, sz) 创建一个数组,这个方法会用参数中的 Class 对象作为数组元素的组件类型。这样创建出的数组的元素类型便不再是 Object,而是 T。这个方法返回 Object 对象,需要把它转型为数组。不过其他操作都不需要转型了,包括 rep() 方法,因为数组的实际类型与 T[] 是一致的。这是比较推荐的创建泛型数组的方法。

总结

数组与泛型的关系还是有点复杂的,Java 中不允许直接创建泛型数组。本文分析了其中原因并且总结了一些创建泛型数组的方式。其中有部分个人的理解,如果错误希望大家指正。下一篇会总结通配符的使用。

Das obige ist der detaillierte Inhalt vonZusammenfassung von Java Generics (2) – Generics und Arrays. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn