问题背景
Java 集合有个缺点,把一个对象"丢进"集合里之后,集合就会"忘记"这个对象的数据类型,当再次取出该对象时 该对象的编译类型就变Object类型(其运行时类型没变),Java集合之所以被设计成这样,是因为集合的设计者不知道我们会用集合来保存什么类型的对象所以他们把集合设计成能保存任何类型的对象,只要求具有很好的通用性。但是,有可能使用过程中不符合预期,导致运行时报错。
import java.util.ArrayList;
import java.util.List; public class ProblemTest {
public static void main(String[] args) {
List persons = new ArrayList();
persons.add("李一桐");
persons.add("刘亦菲");
persons.add("鞠婧祎");
//不小心加入了一个int类型的数据
persons.add(10);
persons.forEach(person -> System.out.println(((String) person).length())); //报错int转换不成String报错
}
}
解决:
- 为要存储的元素/变量 灵活指定数据类型
1、泛型使用:
- 作用:<泛型>指定集合存储元素的类型
语法:
- 集合类<元素类型..> = new 集合类<>;
Set<String> persons = new HashSet<>();
Map<String, Integer> persons = new HashMap<>();
2、自定义泛型
语法:
自定义类名<泛型形参>{ ... }
Iterator对象定义泛型:
Map定义泛型:
我们就是在类名的后面加上类型的替代符,比如上面的T,K,V。我们在使用的时候,就直接把这些T,K,V替换成对应的类型名称就好了。
/**
* @ClassName MyDefineClassInitTypeExample
* @projectName: object1
* @author: Zhangmingda
* @description: 自定义泛型测试
* date: 2021/4/11.
*/
public class MyDefineClassInitTypeExample {
private static class Student<T>{
private T type; public Student(T type) {
this.type = type;
} public T getType() {
return type;
}
}
public static void main(String[] args) {
Student<String> student = new Student<>("高中");
System.out.println(student.getType());//高中
Student<Integer> student1 = new Student<>(202101);
System.out.println(student1.getType());//202101
}
}
3、从泛型类派生子类
语法:
自定义子类名<子类泛型形参> extends 父类名<泛型形参> {
子类构造方法(数据类型|子类泛型形参 形参){
super(形参)
}
}
当创建了带泛型申明的接口或父类之后,可以为该接口创建实现类或创建该类派生子类。子类必须指定父类的泛型具体类型,或者子类必须实现和父类相同的泛型
比如下面代码是不行的:
private static class ElementaryStudent extends Student<T>{
public ElementaryStudent(T type){
super(type);
}
}
但是我们可以指定具体父类泛型派生一个子类:
public class MyDefineClassInitTypeExample {
private static class Student<T>{
private T type; public Student(T type) {
this.type = type;
} public T getType() {
return type;
}
}
private static class ElementaryStudent extends Student<String>{
public ElementaryStudent(String type){
super(type);
}
} public static void main(String[] args) { ElementaryStudent elementaryStudent = new ElementaryStudent("小学");
System.out.println(elementaryStudent.getType()); //小学
}
}
定义一个子类的时候,无法指定父类泛型的具体类型:则
/**
* @ClassName MyDefineClassInitTypeExample
* @projectName: object1
* @author: Zhangmingda
* @description: 自定义泛型测试
* date: 2021/4/11.
*/
public class MyDefineClassInitTypeExample {
private static class Student<T>{
private T type; public Student(T type) {
this.type = type;
} public T getType() {
return type;
}
}
private static class ElementaryStudent<T> extends Student<T>{
public ElementaryStudent(T type){
super(type);
}
} public static void main(String[] args) { ElementaryStudent<Integer> elementaryStudent1 = new ElementaryStudent<>(202107);
System.out.println(elementaryStudent1.getType());
}
}
不存在泛型类:
- 用一个支持泛型的类,创建不同类型的实例,他们还是同一个类的实现。
ElementaryStudent<Integer> elementaryStudent1 = new ElementaryStudent<>(202107);
System.out.println(elementaryStudent1.getType());
ElementaryStudent<String> elementaryStudent2 = new ElementaryStudent<>("幼儿园");
System.out.println(elementaryStudent2.getType());
System.out.println(elementaryStudent1.getClass()); //class MyDefineClassInitTypeExample$ElementaryStudent
System.out.println(elementaryStudent2.getClass()); //class MyDefineClassInitTypeExample$ElementaryStudent
System.out.println(elementaryStudent.getClass().equals(elementaryStudent2.getClass()));//true
4、 泛型通配符?
问题:List<Object> 和 List<String> 什么关系,要求传入List<Object> 传入List<String> 行不行?
import java.util.ArrayList;
import java.util.List; public class ProblemTest {
private static void test(List<Object> list) {
for (Object o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
List<String> persons = new ArrayList<>();
persons.add("李 ");
persons.add("刘 ");
persons.add("祎");
test(persons); //不行,他们是同一类不同的实现,不是子类关系
}
}
解决:用通配符?替代泛型<Object>
import java.util.ArrayList;
import java.util.List; /**
* @ClassName wildcardExample
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/11.
*/
public class WildcardExample {
private static void test(List<?> list){
list.forEach(o -> System.out.println(o));
}
public static void main(String[] args) {
List<String> list = new ArrayList();
list.add("张三");
list.add("李四");
test(list);
/**
* 张三
* 李四
*/
}
}
上面的这个List,其中的?表示的就是一个通配符,表示可以匹配任何类型。在用的时候,通配符里面的元素,都是Object类型。
5、限定通配符适配范围
5.1 在List集合使用中限定示例代码:继承Animal类的子类都匹配
语法:
<?extends 类名>
本例如果直接指定List<Animal>泛型,Dog和Cat虽然是Animal的子类,但是List<Dog> List<Cat> 和<List Animal>之间并不是子类关系,List<Dog> 或List<Cat>不能匹配<List Animal>。
import java.util.ArrayList;
import java.util.List; /**
* @ClassName wildcardExample
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/11.
*/
public class WildcardLimitExample {
private static class Animal{
public void say(){
System.out.println("动物叫了..........");
}
}
private static class Dog extends Animal{
@Override
public void say() {
System.out.println("I am Dog : 汪汪汪");
}
}
private static class Cat extends Animal{
@Override
public void say() {
System.out.println("I am Cat : 喵喵喵!!!");
}
}
public static void animalSay(List<? extends Animal> animals){
animals.forEach(animal -> animal.say());
}
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
animals.add(new Dog());
animals.add(new Cat());
animalSay(animals);
/**
* I am Dog : 汪汪汪
* I am Cat : 喵喵喵!!!
*/
System.out.println("==================="); List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
animalSay(dogs);
List<Cat> cats = new ArrayList<>();
cats.add(new Cat());
animalSay(cats);
/**
* I am Dog : 汪汪汪
* I am Cat : 喵喵喵!!!
*/
}
}
5.2在类的定义过程中给泛型限定范围
语法:
修饰符... class<T extends 父类名 [& 接口名] >
- 形参T代表数据类型 extends 后面为T要继承或实现的接口
如下类class CustomerNumber<T extends Number & Serializable> 的意义为:
在用该类创建对象时候如果指定泛型,指定传入构造方法的数据类型T必须是 继承了Number类并且实现了Serializable接口的数据类型。
import java.io.Serializable;
import java.text.NumberFormat; /**
* @ClassName WildcardLimitCustomerNumberExample
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/11.
*/
public class WildcardLimitCustomerNumberExample {
private static class CustomerNumber<T extends Number & Serializable>{
private T info; public CustomerNumber(T info) {
this.info = info;
} public T getInfo() {
return info;
}
} public static void main(String[] args) {
CustomerNumber<Integer> customerNumber = new CustomerNumber<>(1);
System.out.println(customerNumber.getInfo());
}
}