Java generische Geschichte

Einführung

Aufgrund der vorherigen statischen Java-Story werde ich die Java-Zeit fortsetzen.

Tor

Egal, ob es sich um Generika oder Generika handelt. Schauen wir uns einige einfache Beispiele an und spielen mit Generika in Java.

Generika (generischer Typ) und Typensicherheit

Eingeführt in jdk 1.5.

Gerade als ich dies schrieb, sagte Java: "Bitte aktualisieren Sie jdk 9.0.4" und ich hörte, dass java10 veröffentlicht wird, also wurde es jdk 1.5 ... ..

Davor,

List lst = new ArrayList();
Map map = new HashMap();

Es scheint, dass es in einem rohen Typ wie diesem geschrieben wurde. Es kann immer noch funktionieren, wird aber nicht empfohlen.

Jedes Objekt passt hinein. Es passt hinein.

lst.add("sample");
lst.add(11111);

// for-jeder ist auch 1.5 oder später
//Der Iterator ist noch roh
Iterator it = lst.iterator();
while (it.hasNext()) {
  Object o = it.next();
  System.out.print(o); // sample1111
}

Dies garantiert keine Schimmelpilzsicherheit. Sie müssen beim Abrufen der Elemente in der Sammlung sorgfältig umsetzen

lst.add("sample");
lst.add(11111);

Iterator it = lst.iterator();
while (it.hasNext()) {
  Object o = it.next();
  System.out.print((String) o); //Beispiel und ClassCastException
}

Auf diese Weise bemerken Sie die Ausnahme nur zur Laufzeit, wenn Sie einen Fehler im Typ machen, unabhängig davon, ob es sich um einen Fehler handelt oder nicht, und das Programm wird möglicherweise zu einem unerwarteten Zeitpunkt gestoppt.

In Bezug auf die Ausnahme

Die `` ClassCastException```, die ausgelöst wird, wenn die Umwandlung fehlschlägt, ist eine Ausnahmeklasse, die von der `RuntimeException``` erbt.

RuntimeException ist eine Laufzeitausnahme, und die Ausnahmebehandlung liegt beim Benutzer..


 Mit anderen Worten, die Zeit, um den Fehler zu bemerken, ist nicht zur Kompilierungszeit, sondern zur nachfolgenden Ausführungszeit.


# Formales Argument `` `<E>` ``
 Seit jdk 1.5 hat sich die `` `Collection``` -Schnittstelle (?) Zu` `Collection <E>` `` entwickelt, um nur einen Typ aufzunehmen.

#### **`Wie in der Sammlung javadoc geschrieben, ist E."Arten von Elementen in dieser Sammlung"Ist das. `**

Gleiches gilt für die "Liste", die die Sammlung erbt, die jetzt "Liste " ist. Ich habe noch nicht entschieden, was enthalten sein wird, aber ich garantiere, dass nur ein Typ enthalten sein wird, also werde ich ihn vorübergehend einfügen. Es ist wahrscheinlich Element's E.

Wenn Sie also tatsächlich eine Instanz einer Liste erstellen, ist `List <Integer> lst = new ArrayList <> ();` oder ... Schließlich wird der formale Argumentensitz gefüllt, wobei `` `Integer``` das tatsächliche Argument </ b> in dieser Liste wird.

Diamantoperator seit jdk7

List lst = new ArrayList(); List lst = new ArrayList<>(); //Jetzt optional


 Diese generischen Schnittstellen und ihre Implementierungsklassen werden als <b> generische Typen </ b> bezeichnet.

 Generika in Java können auch für Klassen, Schnittstellen, Methoden und Konstruktoren deklariert werden.

# Unterschied zwischen Rohtyp und `` `<Objekt>` ``
 Beispielsweise kann `` `List <Object>` `wie der Prototyp List Objekte eines beliebigen Typs speichern.

```java
 List<Object> objList = new ArrayList<>();
 objList.add("a");
 objList.add(1);
 objList.add(new Sample());

An diesem Punkt scheint es keinen großen Unterschied zu machen,

Erstens ist die Rohtypliste

  private static List rawList() {
    return new ArrayList();
  }

  private static void printList(List rawList) {
    rawList.forEach(System.out::println);
  }
  public static void main(String... args) {
    printList(rawList()); //Durchlaufen
  } 

Der Compiler sagt nichts. Mit anderen Worten, es ist nicht typsicher, da der Zeitpunkt, zu dem eine Typinkongruenz auftritt und Sie feststellen, dass sie zur Laufzeit liegt.

Auf der anderen Seite `List <Object>`

List<Object> objList = new ArrayList<String>(); //Kompilierungsfehler

Und

  private static List<String> strList() {
    return new ArrayList<String>();
  }

  private static void printObjList(List<Object> objList) {
    objList.forEach(System.out::println);
  }

  public static void main(String... args) {
    printObjList(strList()); //Kompilierungsfehler
  }

Und umgekehrt (?)

  private static List<Object> objList() {
    return new ArrayList<Object>();
  }

  private static void printStrList(List<String> strList) {
    strList.forEach(System.out::println);
  }

  public static void main(String... args) {
    printStrList(objList()); //Kompilierungsfehler
  }

Kann nicht.

Der Grund ist, dass `List <String>` ein Subtyp von List ist, aber nicht `List <Object>` `. Das heißt,` List Es gibt keine Vererbungsbeziehung zwischen `und List ``.

Aus diesem Grund werden Generika als invariant </ I> bezeichnet.

Wenn Sie also einen der vorherigen Codes ändern möchten

List<Object> objList = new ArrayList<String>(); //Kompilierungsfehler
List<?> someTypeList = new ArrayList<String>();

Ich denke, es wird so aussehen.

<?>In Bezug darauf möchte ich es in der folgenden Wildcard organisieren.



# Unterschied zwischen Generika und Sequenz

## Die Sequenz ist kovariant
 Es ist eine Fortsetzung der Geschichte, dass Generika unveränderlich sind.
 Was die Natur der Kovarianz betrifft, haben wir früher über die "Vererbungsbeziehung" gesprochen, aber mit einem Array können Sie so etwas tun.

```java
  private static void printAry(Object[] objAry) {
    Arrays.stream(objAry).forEach(System.out::println);
  }

  private static String[] strAry(String[] strAry) {
    return strAry;
  }

  printAry(strAry(new String[]{"a", "b", "c"}));

Wenn zwischen String und Object eine Vererbungsbeziehung besteht (String ist ein Unterobjekt von Object), wird die Vererbungsbeziehung auch zwischen String [] und Object [] angewendet.

Mit anderen Worten

Object[] objAry = new String[10];
objAry[0] = 1; 

Ich versuche, einen ungültigen Typ zu speichern, bemerke ihn jedoch beim Kompilieren nicht.

Generika-Radiergummi (Entsorgungstyp)

  • Realisierung
    Das Array kennt den Elementtyp bereits zur Laufzeit und wird mit diesem Typ ausgeführt.

  • Unmöglich zu materialisieren
    Auf der anderen Seite werden Generika durch Radiergummi implementiert (Elementtypinformationen werden zur Laufzeit gelöscht), nachdem sie einmal zur Kompilierungszeit überprüft wurden. Eine Information geht zur Laufzeit im Vergleich zur Kompilierungszeit verloren.
    Dies ermöglichte es uns, einem vorhandenen Rohtyp Generika hinzuzufügen, selbst für eine Sammlung von Rohtypen vor jdk 1.5.

Generisches Design

Tuple Ein Tupel ist eine Datenstruktur, in der mehrere und unterschiedliche Elemente der Reihe nach gespeichert werden können.

Pair.java


public class Pair<V, W> {
  private final V v;
  private final W w;

  Pair(V v, W w) {
    this.v = v;
    this.w = w;
  }

  V getV() {
    return v;
  }

  W getW() {
    return w;
  }
}
Pair<String, Integer> tuple2 = new Pair<>("toilet-score", 100);

String v = tuple2.getV();
Integer w = tuple2.getW();
  
System.out.println(v + w);

Durch Erweitern des Verwendungstyps des Paares, das zwei grundlegende heterogene Datentypen speichert und abruft, z

TupleUtils.java


public class Tuple {
  public static class Tuple2<A, B> {
    private final A a;
    private final B b;

    public Tuple2(A a, B b) {
      this.a = a;
      this.b = b;
    }

    public A getA() {
      return a;
    }

    public B getB() {
      return b;
    }
  }

  public static class Tuple3<A, B, C> extends Tuple2<A, B> {
    private final C c;

    public Tuple3(A a, B b, C c) {
      super(a, b);
      this.c = c;
    }

    public C getC() {
      return c;
    }
  }

  // Tuple4, Tuple5 ... 
}

(Statische innere Klasse für Vielseitigkeit hinzugefügt, Accessor für jeden Konstruktor auf öffentlich geändert) Ich denke, Sie können eine generische Tapple-Dienstprogrammklasse erstellen, in der drei, vier oder mehr Datentypen typsicher gespeichert werden können.

Leihen Sie sich die Weisheit unserer Vorgänger aus

Als zuverlässigeres und vielfältigeres Muster existiert die Bibliothek bereits, daher ist das berühmte Commons Lang Pair usw. .

Obwohl es klein ist, wird es absichtlich von einem Typ bis zu zehn Typen (!) Bereitgestellt. Mit einer einfachen Konfiguration javatuples: Offizieller Github Siehe auch .

In Bezug auf Javatuples persönlich dachte ich, dass ein Taple mit nur einem Typ namens Unit gestartet wurde und von dort aus eine schöne Vererbungsform hat ... aber es gibt zwei Typen. Es war interessant, dass die Tupelklasse die Achse war und die Einheit von Tupel geerbt wurde.

Platzhalter

Als Erweiterung der vorherigen Geschichte sind die Generika unveränderlich. Der Pseudotyp <T> garantiert, dass es einen bestimmten Typ gibt, aber es ist ein Typ, der den T-Typ erbt. `` List `` und ``List ` `` haben keine Vererbungsbeziehung. Das heißt, es ist nicht flexibel.

Daher wird zum gleichen Zeitpunkt wie bei Generics (jdk 1.5) ein Platzhalter angezeigt.

Nicht begrenzender Platzhalter `<?>`

Wenn Sie Generika verwenden möchten, sich aber noch nicht für die tatsächlichen Typparameter entschieden haben, übergeben Sie `?` An den temporären Typ `<E>`.

//Weil T nicht aufgelöst werden kann,Kompilierungsfehler
List<T> someList = new ArrayList<String>(); 

//Weil Sie versuchen, es zu speichern, bevor der tatsächliche Typ festgelegt ist,Kompilierungsfehler
List<?> anotherList = new ArrayList<>(); anotherList.add("a"); 

// OK
List<?> theOtherList = new ArrayList<String>(); theOtherList.add("a"); 

Rand-Platzhalter (obere Grenze) `<? T> verlängern`

// OK.Ganzzahl ist das Sub von Number-class.
List<? extends Number> myList = new ArrayList<Integer>();  

 // OK.Long ist das Sub von Number-class.
List<? extends Number> yourList = new ArrayList<Long>();  

//Weil Object ein Typ ist, der Number überschreitet,Kompilierungsfehler
List<? extends Number> someList = new ArrayList<Object>();

Rand-Platzhalter (untere Grenze) `<? Super T>`

//Weil Long keine übergeordnete Klasse von Ganzzahlen ist,Kompilierungsfehler
List<? super Integer> suNumList2 = new ArrayList<Long>(); 

// OK.Number ist eine übergeordnete Klasse von Integer. 
List<? super Integer> suNumList = new ArrayList<Number>();

Funktionsschnittstelle und Generika

Nachfolgend sind die wichtigsten Funktionsschnittstellen aufgeführt, die in jdk 1.8 eingeführt wurden. Funktionsschnittstelle javadoc , aber eine abstrakte Methode Es ist eine Schnittstelle, die nur hat.

Das Folgende ist Teil der Funktionsschnittstelle, die in `` `java.util.Objects``` enthalten ist.

Function.java


@FunctionalInterface
public interface Function<T, R> {
    /**
     * Applies this function to the given argument. 
     *Akzeptiert das angegebene Argument.
     * 
     * @param t the function argument 
     *Parameter t Funktionsargument
     * 
     * @return the function result 
     *Ergebnis der Rückgabefunktion
     */
    R apply(T t);

    // omit 
}

Diese Funktionsschnittstellen, wie z. B. Function BiFunction Consumer Supplier ... Ich habe mich noch nicht für den Typ entschieden, aber ich akzeptiere etwas Auch hier ist der Typ noch nicht entschieden, aber er stellt eine Struktur dar, die als Ergebnis etwas zurückgibt.

IntSupplier getAsInt()Einige Methoden, wie z. B. Methoden, haben im Voraus entschieden, dass der Rückgabewert nur vom Typ int ist. Grundsätzlich können Sie jedoch feststellen, dass wir versuchen, ihn mithilfe von Generika flexibel zu gestalten..



# Generika und I-Generika Methodenähnliches Spiel
 Es ist ein seltsamer Untertitel, aber es ist wie ein Spielmemo.

 Neulich habe ich einen Algorithmus erstellt.
 Es ist eine O (N) -Funktion, die eine Liste akzeptiert, die sich nicht um den Elementtyp kümmert, die Liste verarbeitet und eine weitere Liste mit Operationsergebnissen zurückgibt.


#### **`Something.java`**
```java

  private static List<Object> doSomething(List<Object> list) {

    List<Object> result = new ArrayList<>();

    //wird bearbeitet

    return result;
  }

Aber

Something.java


  public static void main(String[] args) {
   List<String> strListA = Arrays.asList("c", "a", "b", "b", "c", "a", "d", "b"); 
  
   List<Object> strListB = Arrays.asList("c", "a", "b", "b", "c", "a", "d", "b");

   // List<Object>Nicht Liste<String>Kompilierungsfehler zum Bestehen
   doSomething(strListA).forEach(System.out::print);
   
   doSomething(strListB).forEach(System.out::print);
  }

Die Tatsache, dass alle an Parameter übergebenen Listentypparameter auf den Objekttyp beschränkt sind, hat mir nicht gefallen. Wenn Sie beispielsweise eine Methode hinzufügen, die eine Liste generiert, die nur Zahlen akzeptiert, und diese als Parameter übergeben möchten, ist dies etwas unpraktisch.

Something.java


  private static List<Number> makeNumberList(Number...nums) {
    List<Number> list = new ArrayList<>();
    for (Number num : nums) list.add(num);
    return list;
  }

Something.java


  public static void main(String[] args) {
   doSomething(makeNumberList(1, 2, 3, 4, 1)).forEach(System.out::print); //Kompilierungsfehler
  }

Ändern Sie also `<Objekt>` in `<T>`. ~~ Es wurde ein Typ-Token hinzugefügt, damit der Typ beim Kompilieren überprüft werden kann. ~~

Bemerkungen (oben, Stornierungszeile)

//Kral Literal Klasse zur MethodeDurch das Vorbeigehen doSomething(List list, Class type)

//Es ist möglich, Typinformationen zur Laufzeit zu übermitteln doSomething(someList, Integer.class)

 Ich habe versucht, es nur zu kompilieren, wenn der Typparameter und das Typ-Token übereinstimmen, aber ich habe es auf Eis gelegt, weil die Verwendung des Typs in der Methode während des Änderungsprozesses verloren gegangen ist.



#### **`Something.java`**
```java

  private static <T> List<T> doSomething(List<T> list) {
    
    List<T> result = new ArrayList<>();
    
    //wird bearbeitet
    
    return result;
  }

Versuchen Sie, den Nummerntyp und seine untergeordneten Typen in die Methode aufzunehmen, mit der die Nummerntypliste erstellt wird ...

Something.java


  private static <T extends Number> List<T> makeNumberList(T...args) {
    List<T> list = new ArrayList<>();
    for (T arg : args) list.add(arg);
    return list;
  }

Something.java


  public static void main(String[] args) {
    List<Integer> someList = makeNumberList(5, 1, 2, 3, 3, 4, 3, 1, 2, 4, 5);
    doSomething(someList).forEach(System.out::print);
  }

Auf diese Weise können Sie eine andere Liste als `List <Object>` als Parameter übergeben.

Das Ende

Aus irgendeinem Grund machte ich mir Sorgen um Generika. Auf seltsame Weise machte ich mir Sorgen um etwas wie "ein typsicheres und flexibles magisches Werkzeug, das plötzlich in eine Welt fiel, die typsicher, aber nicht so sicher war" (was sagen Sie). Es kann sein.

Während ich dies selbst schrieb, gab es viele Dinge, die nicht eindeutig waren, oder ich wusste das nicht. Bitte lassen Sie es mich wissen, sobald Sie Fehler finden.

Referenz

Effektive Java 2nd Edition Oracle Java docs

  • https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.2
  • https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html#package.description

das ist alles

Recommended Posts