Heim  >  Artikel  >  Java  >  Ausführliche Erklärung der Java-Generika, leicht verständlich in nur 5 Minuten

Ausführliche Erklärung der Java-Generika, leicht verständlich in nur 5 Minuten

高洛峰
高洛峰Original
2016-12-19 15:22:121283Durchsuche

Wir wissen, dass Variablen vor ihrer Verwendung definiert werden müssen. Wenn Sie eine Variable definieren, müssen Sie ihren Datentyp angeben und angeben, welcher Datentyp welchem ​​Wert zugewiesen ist.

Wenn wir nun eine Klasse zur Darstellung von Koordinaten definieren wollen, kann der Datentyp der benötigten Koordinaten Ganzzahlen, Dezimalzahlen und Strings sein, zum Beispiel:

x = 10, y = 10

x = 12,88, y = 129,65

x = „Tokio 180 Grad“, y = „Nördliche Breite 210 Grad“


Für verschiedene Datentypen zusätzlich zur Verwendung von Methodenüberladung, auch mit Hilfe von Autoboxing und Aufwärtstransformation. Wir wissen, dass grundlegende Datentypen automatisch verpackt und in entsprechende Verpackungsklassen konvertiert werden können. Object ist die Vorgängerklasse aller Klassen und Instanzen jeder Klasse können in den Object-Typ umgewandelt werden, zum Beispiel:

int - -> Integer --> Object

double --> Object

String --> , nur Sie müssen eine Methode definieren, um alle Arten von Daten zu empfangen. Bitte schauen Sie sich den folgenden Code an:


Im obigen Code gibt es kein Problem beim Generieren von Koordinaten, aber beim Herausnehmen der Koordinaten müssen Sie nach unten transformieren. Wie bereits erwähnt, bestehen Risiken Bei der Abwärtstransformation ist es außerdem nicht einfach, Ausnahmen während der Laufzeit zu finden. Vermeiden Sie daher die Verwendung von Downcasting. Wenn Sie den obigen Code ausführen, wird in Zeile 12 eine java.lang.ClassCastException-Ausnahme ausgelöst.

public class Demo {
    public static void main(String[] args){
        Point p = new Point();

        p.setX(10);  // int -> Integer -> Object
        p.setY(20);
        int x = (Integer)p.getX();  // 必须向下转型
        int y = (Integer)p.getY();
        System.out.println("This point is:" + x + ", " + y);
       
        p.setX(25.4);  // double -> Integer -> Object
        p.setY("东京180度");
        double m = (Double)p.getX();  // 必须向下转型
        double n = (Double)p.getY();  // 运行期间抛出异常
        System.out.println("This point is:" + m + ", " + n);
    }
}

class Point{
    Object x = 0;
    Object y = 0;

    public Object getX() {
        return x;
    }
    public void setX(Object x) {
        this.x = x;
    }
    public Object getY() {
        return y;
    }
    public void setY(Object y) {
        this.y = y;
    }
}
Gibt es also eine bessere Möglichkeit, die Verwendung von Überlastung (doppeltem Code) zu vermeiden und das Risiko zu minimieren?


Ja, Sie können generische Klassen (Java-Klasse) verwenden, die jede Art von Daten akzeptieren können. Das sogenannte „Generikum“ bezieht sich auf einen „breiten Datentyp“, also auf einen beliebigen Datentyp.

Ändern Sie den obigen Code und verwenden Sie die generische Klasse:


Laufendes Ergebnis:

Dieser Punkt ist: 10, 20
public class Demo {
    public static void main(String[] args){
        // 实例化泛型类
        Point<Integer, Integer> p1 = new Point<Integer, Integer>();
        p1.setX(10);
        p1.setY(20);
        int x = p1.getX();
        int y = p1.getY();
        System.out.println("This point is:" + x + ", " + y);
       
        Point<Double, String> p2 = new Point<Double, String>();
        p2.setX(25.4);
        p2.setY("东京180度");
        double m = p2.getX();
        String n = p2.getY();
        System.out.println("This point is:" + m + ", " + n);
    }
}

// 定义泛型类
class Point<T1, T2>{
    T1 x;
    T2 y;
    public T1 getX() {
        return x;
    }
    public void setX(T1 x) {
        this.x = x;
    }
    public T2 getY() {
        return y;
    }
    public void setY(T2 y) {
        this.y = y;
    }
}
Dieser Punkt ist: 25,4, Tokio 180 Grad


Im Vergleich zur Definition gewöhnlicher Klassen enthält der obige Code 5d766edf0ee4a0fde742e7cd6e836235 Den Wert der Daten bezeichnen wir als Typparameter. In Generika kann nicht nur der Wert von Daten über Parameter übergeben werden, sondern auch der Datentyp kann über Parameter übergeben werden. T1, T2 sind lediglich Platzhalter für Datentypen und werden zur Laufzeit durch die echten Datentypen ersetzt.

Wertparameter (was wir normalerweise Parameter nennen) werden von Klammern umgeben, z. B. (int x, double y), Typparameter (generische Parameter) werden von spitzen Klammern umgeben und mehrere Parameter werden durch Kommas getrennt. Zum Beispiel 8742468051c85b06f0a0af9e3e506b5c oder aa4ab9f8aede8f36f905862d234ed6ca.

Typparameter müssen nach dem Klassennamen angegeben werden. Sobald die Typparameter angegeben sind, können sie innerhalb der Klasse verwendet werden. Der Typparameter muss ein zulässiger Bezeichner sein und es ist üblich, einen einzelnen Großbuchstaben zu verwenden. K steht für einen Schlüssel, V für einen Wert, E für eine Ausnahme oder einen Fehler und T für einen Datentyp im allgemeinen Sinne.

Eine generische Klasse muss beim Instanziieren den spezifischen Typ angeben, d. h. den Wert an den Typparameter übergeben. Das Format lautet:
className variable3e7ff64bb1c9404d6a7b37b44d70da47 ;() ;
Sie können den Datentyp auf der rechten Seite des Gleichheitszeichens auch weglassen, es wird jedoch eine Warnung angezeigt, nämlich:
className variable3e7ff64bb1c9404d6a7b37b44d70da47 = new className();

Denn wenn der Datentyp angegeben wird, wird beim Zuweisen von Werten anderer Typen eine Ausnahme ausgelöst. Es besteht keine Notwendigkeit für eine Abwärtskonvertierung und es bestehen keine potenziellen Risiken. Dies ist praktischer als das automatische Boxen Aufwärtskonvertierung, die am Anfang dieses Artikels eingeführt wurde.

Hinweis:

Generika sind eine neue Funktion von Java 1.5. Sie basieren auf C++-Vorlagen und sind im Wesentlichen die Anwendung parametrisierter Typen.

Typparameter können nur zur Darstellung von Referenztypen und nicht zur Darstellung von Grundtypen wie int, double, char usw. verwendet werden. Die Übergabe von Basistypen verursacht jedoch keinen Fehler, da sie automatisch in die entsprechende Wrapper-Klasse eingepackt werden.

Generische Methoden

Zusätzlich zur Definition generischer Klassen können Sie auch generische Methoden definieren. Definieren Sie beispielsweise eine generische Methode zum Drucken von Koordinaten:

Ergebnisse ausführen :

Dieser Punkt ist: 10, 20
public class Demo {
public static void main(String[] args){
// 实例化泛型类
Point<Integer, Integer> p1 = new Point<Integer, Integer>();
p1.setX(10);
p1.setY(20);
p1.printPoint(p1.getX(), p1.getY());

Point<Double, String> p2 = new Point<Double, String>();
p2.setX(25.4);
p2.setY("东京180度");
p2.printPoint(p2.getX(), p2.getY());
}
}

// 定义泛型类
class Point<T1, T2>{
T1 x;
T2 y;
public T1 getX() {
return x;
}
public void setX(T1 x) {
this.x = x;
}
public T2 getY() {
return y;
}
public void setY(T2 y) {
this.y = y;
}

// 定义泛型方法
public <T1, T2> void printPoint(T1 x, T2 y){
T1 m = x;
T2 n = y;
System.out.println("This point is:" + m + ", " + n);
}
}
Dieser Punkt ist: 25,4, Tokio 180 Grad


Der obige Code definiert eine generische Methode printPoint(), die sowohl gewöhnliche Parameter als auch Typen hat. Parameter und Typ Parameter müssen nach dem Modifikator und vor dem Rückgabewerttyp platziert werden. Sobald Typparameter definiert sind, können sie in Parameterlisten, Methodenkörpern und Rückgabetypen verwendet werden.

Im Gegensatz zur Verwendung einer generischen Klasse müssen Sie bei Verwendung einer generischen Methode den Parametertyp nicht angeben. Der Compiler findet den spezifischen Typ automatisch anhand der übergebenen Parameter. Abgesehen von den unterschiedlichen Definitionen werden generische Methoden genauso aufgerufen wie gewöhnliche Methoden.

Hinweis: Generische Methoden hängen nicht unbedingt mit generischen Klassen zusammen, und generische Methoden können auch in gewöhnlichen Klassen definiert werden. Die Typparameter T1 und T2 in der generischen Methode printPoint() stehen nicht unbedingt im Zusammenhang mit T1 und T2 in der generischen Klasse Point. Stattdessen können auch andere Bezeichner verwendet werden:

public static <V1, V2> void printPoint(V1 x, V2 y){
V1 m = x;
V2 n = y;
System.out.println("This point is:" + m + ", " + n);
}

泛型接口

在Java中也可以定义泛型接口,这里不再赘述,仅仅给出示例代码:

public class Demo {
public static void main(String arsg[]) {
Info<String> obj = new InfoImp<String>("www.weixueyuan.net");
System.out.println("Length Of String: " + obj.getVar().length());
}
}

//定义泛型接口
interface Info<T> {
public T getVar();
}

//实现接口
class InfoImp<T> implements Info<T> {
private T var;

// 定义泛型构造方法
public InfoImp(T var) {
this.setVar(var);
}

public void setVar(T var) {
this.var = var;
}

public T getVar() {
return this.var;
}
}

运行结果:
Length Of String: 18

类型擦除

如果在使用泛型时没有指明数据类型,那么就会擦除泛型类型,请看下面的代码:

public class Demo {
public static void main(String[] args){
Point p = new Point();  // 类型擦除
p.setX(10);
p.setY(20.8);
int x = (Integer)p.getX();  // 向下转型
double y = (Double)p.getY();
System.out.println("This point is:" + x + ", " + y);
}
}

class Point<T1, T2>{
T1 x;
T2 y;
public T1 getX() {
return x;
}
public void setX(T1 x) {
this.x = x;
}
public T2 getY() {
return y;
}
public void setY(T2 y) {
this.y = y;
}
}

运行结果:
This point is:10, 20.8

因为在使用泛型时没有指明数据类型,为了不出现错误,编译器会将所有数据向上转型为 Object,所以在取出坐标使用时要向下转型,这与本文一开始不使用泛型没什么两样。

限制泛型的可用类型

在上面的代码中,类型参数可以接受任意的数据类型,只要它是被定义过的。但是,很多时候我们只需要一部分数据类型就够了,用户传递其他数据类型可能会引起错误。例如,编写一个泛型函数用于返回不同类型数组(Integer 数组、Double 数组、Character 数组等)中的最大值:

public <T> T getMax(T array[]){
T max = null;
for(T element : array){
max = element.doubleValue() > max.doubleValue() ? element : max;
}
return max;
}

上面的代码会报错,doubleValue() 是 Number 类的方法,不是所有的类都有该方法,所以我们要限制类型参数 T,让它只能接受 Number 及其子类(Integer、Double、Character 等)。

通过 extends 关键字可以限制泛型的类型,改进上面的代码:

public <T extends Number> T getMax(T array[]){
T max = null;
for(T element : array){
max = element.doubleValue() > max.doubleValue() ? element : max;
}
return max;
}

ae70bf429ee8ba84ce42dadb64335cd7 表示 T 只接受 Number 及其子类,传入其他类型的数据会报错。这里的限定使用关键字 extends,后面可以是类也可以是接口。但这里的 extends 已经不是继承的含义了,应该理解为 T 是继承自 Number 类的类型,或者 T 是实现了 XX 接口的类型。



更多Java泛型详解相关文章请关注PHP中文网!

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
Vorheriger Artikel:Generische Methoden in JavaNächster Artikel:Generische Methoden in Java