文章目录
- 泛型
- 为什么用泛型
- 泛型特性
- 泛型的擦出和补偿
- 自定义泛型
- 在类上自定义泛型
- 在方法上使用泛型
- 在接口上定义泛型
- 类型通配符
泛型
为什么用泛型
不用泛型
public class User {public static void main(String[] args) {//创建 arraylist 集合Collection arraylist = new ArrayList();//把 Person 对象假如 arraylistPerson p = new Person();arraylist.add(p);//用迭代器遍历调用 test 方法Iterator it = arraylist.iterator();while (it.hasNext()) {Object o = it.next();//这里非常麻烦,要向下转型,所以我们可以用泛型if (o instanceof Person) {Person temp = (Person) o;temp.test();}}}
}class Person {public void test() {System.out.println("调用 test 方法");}
}
使用泛型
public class User {public static void main(String[] args) {//创建 arraylist 集合Collection<Person> arraylist = new ArrayList<>();//把 Person 对象假如 arraylistPerson p = new Person();arraylist.add(p);//用迭代器遍历调用 test 方法Iterator<Person> it = arraylist.iterator();while (it.hasNext()) {//这里用了泛型所以代码更简便了,也不用向下转型了,并且编译会进行类型检查,更安全了Person temp = it.next();temp.test();}}
}class Person {public void test() {System.out.println("调用 test 方法");}
}
泛型特性
- 泛型是 java5 的新特性,属于编译阶段的功能
- 让开发者指定集合中存储的数据类型
//这里就表示只能存 String 类型数据,否则编译阶段报错
Arraylist<String> al = new ArrayList<String>();
作用:
- 类型安全:指定集合中元素类型后,编译器会进行类型检查,如果尝试将类型错误的元素添加到集合中,就会在编译时报错,避免了运行时出现的错误问题
- 代码简洁:避免频繁类型转换,因为不用泛型集合就默认返回Object
钻石表达式:
java7 的新特性
//后面<>里的 String 可以省略
Arraylist<string> a1 = new ArrayList();
泛型的擦出和补偿
擦除
- 泛型提高安全性,是编译阶段的技术,专门给编译器用的,加载类的时候,会把泛型擦除掉,擦成 Object 类型
- 擦除的本质是让 JDK1.4之前的 和 JDK1.5 能兼容同一个类加载器,1.5擦除后为 Object 和 1.4 之前就兼容了
补偿
- 擦除为 Object 类型,所以添加的元素就被转化为 Object 类型,同时取出的元素也默认为 Object 类型
- 这里有一个默认的操作:他帮你自动把 Object 强转成对应的类型,不用强转
自定义泛型
在类上自定义泛型
public class User {public static void main(String[] args) {//下面Person类所有 T 都替换为 StringPerson<String> p = new Person<>("张三");}
}//如果多个泛型就用 逗号 隔开
class Person<T1> {private T name;public Person(T name){this.name = name}public T getName() {return name;}public void setName(T name) {this.name = name;}
}
这个 T 是自定义的 名字可以随意取
在方法上使用泛型
class Person<R> { //这个 T 和 T 对应属于泛型方法,R 和 r 对应属于泛型类public <T> void method(T t, R r)
}
在类上定义的泛型,静态方法中无法使用,如果在静态方法中使用泛型则需要在返回值类型前进行泛型声明
class Person<T1> {public staic void setName(T name) {this.name = name;}
}
因为如果静态方法能使用,你用类名.方法 (Person.setName),直接调用,定义不了泛型直接寄了
如果要在静态上用泛型,就要使用自定义泛型方法
class Person {//提前定义好 T1//这个 T 是属于这个方法的泛型public staic <T> void setName(T name) {this.name = name;}
}
在接口上定义泛型
接口继承时定义泛型
interface IA extends IUsb<String, Double> {}
//当我们去实现IA接口时,因为IA在继承IUsu 接口时,指定了U 为String R为Double
//,在实现IUsu接口的方法时,使用String替换U, 是Double替换R
class AA implements IA {@Overridepublic Double get(String s) {return null;}@Overridepublic void hi(Double aDouble) {}@Overridepublic void run(Double r1, Double r2, String u1, String 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;}
}
实现接口时定义泛型
//实现接口时,直接指定泛型接口的类型
//给U 指定Integer 给 R 指定了 Float
//所以,当我们实现IUsb方法时,会使用Integer替换U, 使用Float替换R
class BB implements IUsb<Integer, Float> {@Overridepublic Float get(Integer integer) {return null;}@Overridepublic void hi(Float aFloat) {}@Overridepublic void run(Float r1, Float r2, Integer u1, Integer 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;}
}
没有指定接口
//没有指定类型,默认为Object
//建议直接写成 IUsb<Object,Object>
class CC implements IUsb { //等价 class CC implements IUsb<Object,Object> {@Overridepublic Object get(Object o) {return null;}@Overridepublic void hi(Object o) {}@Overridepublic 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;}
}
简单来说就是在 继承接口,或实现接口时指定泛型
接口中,静态成员也不能使用泛型 (接口中)
类型通配符
泛型不具备继承性
也就是 如果你是 Number 那就是 Number 不能是它的 子类
比如集合定义好了泛型,你不确定存声明类型,直接 ArrayList<?>
- 无限定通配符:<?>:可以为任意引用数据类型
- 上限通配符:<? extends Numbers>:必须为 Numbers 或者它的子类
- 下限通配符:<? super Numbers>:必须为 Numbers 或者它的父类
public class User {public static void main(String[] args) {//啥类型都可以User.test(new ArrayList<String>());User.test(new ArrayList<Double>());//只能是 Number 和 Number 的子类User.test2(new ArrayList<Double>());//只能是 Double 和 Double 的父类User.test3(new ArrayList<Number>());}public static void test(ArrayList<?> list){}public static void test2(ArrayList<? extends Number> list){}public static void test3(ArrayList<? super Double> list){}
}