定义类、接口、方法时,同时声明了一个或者多个类型变量(如:<E>) ,称为泛型类、泛型接口,泛型方法、它们统称为泛型。
作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常。
1.泛型类:
定义类的同时定义了泛型的类就是泛型类
泛型类的格式:
作用: 在编译阶段可以指定能操作的数据的类型
原理: 把出现泛型变量的地方全部替换成传输的真实数据类型。
代码演示1:
package com.itheima.day06.teacher.c_generics;
import java.util.ArrayList;
/**
集合用泛型的意义
集合是存储对象的容器。一般来说存储的同种类型的对象。
泛型:
未知的类型 定义的时候不知道具体的类型。
定义集合的时候,是不知道具体的集合对象存储什么类型对象的。
所以集合采用泛型 来表示 未知的类型。
你可以将泛型理解成一个占位符。
使用集合的时候就需要确定 集合存储什么具体的类型了。
确定之后 所有用到泛型的位置都会变成 具体的类型
泛型 在定义不确定,在使用的时候必须确定。
*/
public class GenericsDemo01 {
public static void main(String[] args) {
//这里是没用泛型一个类型定义一个集合
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
// list.add(12); //泛型限定传输的类型
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(12);
ArrayList list3 = new ArrayList();
// Object
list3.add("aaa");
list3.add(123);
list3.add("123");
for (int i = 0; i < list3.size(); i++) {
Object o = list3.get(i);
String s = (String) o;
}
}
}
--------------
package com.itheima.day06.teacher.c_generics;
/**
这里是用泛型之后
*/
public class NBA<MVP>{ //泛型定义在类上
//作用 在类中 都可以去使用这个未知的类型
private MVP mvp ;//MVP泛型类型 定义的时候不具体
public MVP getMvp(){//可以当成返回值类
return mvp;
}
public void setMvp(MVP mvp) {//可以作为参数类型
this.mvp = mvp;
}
}
------
package com.itheima.day06.teacher.c_generics;
/**
* 测试类
*/
public class Demo01 {
public static void main(String[] args) {
//创建一个NBA对象
NBA<String> nba = new NBA<>();
nba.setMvp("恩比德");
String mvp = nba.getMvp();
System.out.println(mvp);
NBA<Integer> nba2 = new NBA<>();
nba2.setMvp(77);
System.out.println(nba2.getMvp());
}
}
-----------
代码演示2:
package com.itheima.day06.teacher.d_generics;
/*
泛型只能是引用类型
*/
public class MyArrayList<E>{
//定义底层数组变量
private Object[] array = new Object[10];//定义初始容量
//定义一个数组的初始索引
private int index = 0;
/*
添加元素 add(E )
*/
public void add(E e){
array[index] = e;
index++;
}
/*
根据索引获取元素
*/
public E get(int index){
return (E)array[index];
}
}
-------------
package com.itheima.day06.teacher.d_generics;
/**
* 测试类
*/
public class Demo {
public static void main(String[] args) {
MyArrayList<String> list = new MyArrayList<>();
list.add("霍霍霍");
list.add("魁魁魁");
String s = list.get(0);
System.out.println(s);
}
}
2.泛型接口
使用了泛型定义的接口就是泛型接口
格式:修饰符 interface 接口名称<泛型变量>{}
作用:泛型接口可以让实现类选择当前功能需要操作的数据类型
原理:实现类可以在实现接口的时候传入自己操作的数据类型,这样重写的方法都将是针对于该类型的操作。
代码演示:
package com.itheima.day06.teacher.e_generics;
/**
泛型接口
*/
public interface MyHoliday<X>{
//泛型定义在接口 规范方法的使用
void eat(X x);
}
-------------
package com.itheima.day06.teacher.e_generics;
public class Student implements MyHoliday<String>{
@Override
public void eat(String s) {
System.out.println("吃烧烤...吃棒棒糖 bulubiu");
}
}
------------
package com.itheima.day06.teacher.e_generics;
import java.util.Comparator;
/**
泛型类也可以实现泛型接口
*/
public class Teacher<X> implements MyHoliday<X>{
@Override
public void eat(X x) {
}
}
3.泛型方法
定义方法时同时定义了泛型的方法就是泛型方法
格式:
作用:方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性。
代码演示:
package com.itheima.day06.teacher.f_generics;
/**
泛型方法
*/
public class GenericsDemo {
public static void main(String[] args) {
String aaa = test("aaa");
Integer test = test(12);
Double test1 = test(1.23);
}
public static <T> T test(T t){
return t;
}
}
---------------
package com.itheima.day06.teacher.f_generics;
/**
泛型类里定义泛型方法
*/
public class Sugar<E> {
private E e;
public E getE(){
return e;
}
public <T> void show(T t){//泛型定义在方法上
}
}
4.泛型通配符、上下限
? 可以在“使用泛型”的时候代表一切类型。
E T K V 是在定义泛型的时候使用的。
泛型的上下限:
1. ? extends Car: ?必须是Car或者其子类 泛型上限
2. ? super Car : ?必须是Car或者其父类 泛型下限
代码演示:
package com.itheima.day06.teacher.f_generics;
import java.util.ArrayList;
/*
定义几个类
*/
class Car extends Object {
}
class BENZ extends Car {
}
class ThreeBengZi extends Car {
}
public class Test {
public static void main(String[] args) {
ArrayList<BENZ> list1 = new ArrayList<>();
ArrayList<ThreeBengZi> list2 = new ArrayList<>();
ArrayList<Car> list3 = new ArrayList<>();
ArrayList<Object> list4 = new ArrayList<>();
test1(list1);
test1(list2);
test1(list3);
test1(list4);
test2(list1);
test2(list2);
test2(list3);
test2(list4);
System.out.println("===================");
System.out.println("= 只能车及其子类 可以接收========");
test3(list1);// ArrayList<BENZ> list1 = new ArrayList<>();
test3(list2);// ArrayList<ThreeBengZi> list2 = new ArrayList<>();
test3(list3);// ArrayList<Car> list3 = new ArrayList<>();
// ArrayList<Car> list3 = new ArrayList<Car>();
// 如果类型有泛型 类型<泛型> 整体是一个类型!!
// List<Car> ArrayList<Car>
// test3(list4);
// <? extends Car> 泛型的类型是Car或及其子类!
// <? super Car> 泛型的类型是Car或及其父类!
System.out.println("= 只能车及其父类 可以接收========");
// test4(list1);
// test4(list2);
test4(list3);
test4(list4);
}
/*
设计一个方法 可以接收 各种各样的集合
*/
public static <T> void test1(ArrayList<T> list) {
System.out.println("测试");
}
/*
? 通配符接收带有泛型集合
*/
public static void test2(ArrayList<?> list) {
System.out.println("测试");
}
public static void test3(ArrayList<? extends Car> list) {
System.out.println("限制泛型的类型!!!!!");
}
public static void test4(ArrayList<? super Car> list) {
System.out.println("限制泛型的类型!!!!!");
}
}
最后注意事项:
泛型的注意事项:擦除问题、基本数据类型问题
1.泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。(大白话就是执行后生成的class文件,不在是你定义的字母,而是你传入的实际类型)
2.泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。