Les génériques sont une nouvelle fonctionnalité de Java SE 1.5 L'essence des génériques est types paramétrés , c'est-à-dire que le type de données sur lequel l'opération est effectuée est spécifié en tant que paramètre.
Avant Java SE 1.5, en l'absence de génériques, le paramètre "arbitraire" était implémenté en faisant référence au type Object. L'inconvénient de "arbitraire" était qu'une coercition explicite était nécessaire. Conversion de type , et ceci la conversion nécessite que le développeur connaisse au préalable le type de paramètre réel. Pour les erreurs de conversion de type forcée, le compilateur peut ne pas générer d'erreur , et l'exception ne se produira que lorsque s'exécutera . Il s'agit d'un risque de sécurité.
L'avantage des génériques est que la sécurité des types est vérifiée au moment de la compilation et que tous les transtypages sont automatiques et implicites pour améliorer la réutilisation du code.
Prenons d'abord un cas où une chaîne est ajoutée à une ArrayList. Accidentellement » « Des entiers sont ajoutés, comme dans le code suivant,
Il n'y a pas d'erreur :
Mais lors de l'exécution, une erreur sera signalée : » java.lang.classCastException"
Parce qu'ArrayList maintient un tableau Object, private transient Object[] elementData;
, l'utilisation de get() renvoie un objet Object, qui nécessite cast , mais une valeur entière est mélangée au milieu, provoquant l'échec de la conversion forcée. Cette erreur est causée par le arbitraire de l'objet.
Si l'erreur de type de données peut être trouvée lors de la phase de compilation, il sera très pratique que les Génériques répondent à cette exigence : je modifierai ce programme en utilisant des génériques : vous le trouverez lors de la compilation. phase. Une erreur a été signalée
N'utilise pas de génériques :
package com.chb.fanxing;public class NoGen { private Object ob; public NoGen(Object ob) { this.ob = ob; } getter setter... private void showType() { System.out.println("数据的实际类型是:" + ob.getClass().getName()); } public static void main(String[] args) { NoGen ngInt = new NoGen(88); ngInt.showType(); int i = (int)ngInt.getOb(); System.out.println("value = " + i); System.out.println("---------------"); NoGen ngStr = new NoGen("88"); ngStr.showType(); String str = (String)ngStr.getOb(); System.out.println("value = " + str); } }
Utiliser des génériques :
package com.chb.fanxing;public class Gen<T> { private T ob; public Gen(T ob) { this.ob = ob; } getter setter... private void showType() { System.out.println("T的实际类型:"+ob.getClass().getName()); } public static void main(String[] args) { //定义一个Integer版本 Gen<Integer> genInt = new Gen<Integer>(88); genInt.showType(); int i = genInt.getOb();//此处不用强制转换 System.out.println("value = " + i); System.out.println("----------------------"); Gen<String> genStr = new Gen<String>("88"); genStr.showType(); String str = genStr.getOb(); System.out.println("value = "+str); } }
Les résultats d'exécution des deux exemples sont cohérents
数据的实际类型是:java.lang.Integervalue = 88 ---------------数据的实际类型是:java.lang.String value = 88
En comparant les deux exemples, vous trouverez :
En utilisant des génériques, la conversion forcée est automatiquement effectuée :
int i = genInt.getOb();//此处不用强制转换
Au lieu d'utiliser des génériques, vous devez effectuer une conversion forcée manuelle
int i = (int)ngInt.getOb();
class StringDemo { private String s; public StringDemo (String s) { this.s = s; } setter geter.... } class DoubleDemo{ private Double d; public DoubleDemo(Double d) { this.d = d; } setter getter... }
Observez attentivement que les fonctions des deux classes sont fondamentalement les mêmes, mais que les types de données sont différents car Object est le. classe de base de toutes les classes. , vous pouvez donc utiliser Object comme variable membre, afin que le code puisse être général. Le code refactorisé est le suivant :
class ObjectDemo{ private Object ob; public ObjectDemo(Object ob){ this.ob = ob; } setter getter... }
Test ObjectDemo :
public static void ObjectDemoTest(){ ObjectDemo strOD = new ObjectDemo("123"); ObjectDemo dOD = new ObjectDemo(new Double(23)); ObjectDemo od = new ObjectDemo(new Object()); System.out.println((String)strOD.getOb()); System.out.println((Double)dOD.getOb()); System.out.println(od.getOb()); }
Résultats d'exécution :
J'ai trouvé que la ObjectDemoTest()
conversion forcée doit être utilisée dans ce qui précède , ce qui est plus gênant Nous devons également connaître le type de données à convertir à l'avance afin de. effectuez la conversion correcte, sinon une erreur se produira lors de la compilation de l'entreprise, mais lors de l'exécution, "classCastException" apparaîtra. Nous n’avons donc pas besoin de réaliser nous-mêmes le casting, ce qui est particulièrement important pour les génériques.
class GenDemo<T>{ private T t; public GenDemo(T t) { this.t = t; } public void setT(T t) { this.t = t; } public T getT() { return t; } }
Test : Éliminer la conversion manuelle
public static void GenTest() { GenDemo<String> strOD = new GenDemo<String>("123"); GenDemo<Double> dOD = new GenDemo<Double>(new Double(23)); GenDemo<Object> od = new GenDemo<Object>(new Object()); System.out.println(strOD.getT()); System.out.println(dOD.getT()); System.out.println(od.getT()); }
Utiliser la représentation Un nom de support de type est équivalent à un paramètre formel. Le type de données est déterminé par le type des données réelles transmises et T est utilisé comme type de valeur de retour du membre, du paramètre et de la méthode.
T n'est qu'un nom, vous pouvez le choisir à volonté.
classe GenDemo, T n'impose aucune restriction, elle est en fait équivalente à Object,
est équivalente à la classe GenDemo.
Par rapport à Object, les classes définies à l'aide de génériques peuvent utiliser
GenDemo
peut également être laissé non spécifié, une conversion forcée est alors requise.
Ci-dessous, nous continuerons avec les génériques de Java
Génériques restreints
Restrictions d'interfaces multiples
Génériques génériques
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。
首先我们看一个案例,向一个ArrayList中添加字符串,“不小心”添加了整数,如下面代码,
并没有错误:
但是执行时,会报错:“java.lang.classCastException”
因为ArrayList中维护的是一个Object数组, private transient Object[] elementData;
, 使用get()返回的是一个Object对象, 需要强制转换,但是中间混杂一个Integer数值, 导致强制转换失败。这个错误就是由于Object的任意化导致的。
如果能在编译阶段就发现数据类型有错, 那么就很方便,泛型就满足了这个要求:我将这个程序修改一下,ArrayList使用泛型:会发现编译阶段就报错了.
不使用泛型:
package com.chb.fanxing;public class NoGen { private Object ob; public NoGen(Object ob) { this.ob = ob; } getter setter... private void showType() { System.out.println("数据的实际类型是:" + ob.getClass().getName()); } public static void main(String[] args) { NoGen ngInt = new NoGen(88); ngInt.showType(); int i = (int)ngInt.getOb(); System.out.println("value = " + i); System.out.println("---------------"); NoGen ngStr = new NoGen("88"); ngStr.showType(); String str = (String)ngStr.getOb(); System.out.println("value = " + str); } }
使用泛型:
package com.chb.fanxing;public class Gen<T> { private T ob; public Gen(T ob) { this.ob = ob; } getter setter... private void showType() { System.out.println("T的实际类型:"+ob.getClass().getName()); } public static void main(String[] args) { //定义一个Integer版本 Gen<Integer> genInt = new Gen<Integer>(88); genInt.showType(); int i = genInt.getOb();//此处不用强制转换 System.out.println("value = " + i); System.out.println("----------------------"); Gen<String> genStr = new Gen<String>("88"); genStr.showType(); String str = genStr.getOb(); System.out.println("value = "+str); } }
两个例子的运行结果是一致的
数据的实际类型是:java.lang.Integervalue = 88 ---------------数据的实际类型是:java.lang.String value = 88
对比两个例子会发现:
使用泛型,强制转换时自动进行的:
int i = genInt.getOb();//此处不用强制转换
而不使用泛型,必须要进行手动强制转化
int i = (int)ngInt.getOb();
class StringDemo { private String s; public StringDemo (String s) { this.s = s; } setter geter.... } class DoubleDemo{ private Double d; public DoubleDemo(Double d) { this.d = d; } setter getter... }
仔细观察两个类功能基本一致,只是数据类型不一样,考虑到重构,因为Object是所有类的基类,所以可以使用Object作为成员变量,这样代码就可以通用了。重构代码如下:
class ObjectDemo{ private Object ob; public ObjectDemo(Object ob){ this.ob = ob; } setter getter... }
ObjectDemo测试:
public static void ObjectDemoTest(){ ObjectDemo strOD = new ObjectDemo("123"); ObjectDemo dOD = new ObjectDemo(new Double(23)); ObjectDemo od = new ObjectDemo(new Object()); System.out.println((String)strOD.getOb()); System.out.println((Double)dOD.getOb()); System.out.println(od.getOb()); }
运行结果:
发现上面的ObjectDemoTest()
中必须要使用强制转换,这比较麻烦,我们还必须事先知道要转换的数据类型,才能进行正确的转换,否则,会出现错误, 业务编译时没有问题,但是一运行,会出现”classCastException”。所以我们需要不用自己进行强制转换,这是泛型就尤为重要。
class GenDemo<T>{ private T t; public GenDemo(T t) { this.t = t; } public void setT(T t) { this.t = t; } public T getT() { return t; } }
测试:省去了手动进行强制转换
public static void GenTest() { GenDemo<String> strOD = new GenDemo<String>("123"); GenDemo<Double> dOD = new GenDemo<Double>(new Double(23)); GenDemo<Object> od = new GenDemo<Object>(new Object()); System.out.println(strOD.getT()); System.out.println(dOD.getT()); System.out.println(od.getT()); }
使用表示一个类型持有者名称, 相当于一个形参,数据的类型是有实际传入的数据的类型决定,然后T作为成员、参数、方法的返回值的类型。
T仅仅是一个名字,可以随意取的。
class GenDemo , T没有进行任何限制, 实际相当于 Object,
等同于 class GenDemo。
与Object相比,使用泛型所定义的类,在定义和声明,可以使用来制定真实的数据类型,如:
GenDemo
也可以不指定,那么就需要进行强制转换。
以上就是java之泛型的内容,更多相关内容请关注PHP中文网(m.sbmmt.com)!