Java泛型
一、泛型引入
编写一段程序,在ArrayList中,添加3个Dog对象,Dog对象含有name和age属性,并输出name和age
-
不使用泛型解决
package com.hspedu.generic; import java.util.ArrayList; @SuppressWarnings({"all"}) public class Generic01 { public static void main(String[] args) { //使用传统的方法来解决 ArrayList arrayList = new ArrayList(); arrayList.add(new Dog("旺财", 10)); arrayList.add(new Dog("发财", 1)); arrayList.add(new Dog("小黄", 5)); //假如我们的程序员,不小心,添加了一只猫 arrayList.add(new Cat("招财猫", 8));//由于定义ArrayList时并没有指定类(默认是Object类型),所以这里加入Cat类型并不会报错,但到后面转型成Dog类型时会报运行错误 //遍历 for (Object o : arrayList) {//这里o的类型必须是Object不能指定成Dog,因为在定义的时候没有指定类型默认为Object;需要强制转型才能转成Dog类型 //向下转型Object ->Dog Dog dog = (Dog) o; System.out.println(dog.getName() + "-" + dog.getAge()); } } } /* 请编写程序,在ArrayList 中,添加3个Dog对象 Dog对象含有name 和 age, 并输出name 和 age (要求使用getXxx()) */ class Dog { private String name; private int age; public Dog(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class Cat { //Cat类 private String name; private int age; public Cat(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
-
使用泛型
package com.hspedu.generic.improve; import java.util.ArrayList; @SuppressWarnings({"all"}) public class Generic02 { public static void main(String[] args) { //使用传统的方法来解决===> 使用泛型 //1. 当我们 ArrayList<Dog> 表示存放到 ArrayList 集合中的元素是Dog类型 //2. 如果编译器发现添加的类型,不满足要求,就会报错 //3. 在遍历的时候,可以直接取出 Dog 类型而不是 Object //4. public class ArrayList<E> {} E称为泛型,那么 Dog->E ArrayList<Dog> arrayList = new ArrayList<Dog>(); arrayList.add(new Dog("旺财", 10)); arrayList.add(new Dog("发财", 1)); arrayList.add(new Dog("小黄", 5)); //假如我们的程序员,不小心,添加了一只猫 //arrayList.add(new Cat("招财猫", 8));这里直接会报编译错误 System.out.println("===使用泛型===="); for (Dog dog : arrayList) { System.out.println(dog.getName() + "-" + dog.getAge()); } } } /* 1.请编写程序,在ArrayList 中,添加3个Dog对象 2.Dog对象含有name 和 age, 并输出name 和 age (要求使用getXxx()) 3.老师使用泛型来完成代码 */ class Dog { private String name; private int age; public Dog(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class Cat { //Cat类 private String name; private int age; public Cat(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
-
总结
-
不使用泛型
-
加入-Dog—>取出-Object—>转换成Dog
-
不能对加入到集合中的数据类型进行约束(不安全)
-
遍历时,需要进行类型转换,如果集合中的数据量较大,对效率有影响
-
-
使用泛型后
-
Dog—>Dog—>Dog//加入和取出不需要类型转换
-
编译时,检查添加元素的类型,提高了安全性
-
减少了类型转换的次数,提高效率
-
-
二、基本介绍
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,即给类型指定一个参数,然后在使用时再指定此参数具体的值,那样这个类型就可以在使用时决定了。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
2.1语法
-
泛型的声明
interface 接口名
{} 和 class 类名 <K,V>{} 注:1)T,K,V不代表值,而是表示类型
2)任意字母都可以。常用T(Type)表示
-
泛型的实例化
要在类名的后面指定类型参数的值(也即类型)。如:
List<String> strList =new ArrayList<String>();
-
泛型的注意事项和使用细节
-
泛型
只能是引用类型 -
在给泛型指定具体类型后,可以传入该类型或者其子类型
-
泛型使用形式
List<Integer> list =new ArrayList<>();//后面的尖括号中内容可以省略 //如果都不写默认为Object类型
-
示例
package com.hspedu.generic; import java.util.ArrayList; import java.util.List; @SuppressWarnings({"all"}) public class GenericDetail { public static void main(String[] args) { //1.给泛型指向数据类型是,要求是引用类型,不能是基本数据类型 List<Integer> list = new ArrayList<Integer>(); //OK //List<int> list2 = new ArrayList<int>();//错误 //2. 说明 //因为 E 指定了 A 类型, 构造器传入了 new A() //在给泛型指定具体类型后,可以传入该类型或者其子类类型 Pig<A> aPig = new Pig<A>(new A()); aPig.f(); Pig<A> aPig2 = new Pig<A>(new B()); aPig2.f(); ArrayList<A> arrayList1 = new ArrayList<>(); arrayList1.add(new A()); arrayList1.add(new B()); //arrayList1.add("sss");因为指定了A类为类型,所以字符串是传不进去的 //3. 泛型的使用形式 ArrayList<Integer> list1 = new ArrayList<Integer>(); List<Integer> list2 = new ArrayList<Integer>(); //在实际开发中,我们往往简写 //编译器会进行类型推断, 老师推荐使用下面写法 ArrayList<Integer> list3 = new ArrayList<>(); List<Integer> list4 = new ArrayList<>(); ArrayList<Pig> pigs = new ArrayList<>(); //4. 如果是这样写 泛型默认是 Object ArrayList arrayList = new ArrayList();//等价 ArrayList<Object> arrayList = new ArrayList<Object>(); Tiger tiger = new Tiger(); /* class Tiger {//类 Object e; public Tiger() {} public Tiger(Object e) { this.e = e; } } */ } } class Tiger<E> {//类 E e; public Tiger() {} public Tiger(E e) { this.e = e; } } class A {} class B extends A {} class Pig<E> {// E e; public Pig(E e) { this.e = e; } public void f() { System.out.println(e.getClass()); //运行类型 } }
-
三、自定义泛型
3.1自定义泛型类
-
语法
class 类名<T,R,...>{//...表示可以有多个泛型
}
-
细节
- 普通成员(属性、方法)可以使用泛型
- 使用泛型的数组不能被初始化
- 静态方法中不能使用类的泛型
- 泛型类的类型是在创建对象时确定的(因为创建对象时,需要确定类型)
-
示例:
package com.hspedu.customgeneric; import java.util.Arrays; @SuppressWarnings({"all"}) public class CustomGeneric_ { public static void main(String[] args) { //T=Double, R=String, M=Integer Tiger<Double,String,Integer> g = new Tiger<>("john"); g.setT(10.9); //OK //g.setT("yy"); //错误,类型不对 System.out.println(g); Tiger g2 = new Tiger("john~~");//OK T=Object R=Object M=Object g2.setT("yy"); //OK ,因为 T=Object "yy"=String 是Object子类 System.out.println("g2=" + g2); } } //1. Tiger 后面泛型,所以我们把 Tiger 就称为自定义泛型类 //2, T, R, M 泛型的标识符, 一般是单个大写字母 //3. 泛型标识符可以有多个. //4. 普通成员可以使用泛型 (属性、方法) //5. 使用泛型的数组,不能初始化 //6. 静态方法中不能使用类的泛型 class Tiger<T, R, M> { String name; R r; //属性使用到泛型 M m; T t; //因为数组在new 不能确定T的类型,就无法在内存开空间 T[] ts; public Tiger(String name) { this.name = name; } public Tiger(R r, M m, T t) {//构造器使用泛型 this.r = r; this.m = m; this.t = t; } public Tiger(String name, R r, M m, T t) {//构造器使用泛型 this.name = name; this.r = r; this.m = m; this.t = t; } //因为静态是和类相关的,在类加载时,对象还没有创建 //所以,如果静态方法和静态属性使用了泛型,JVM就无法完成初始化 // static R r2; // public static void m1(M m) { // // } //方法使用泛型 public String getName() { return name; } public void setName(String name) { this.name = name; } public R getR() { return r; } public void setR(R r) {//方法使用到泛型 this.r = r; } public M getM() {//返回类型可以使用泛型. return m; } public void setM(M m) { this.m = m; } public T getT() { return t; } public void setT(T t) { this.t = t; } @Override public String toString() { return "Tiger{" + "name='" + name + '\'' + ", r=" + r + ", m=" + m + ", t=" + t + ", ts=" + Arrays.toString(ts) + '}'; } }
3.2自定义接口泛型
-
语法
interface 接口名<T,R,...>{
}
-
细节
- 接口中,静态成员也不能使用泛型(接口中属性默认为public static final)
- 泛型接口的类型,在继承接口或者实现接口时确定
-
示例:
package com.hspedu.customgeneric; public class CustomInterfaceGeneric { public static void main(String[] args) { } } /** * 泛型接口使用的说明 * 1. 接口中,静态成员也不能使用泛型 * 2. 泛型接口的类型, 在继承接口或者实现接口时确定 * 3. 没有指定类型,默认为Object */ //在继承接口 指定泛型接口的类型 interface IA extends IUsb<String, Double> { } //当我们去实现IA接口时,因为IA在继承IUsu 接口时,指定了U 为String R为Double //,在实现IUsu接口的方法时,使用String替换U, 是Double替换R class AA implements IA { @Override public Double get(String s) { return null; } @Override public void hi(Double aDouble) { } @Override public void run(Double r1, Double r2, String u1, String u2) { } } //实现接口时,直接指定泛型接口的类型 //给U 指定Integer 给 R 指定了 Float //所以,当我们实现IUsb方法时,会使用Integer替换U, 使用Float替换R class BB implements IUsb<Integer, Float> { @Override public Float get(Integer integer) { return null; } @Override public void hi(Float aFloat) { } @Override public void run(Float r1, Float r2, Integer u1, Integer u2) { } } //没有指定类型,默认为Object //建议直接写成 IUsb<Object,Object> class CC implements IUsb { //等价 class CC implements IUsb<Object,Object> { @Override public Object get(Object o) { return null; } @Override public void hi(Object o) { } @Override public void run(Object r1, Object r2, Object u1, Object u2) { } } interface IUsb<U, R> { int n = 10; //U name; 不能这样使用 //普通方法中,可以使用接口泛型 R get(U u); void hi(R r); void run(R r1, R r2, U u1, U u2); //在jdk8 中,可以在接口中,使用默认方法, 也是可以使用泛型 default R method(U u) { return null; } }
3.3自定义泛型方法
-
语法
修饰符 <T,R,...>返回类型 方法名(参数列表){
}
-
细节
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中
- 当方法被调用时,类型会确定
- public void eat(E e){},修饰符后面没有<T,R,...>eat 方法不是泛型方法,而是使用了泛型
-
示例:
package com.hspedu.customgeneric; /** * @author 韩顺平 * @version 1.0 */ public class CustomMethodGenericExercise { public static void main(String[] args) { //T->String, R->Integer, M->Double Apple<String, Integer, Double> apple = new Apple<>(); apple.fly(10);//10 会被自动装箱 Integer10, 输出Integer apple.fly(new Dog());//Dog } } class Apple<T, R, M> {//自定义泛型类 public <E> void fly(E e) { //泛型方法 System.out.println(e.getClass().getSimpleName()); } //public void eat(U u) {}//错误,因为U没有声明 public void run(M m) { } //ok } class Dog { }
3.4泛型的继承和通配符
-
泛型不具备继承性
List<Object> list = new ArrayList<String>();//不对
- :支持任意泛型类型
- :支持A类以及A类的子类,规定了泛型的上限
- :支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
-
示例:
package com.hspedu; import java.util.ArrayList; import java.util.List; public class GenericExtends { public static void main(String[] args) { Object o = new String("xx"); //泛型没有继承性 //List<Object> list = new ArrayList<String>(); //举例说明下面三个方法的使用 List<Object> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); List<AA> list3 = new ArrayList<>(); List<BB> list4 = new ArrayList<>(); List<CC> list5 = new ArrayList<>(); //如果是 List<?> c ,可以接受任意的泛型类型 printCollection1(list1); printCollection1(list2); printCollection1(list3); printCollection1(list4); printCollection1(list5); //List<? extends AA> c: 表示 上限,可以接受 AA或者AA子类 // printCollection2(list1);//× // printCollection2(list2);//× printCollection2(list3);//√ printCollection2(list4);//√ printCollection2(list5);//√ //List<? super AA> c: 支持AA类以及AA类的父类,不限于直接父类 printCollection3(list1);//√ //printCollection3(list2);//× printCollection3(list3);//√ //printCollection3(list4);//× //printCollection3(list5);//× } // ? extends AA 表示 上限,可以接受 AA或者AA子类 public static void printCollection2(List<? extends AA> c) { for (Object object : c) { System.out.println(object); } } //说明: List<?> 表示 任意的泛型类型都可以接受 public static void printCollection1(List<?> c) { for (Object object : c) { // 通配符,取出时,就是Object System.out.println(object); } } // ? super 子类类名AA:支持AA类以及AA类的父类,不限于直接父类, //规定了泛型的下限 public static void printCollection3(List<? super AA> c) { for (Object object : c) { System.out.println(object); } } } class AA { } class BB extends AA { } class CC extends BB { }