集合它可以存放任意对象,当把对象存储到集合后,他们都会被提升成Object类型。然后我们再取出每一个对象并且进行相应操作时,必须采用强制类型转换。
public class Demo01Generic {
public static void main(String[] args) {
// 创建一个ArrayList集合对象,指定存储数据的类型为String
ArrayList<String> list = new ArrayList<>();
list.add("aa");
list.add("bbb");
list.add("cccc");
// 将运行时异常,提前到了编译时期,降低了程序员的工作量
//list.add(1000); //只能存String类型数据
// 使用增强for进行遍历
for (String str : list) {
System.out.println(str + "的长度: " + str.length());
}
System.out.println("===================");
// 创建集合,不指定存储数据的类型
// 默认按照Object类型处理
ArrayList list2 = new ArrayList();
list2.add("aa");
list2.add("bbb");
list2.add("cccc");
// 可以存
// 但是取出来进行强制类型转换,报出类型转换异常
list2.add(2022);
// 使用增强for进行遍历
for (Object obj : list2) {
// 因为创建ArrayList集合对象时,并没有指定存储数据的类型(泛型),
// 所以内部存储的所有内容均被当做Object类型处理
// 必须做强制类型转换(向下转型),存在安全隐患:类型转换异常 ClassCastException
String str = (String) obj;
System.out.println(obj + "的长度: " + str.length());
}
}
}
为什么会报错ClassCastException?因为上述的ArrayList集合只能存储同一类型对象(例如list2存储的都是字符串对象),当取出String类型数据时需要强转,即Object强转成String,又因为元素2022它强转后是Integer而不是String类型,所以把Integer类型的数据赋值给String类型后就会报ClassCastException类型转换异常。解决方案:使用泛型来约束集合存储指定类型数据。
什么是泛型?
泛型是程序设计语言的一种风格或范式。
泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型(相当于占位符,可以被预先使用),在实例化时作为参数指明这些类型(实现代码的模板化,把数据类型当做参数传递)。其目的是加强类型安全以及减少类型转换的次数。
泛型是 JDK1.5之后增加的,允许在定义类和接口以及方法时使用类型参数(Type Parameter)。泛型它可以帮助我们建立类型安全的集合。提高了代码复用性和安全性。
使用泛型的好处?
泛型类、泛型接口、泛型方法。
如何定义泛型类:在类的声明处添加泛型变量即可。
泛型变量一般用大写字母表示 , 如 T(Type),E(Element),K(Key),V(Value)
。
如果一次要声明多个泛型变量,中间用逗号,
分割即可。例如
。
格式如下:
public class 类名<泛型变量>{
}
//例如
public class MyClass<T> {
}
示例:泛型类的使用
// 定义一个泛型类
public class MyClass<T> {
// 定义成员变量
private T t;
// 无参构造器
public MyClass() {
}
// 有参构造器
public MyClass(T t) {
this.t = t;
}
// set/get方法
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
// toString方法
@Override
public String toString() {
return "MyClass{" +
"t=" + t +
'}';
}
}
测试类:
public class Demo02Generic {
public static void main(String[] args) {
// 使用无参构造器创建对象,并指定泛型
MyClass<String> mc = new MyClass<>();
// 使用set方法为成员变量赋值
mc.setT("helloworld");
System.out.println(mc); //MyClass{t=helloworld}
// 使用get方法取值
String str = mc.getT(); //因为创建对象的时候传了泛型,所以返回值类型就是Stirng类型
System.out.println(str); //helloworld
// 使用有参构造器创建对象
MyClass<Integer> mc02 = new MyClass<>(100);//发生自动装箱操作
//错误示范: 左侧<>中指定了Integer类型,有参构造右侧只能传递Integer数据,不能传递字符串
//MyClass03 mc02 = new MyClass03<>("100");
//调用toString方法获取数据
String s2 = mc02.toString();
System.out.println(s2); //MyClass{t=100}
}
}
泛型方法可以是普通方法、静态方法和构造器方法。
泛型方法定义格式:
修饰符 <泛型变量> 返回值类型 方法名(参数类型 参数列表){
}
//例如Collections的两个泛型方法
public <T> T[] toArray(T[] a){
return s.toArray(a);
}
public static <T> boolean addAll(Collection<? super T> c, T... elements) {
boolean result = false;
for (T element : elements)
result |= c.add(element);
return result;
}
示例:泛型方法的使用
public class MyClass<T> {
/*
这种定义方法不叫泛型方法(因为它使用类上定义的泛型)
*/
public void method(T t){
System.out.println(t);
}
//定义非静态泛型方法
//泛型方法: 该泛型只属于当前方法使用
public <E> void show(E e) {
System.out.println(e);
}
/*
错误示范:
静态方法必须使用类名直接调用,和对象无关
但是类上的泛型,必须创建对象后才能确定具体的类型
然而静态方法和对象无关,调用时根本没有对象,就没有泛型
总结:
静态方法,不能使用类上定义的泛型,只能使用方法中的泛型
*/
/*public static void fun(T t){
System.out.println(t);
}*/
//定义静态泛型方法
public static <K> void test(K k) {
System.out.println(k);
}
}
public class Demo01GenericMethod {
public static void main(String[] args) {
MyClass<String> mc01 = new MyClass<>();
mc01.method("hello");
/*
错误示范:
method方法使用类上的泛型已经被确定为了String,
就不能传递String以外的类型
*/
//mc01.method(100);
/*
正确写法:
show方法上有自己的泛型,
根据调用方法传递的参数的类型,来确定方法上泛型的类型
*/
mc01.show("World");
mc01.show(100);
mc01.show(new Student("zs", 18));
/*
正确写法:
test方法上有自己的泛型,
根据调用方法传递的参数的类型,来确定方法上泛型的类型
*/
MyClass.test("Java");
MyClass.test(200);
MyClass.test(new Student("ls",38));
}
}
泛型接口定义格式如下:
public interface 接口名称<泛型变量> {
}
//例如
public interface Iterable<T> {}
public interface Collection<E> extends Iterable<E> {}
public interface List<E> extends Collection<E> {}
public interface Set<E> extends Collection<E> {}
public interface Map<K,V> {}
示例:泛型接口
public interface MyInter<T> {
// 抽象方法
/*public abstract */void method(T t);
}
实现类:MyInterImplA
/*
已经确定接口要传的类型 String
*/
public class MyInterImplA implements MyInter<String> {
@Override
public void method(String str) {
System.out.println(str);
}
}
实现类:MyInterImplB
/*
不确定接口要传的类型
把实现类定义成泛型类,在实现接口时,就可以使用类上定义的泛型啦QAQ
*/
public class MyInterImplB<T> implements MyInter<T> {
@Override
public void method(T t) {
System.out.println(t);
}
}
测试类:
public class MyTest {
public static void main(String[] args) {
MyInterImplA mia = new MyInterImplA();
mia.method("Hello");
MyInterImplB<String> mib = new MyInterImplB<>(); //指定泛型
mib.method("World");
MyInterImplB<Student> mib2 = new MyInterImplB<>();//指定泛型
mib2.method(new Student("zs",18));
}
}
泛型通配符: ?
可以理解成占位符,用来匹配泛型的,但是不能使用?
定义泛型。
主要应用场景: 定义参数/变量时,不能确定变量的具体类型时,使用?
代替。
示例1:
public class Demo01TongPeiFu {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("CCC");
Set<Integer> set = new HashSet<>();
set.add(111);
set.add(222);
set.add(333);
//调用方法
print(list);
print(set);
System.out.println("-------------");
//调用方法
print2(list);
print2(set);
}
/**
* 定义一个方法,完成以上两个集合的遍历
* 泛型通配符: ? 就是一个占位符, 用来匹配泛型的,但是不能使用?定义泛型。
* @param coll
*/
public static void print2(Collection<?> coll) {
for (Object o : coll) {
System.out.println(o);
}
}
/*
定义一个方法,完成以上两个集合的遍历
目前: 把print方法定义成了泛型方法
方法参数:
使用Collection接口定义变量,Collection接口在定义时是有泛型的,
使用Collection接口定义变量,需要指定泛型
泛型是不存在的多态的
此处:
print2方法是一个泛型方法
*/
public static <T> void print(Collection<T> coll) {
for (T t : coll) {
System.out.println(t);
}
}
}
示例2:
public class Demo02TongPeiFuNotice {
public static void main(String[] args) {
List<Object> list1 = new ArrayList<>();//右侧<>中省略不写,就是Object
List<Object> list2 = new ArrayList<Object>();//右侧<>中写Object也是可以的
//List
//List
//List
List list6 = new ArrayList(); //不加泛型默认是Object类型
//?通配符: 用来匹配泛型的,不能用来定义泛型
//主要的应用场景: 定义参数/变量时,不能确定变量的具体类型时,使用?代替
//?: 代表任意一种引用类型
List<?> list;
list = new ArrayList<>();//<>不写数据类型代表Object
list = new ArrayList<String>(); //String
list = new ArrayList<Integer>(); //Integer
list = new ArrayList<Student>(); //Student
/*
注意事项:
1.泛型是不存在多态的,左侧<>中写的类型必须和右侧<>中的类型保持一致(可以省略右侧<>中的内容)
2.使用泛型通配符,定义变量:
List> list 可以接收哪些对象?
只要是List接口实现类的任意泛型对象就可以(创建对象时,只要在<>中写上一种引用类型就行)
3.List> list: 理解为它是各种泛型List集合对象的父类
*/
}
}
泛型通配符的限制
一个父类的子类可以有任意多个,那如何表示出一个父类的任意子类呢?(找儿子)
泛型的上限:
// ?: 代表的是任意一种引用类型
? extends Person: 表示Person类型或者Person类型的任意子类型
? extends E: 表示E类型或者E类型的任意子类型
示例:泛型上限的使用
//Person类
public class Person {
private String name;
private int age;
//生成空参,满参构造,set和get方法
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", 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;
}
}
//Worker类 工人
public class Worker extends Person {
//根据父类生成空参,满参构造
public Worker() {
}
public Worker(String name, int age) {
super(name, age);
}
@Override
public String toString() {
return "Worker{" + "name='" + getName() + '\'' + ", age=" + getAge() + '}';
}
}
//Teacher类
public class Teacher extends Worker {
//根据父类生成空参,满参构造
public Teacher() {
}
public Teacher(String name, int age) {
super(name, age);
}
@Override
public String toString() {
return "Teacher{" + "name='" + getName() + '\'' + ", age=" + getAge() + '}';
}
}
//JavaTeacher类
public class JavaTeacher extends Teacher {
public JavaTeacher() {
}
//根据父类生成空参,满参构造
public JavaTeacher(String name, int age) {
super(name, age);
}
@Override
public String toString() {
return "JavaTeacher{" + "name='" + getName() + '\'' + ", age=" + getAge() + '}';
}
}
测试类:
@SuppressWarnings("all") //抑制警告
public class Demo01GenericShangXian {
public static void main(String[] args) {
ArrayList<Person> list1 = new ArrayList<>();
list1.add(new Person("zs", 18));
list1.add(new Person("ls", 28));
list1.add(new Person("ww", 38));
ArrayList<Worker> list2 = new ArrayList<>();
list2.add(new Worker("zs01", 18));
list2.add(new Worker("ls01", 28));
list2.add(new Worker("ww01", 38));
ArrayList<Teacher> list3 = new ArrayList<>();
list3.add(new Teacher("zs02", 18));
list3.add(new Teacher("ls02", 28));
list3.add(new Teacher("ww02", 38));
ArrayList<String> list4 = new ArrayList<>();
list4.add("aaa");
list4.add("bbb");
ArrayList<Integer> list5 = new ArrayList<>();
list5.add(100);
list5.add(200);
print(list1);
print(list2);
print(list3);
// print(list4); //错误:String不是Person的子类
// print(list5); //错误:Integer不是Person的子类
}
/**
* 定义一个方法,只能完成以下3个集合的遍历
* ArrayList、ArrayList、ArrayList
* Worker是Person的子类,Teacher也是Person的子类
* ? extends Person: 代表Person类型或者Person类型的任意子类类型
* @param list
*/
public static void print(ArrayList<? extends Person> list) {
for (Person person : list) {
System.out.println(person);
}
System.out.println("---------------");
}
}
一个子类的父类可以有任意多个,如何表示一个子类的任意父类类型呢?(找爹)
泛型的上限:
// ?: 代表的是任意一种引用类型
? super JavaTeacher: 代表JavaTeacher类型或者JavaTeacher类型的任意父类类型
? super T: 代表T类型或者T类型的任意父类类型
示例:泛型下限的使用
//Person类
public class Person {
private String name;
private int age;
//生成空参,满参构造,set和get方法
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", 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;
}
}
//Worker类 工人
public class Worker extends Person {
//根据父类生成空参,满参构造
public Worker() {
}
public Worker(String name, int age) {
super(name, age);
}
@Override
public String toString() {
return "Worker{" + "name='" + getName() + '\'' + ", age=" + getAge() + '}';
}
}
//Teacher类
public class Teacher extends Worker {
//根据父类生成空参,满参构造
public Teacher() {
}
public Teacher(String name, int age) {
super(name, age);
}
@Override
public String toString() {
return "Teacher{" + "name='" + getName() + '\'' + ", age=" + getAge() + '}';
}
}
//JavaTeacher类
public class JavaTeacher extends Teacher {
public JavaTeacher() {
}
//根据父类生成空参,满参构造
public JavaTeacher(String name, int age) {
super(name, age);
}
@Override
public String toString() {
return "JavaTeacher{" + "name='" + getName() + '\'' + ", age=" + getAge() + '}';
}
}
测试类:
public class Demo02GenericXiaXian {
public static void main(String[] args) {
ArrayList<Person> list1 = new ArrayList<>();
list1.add(new Person("zs", 18));
list1.add(new Person("ls", 28));
list1.add(new Person("ww", 38));
ArrayList<Worker> list2 = new ArrayList<>();
list2.add(new Worker("zs01", 18));
list2.add(new Worker("ls01", 28));
list2.add(new Worker("ww01", 38));
ArrayList<Teacher> list3 = new ArrayList<>();
list3.add(new Teacher("zs02", 18));
list3.add(new Teacher("ls02", 28));
list3.add(new Teacher("ww02", 38));
ArrayList<String> list4 = new ArrayList<>();
list4.add("aaa");
list4.add("bbb");
ArrayList<Integer> list5 = new ArrayList<>();
list5.add(100);
list5.add(200);
ArrayList<JavaTeacher> list6 = new ArrayList<>();
list3.add(new JavaTeacher("zs02", 18));
list3.add(new JavaTeacher("ls02", 28));
list3.add(new JavaTeacher("ww02", 38));
print(list1);
print(list2);
print(list3);
// print(list4); //错误:String不是Teacher的父类
// print(list5); //错误:Integer不是Teacher的父类
// print(list6); //错误:JavaTeacher不是Teacher的父类
}
/**
* 使用泛型下限变量一下三个集合定义一个方法
* ArrayList list1、ArrayList list2 、ArrayList list3
*
* @param list
*/
public static void print(ArrayList<? super Teacher> list) {
for (Object o : list) {
System.out.println(o);
}
System.out.println("---------------");
}
}