Java SE面向对象--13.Java泛型
1、概述
在前面学习集合时,我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。
public class GenericDemo {
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add("abc");
coll.add("itcast");
coll.add(5);//由于集合没有做任何限定,任何类型都可以给其中存放
Iterator it = coll.iterator();
while(it.hasNext()){
//需要打印每个字符串的长度,就要把迭代出来的对象转成String类型
String str = (String) it.next();
System.out.println(str.length());
}
}
}
程序在运行时发生了问题java.lang.ClassCastException。怎么解决这个问题 呢、下面就来说说泛型
泛型:可以在类或方法中预支地使用未知的类型。用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。
好处:
-
将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
-
避免了类型强转的麻烦。
2、定义泛型类
格式:
修饰符 class 类名<代表泛型的变量> { }
// 自定义一个泛型类, 泛型的标志是 <>
// E (Element) T (Type) K (Key) V (Value) R (Result)
// 请问 : 泛型类中的泛型在何时进行确定 ??? 在实例化该对象时, 进行泛型真实类型的确定.
// 泛型类中泛型是为本类的属性和方法提供的. ArrayList<E>
public class GenericClass1<T> {
// 属性
private T field;
// 方法 :
public void setField(T field) {
this.field = field;
}
public T getField() {
return field;
}
}
演示代码:
public class TestGenericClass1 {
public static void main(String[] args) {
// 1. 创建一个泛型类对象
GenericClass1<String> g1 = new GenericClass1<>();
g1.setField("hello Java.");
String str = g1.getField();
System.out.println(str);
GenericClass1<Integer> g2 = new GenericClass1<>();
g2.setField(998);
Integer number = g2.getField();
System.out.println(number);
}
}
3、定义泛型接口
格式:
修饰符 interface接口名<代表泛型的变量> { }
// 定义了一个泛型接口 : 类可以在实例化同时确定类上的泛型, 而接口不能实例化, 何时确定泛型呢 ??? 实现类来确定.
public interface MyGenericInterface<T> {
// 抽象方法
void method(T t);
}
演示代码:
接口代码一:
// 确定接口中泛型的第一种方式, 实现类在实现该接口的同时进行确定.
public class MyGenericInterfaceImpl1 implements MyGenericInterface<String> {
@Override
public void method(String s) {
System.out.println(s);
}
}
接口代码二:
// 确定接口中泛型的第二种方式, 当实现类实现该接口时, 不确定该接口的泛型, 此时, 该类必须定义为 `泛型类`.
// 泛型类在实例化对象时, 就可以确定泛型的真实类型, 此时, 该类型就可以被传递接口的泛型中, 从而来确定接口的泛型.
public class MyGenericInterfaceImpl2<T> implements MyGenericInterface<T> {
@Override
public void method(T t) {
System.out.println(t);
}
}
测试类:
public class TestMyGenericInterface {
public static void main(String[] args) {
// 1. 实现类 MyGenericInterfaceImpl1
MyGenericInterfaceImpl1 g1 = new MyGenericInterfaceImpl1();
g1.method("你好, 上海.");
// ArrayList<E> 实现了 List<E> 接口.
// 2. 实现类 MyGenericInterfaceImpl2
MyGenericInterfaceImpl2<String> g2 = new MyGenericInterfaceImpl2<>();
g2.method("hello ShangHai.");
MyGenericInterfaceImpl2<Integer> g3 = new MyGenericInterfaceImpl2<>();
g3.method(998);
}
}
4、定义泛型方法
格式:
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
代码:
public class GenericMethodTest {
public static void main(String[] args) {
printInfo(new Person("张三", 18));
printInfo(new Animal("哮天犬", 38));
}
// 定义一个方法 : 输出 `对象信息` (Person, Animal) -> Object
// 泛型必须先定义, 后使用. 泛型使用 <> 尖括号定义. 位置: 修饰符 <T> 返回值类型 方法名(参数列表) { 方法体 }
public static <T> void printInfo(T t) {
System.out.println(t);
}
}
5、泛型通配符:?
说明:当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
使用场景 : 必须在已经定义的接口或类上的泛型位置才能使用.
演示:
public class GenericMethodTest {
public static void main(String[] args) {
ArrayList<Person> list1 = new ArrayList<>();
list1.add(new Person("张三", 18));
list1.add(new Person("李四", 19));
list1.add(new Person("王五", 20));
printCollection(list1);
ArrayList<Animal> list2 = new ArrayList<>();
list2.add(new Animal("哮天犬1号", 100));
list2.add(new Animal("哮天犬2号", 200));
printCollection(list2);
}
// 需求 : 定义一个方法, 输出集合中所有元素的信息.
// ? 泛型的通配符, 好处, 不要声明.
// 默认 ? extends Object
// 翻译 : ? 表示我不知道这是什么类型, 但是, 我知道, 它可以是 Object 类型, 或者该类型 继承 Object 类型.
public static void printCollection(Collection<?> c) {
for (Object obj : c){
System.out.println(obj);
}
}
/*public static <T> void printCollection(Collection<T> c) {
for (T t : c) {
System.out.println(t);
}
}*/
}
泛型上下限:
泛型的上限:
格式: 类型名称 <? extends 类 > 对象名称
意义: 只能接收该类型及其子类
泛型的下限:
格式: 类型名称 <? super 类 > 对象名称
意义: 只能接收该类型及其父类型
上限:
public class Test {
public static void main(String[] args) {
ArrayList<Person> list1 = new ArrayList<>();
printCollection(list1);
ArrayList<Student> list2 = new ArrayList<>();
printCollection(list2);
ArrayList<Teacher> list3 = new ArrayList<>();
printCollection(list3);
// ArrayList<Animal> list4 = new ArrayList<>();
// printCollection(list4);
}
// 定义一个方法 :
public static void printCollection(Collection<? extends Person> c) {
for (Person p : c) {
System.out.println(p);
}
}
}
下限:
public class Test {
public static void main(String[] args) {
ArrayList<ArmyDog> list1 = new ArrayList<>();
printCollection(list1);
ArrayList<Dog> list2 = new ArrayList<>();
printCollection(list2);
ArrayList<Animal> list3 = new ArrayList<>();
printCollection(list3);
/*
ArrayList<Cat> list4 = new ArrayList<>();
printCollection(list4);
ArrayList<Wolf> list5 = new ArrayList<>();
printCollection(list5);
*/
}
// 方法 :
public static void printCollection(Collection<? super ArmyDog> c) {
for (Object obj : c) {
System.out.println(obj);
}
}
}
总结:
注意 : 如果泛型不指定, 那么该泛型就会被提升为 Object 类型.
好处1 : 统一了集合中元素类型.
好处2 : 避免了类型强转问题.
好处3 : 将运行时期的类型转换异常, 提前到编译时期进行语法检查.