泛型的概述
- 泛型就是一个标签:
<数据类型>
- 泛型可以在编译阶段约束只能操作某种数据类型。
- 注意:1.jdk1.7开始之后,泛型后面的声明可以省略不写。
2.泛型和集合都只能支持引用数据类型,不支持基本数据类型。
比如< >里面用int就会报错,而用Integer则不会。 - 案例:
代码:
public class TestDemo01 {
public static void main(String[] args) {
//未加泛型
ArrayList list1 = new ArrayList();
list1.add(123);
list1.add("Java");
list1.add(9.99);
list1.add('a');
System.out.println(list1);
//规定了泛型
ArrayList<String> list2 = new ArrayList<String>();
list2.add("111");
list2.add("hello");
list2.add("a");
System.out.println(list2);
}
}
运行结果:
- 使用泛型的好处:
泛型在编译阶段约束了操作的数据类型,从而不会出现类型转换异常。
自定义泛型类
- 泛型类概念:
使用了泛型定义的类就是泛型类。 - 泛型类的格式:
修饰符 class 类名<泛型变量>{
}
泛型变量建议使用E、T、K、V
- 需求:模拟ArrayList集合自定义一个MyArrayList集合。
- 泛型类的核心思想:是把出现泛型变量的地方全部替换成传旭的真实数据类型。
- 案例:
public class TestDemo02 {
public static void main(String[] args) {
MyArrayList<String> mylist = new MyArrayList<>();
mylist.add("Java");
mylist.add("mysql");
mylist.remove("Java");
}
}
class MyArrayList<E>{
public void add(E e){
//...
}
public void remove(E e){
//...
}
}
自定义泛型方法
- 泛型方法的概念
定义了泛型的方法。 - 泛型方法的定义格式:
修饰符 <泛型变量> 返回值类型方法名称(形参列表){
}
- 注意:方法定义了是什么泛型变量,后面就只能用什么泛型变量。
- 泛型方法的核心思想:把出现泛型变量的地方全部替换成传输的真实数据类型。
- 需求:给你任何一个类型的数组,都能返回它的内容。
- 案例:
代码:
public class TestDemo03 {
public static void main(String[] args) {
Integer[] nums = {10, 20, 30, 40, 50};
System.out.println(arrToString(nums));
String[] names = {"张三", "李四", "王二麻"};
System.out.println(arrToString(names));
}
public static <T> String arrToString(T[] list){
StringBuilder sb = new StringBuilder();
sb.append("[");
if(list!=null && list.length > 0){
for(int i = 0;i < list.length;i++){
T ele = list[i];
sb.append(i==list.length-1?ele:ele+",");
}
}
sb.append("]");
return sb.toString();
}
}
运行结果:
- 结论:泛型类和泛型方法可以做通用技术架构。
泛型接口
- 泛型接口概念:
使用了泛型定义的接口。 - 泛型接口的格式:
修饰符 interface 接口名称 <泛型变量>{
}
- 需求:教务系统,提供一个接口可约束一定要完成数据(学生、老师)的增删改查操作。
代码:
public class TestDemo04 {
public static void main(String[] args) {
Student stu1 = new Student("kiki");
Student stu2 = new Student("jojo");
Student stu3 = new Student("lala");
StudentData studata = new StudentData();
studata.add(stu1);
studata.add(stu2);
studata.add(stu3);
studata.delete(stu2);
studata.print();
}
}
interface Data<E>{
void add(E e);
void delete(E e);
}
class Student{
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class StudentData implements Data<Student>{
private ArrayList<Student> list = new ArrayList<>();
@Override
public void add(Student student) {
list.add(student);
}
@Override
public void delete(Student student) {
list.remove(student);
}
public void print(){
StringBuilder sb = new StringBuilder();
sb.append("[");
if(list!=null && list.size()> 0){
for(int i = 0;i < list.size();i++){
Student ele = list.get(i);
sb.append(i==list.size()-1?ele.getName():ele.getName()+",");
}
}
sb.append("]");
System.out.println(sb);
}
}
运行结果:
- 结论:泛型接口的核心思想,在实现接口的时候传入真实的数据类型,这样重写的方法就是对该数据类型进行操作。
泛型通配符
- 通配符 :?
?可以在“使用”
泛型的时候代表一切类型
E、T、K、V是在“定义”
泛型的时候代表使用一切类型。 - 泛型的上下限
? extends Car
:那么?必须是Car或者其子类。(泛型的上限)
? super Car
:那么?必须是Car或者其父类。(泛型的下限) - 需求:开发一个极品飞车游戏,所有车都能一起参加比赛。
- 案例:
代码:
public class TestDemo05 {
public static void main(String[] args) {
ArrayList<BMW> bmws = new ArrayList<>();
bmws.add(new BMW());
bmws.add(new BMW());
run(bmws);
game(bmws);
ArrayList<Benz> benzs = new ArrayList<>();
benzs.add(new Benz());
benzs.add(new Benz());
run(benzs);
game(benzs);
ArrayList<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
dogs.add(new Dog());
run(dogs);
//Dog就进不来
//game(dogs);
}
public static void run(ArrayList<?> runners){ //加了?不管什么类型都能进
System.out.println("开始跑");
}
public static void game(ArrayList<? extends Car> cars){ //设置了上限
System.out.println("加入比赛");
}
}
class Car{
//...
}
class BMW extends Car{
//...
}
class Benz extends Car{
//...
}
class Dog{
//...
}
- 注意:虽然BMW和Benz都继承了Car,但是
ArrayList<BMW>
、ArrayList<Benz>
与ArrayList<Car>
是没有关系的。