存的时候什么类型都可以,但是要取的时候会懵,因为取出来啥也不是,或者不知道取的是什么类型的数据。像这样:
// 创建ArrayList集合,不使用泛型
ArrayList arrayList = new ArrayList();
// 往集合中添加元素
arrayList.add("老二");
arrayList.add("张三");
arrayList.add(10);
arrayList.add(1.1);
System.out.println(arrayList);
// 遍历集合中的元素
for(Object o : arrayList){
String str = (String) o;
System.out.println(str);
}
输出结果:
使用泛型在编译器里直接对类做了控制,只能存储泛型定义的数据
像这样:
结果:存储泛型指定以外的数据类型,编译都不会通过的
总结:
泛型:定义的时候表示一种未知的数据类型,在使用的时候确定其具体的数据类型
重点:
泛型的作用是在创建对象时,将未知的类型确定为具体的类型。
当没有指定泛型时,默认类型为Object类型
public class 类名称<泛型变量>{}
任意字母。一般都会写 E ,方便识别。
public class MyArrayList {
E e;
public E method(E e){
return e;
}
}
当类中的成员变量或者成员方法的 形参类型/返回值类型不确定的时候,就可以把该类型定义为含有泛型的累
MyArrayList list1 = new MyArrayList<>();
list1.e = "张三";
String str1 = list1.method("张三");
System.out.println(str1);
MyArrayList list2 = new MyArrayList<>();
list2.e = 100;
Integer str2= list2.method(100);
System.out.println(str2);
修饰符<范型变量> 返回值类型 方法名称(形参列表){
方法体
}
任意字母。一般会写T 或者M ,方便识别
如果一个类中,某个方法的参数类型或者返回值类型不确定的时候,可以把该方法定义为含有泛型的方法
注意:调用含有泛型的方法的时候确定其泛型的具体数据类型
// 定义一个含有泛型的方法
public static T method(T t){
return t;
}
public static void main(String[] args){
// 指定泛型的具体数据类型为Integer
Integer i = method(100);
String str = method("张三");
}
public interface 接口名<泛型变量>{
}
任意字母,一般可以使用 E
确定接口的泛型的具体数据类型
(1)、通过实现类的方式确定接口泛型的具体数据类型
public class 类名 implements 接口名<具体的数据类型>{}
(2)、实现类实现接口的时候如果不确定接口反省的具体数据类型,可以在创建实现类对象的时候来确定接口泛型的具体数据类型
public class 类名<泛型变量> implements 接口名<具体的数据类型>{}
// 接口
public interface A {
public abstract void method1(E e);
public default E method2(E e){
return e;
}
}
// 实现类1
public class Impl_1 implements A{
@Override
public void method1(String s) {
}
@Override
public String method2(String s) {
return null;
}
}
/*
* 实现类实现接口的时候不确定接口泛型的具体数据类型
* 实现类类名称后面就必须跟上,否则重写的方法中的泛型将无法被定义
*
* 这样的实现类 在创建实现类对象的时候来确定泛型的具体数据类型
* */
// 实现类2
public class Impl_2 implements A{
@Override
public void method1(E e) {
System.out.println("实现类 method1");
}
@Override
public E method2(E e) {
return null;
}
}
public static void main(String[] args) {
// 创建实现类对象的时候确定接口泛型的具体数据类型
Impl_1 impl_1 = new Impl_1();
impl_1.method1("donglan");
String donglan = impl_1.method2("donglan");
System.out.println(donglan);
// 创建实现类对象的时候不确定接口泛型的具体数据类型
Impl_2 impl_2 = new Impl_2<>();
impl_2.method1("donglan");
String donglan2 = impl_2.method2("donglan");
System.out.println(donglan2);
Impl_2 impl_3 = new Impl_2<>();
impl_3.method1(100);
Integer donglan3 = impl_3.method2(100);
System.out.println(donglan3);
}
总结:
泛型是一种位置的数据类型
定义在类上的泛型,使用类的时候会确定泛型的类型
定义在方法上的泛型,会在使用方法的时候确定泛型的类型
定义在接口上的泛型,会在使用接口的时候确定泛型的类型
泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用【?】
【?】表示位置通配符
注意:不同往使用通配符的集合里存储数据,因为系统不知道该存储什么样的类型
用代码来理解:
public static void main(String[] args) {
// 关系: String类继承Object类
ArrayList
通配符的高级使用 --> 受限类型
上限:< ? extends 类名> 只能接收该类类型或者其子类类型
下限:< ? super 类名> 只能接收该类类型或者其父类类型
上限意思就是天花板的意思,级别不能再高了,最高的级别限度
下限意思就是地板的意思,最低的级别限度
代码来理解:
public static void main(String[] args) {
// 关系: String类继承Object类
ArrayList list1 = new ArrayList<>();
ArrayList list2 = new ArrayList<>();
// 关系: Integer类继承Number Number类继承Object类
ArrayList list3 = new ArrayList<>();
ArrayList list4 = new ArrayList<>();
/*
* < ? extends 类名> < ? super 类名>
* */
method1(list1);
method1(list2);
method1(list3);
method1(list4);
method2(list1);
method2(list2);
method2(list3);
method2(list4);
//method3(list1); 编译报错,因为方法参数是 上限为Number,
//method3(list2); 编译报错,因为方法参数是 上限为Number,
method3(list3);
method3(list4);
method4(list1);
//method4(list2); 编译报错,因为方法参数是 下限为Integer
method4(list3);
method4(list4);
}
// 定义一个方法,可以接收以上四种集合对象
public static void method1(ArrayList arrayList){}
public static void method2(ArrayList> arrayList){}
// 定义一个方法,只可以接收list3和list4集合对象
public static void method3(ArrayList extends Number> arrayList){}
// 定义一个方法,只可以接收list1,list3,list4集合对象
public static void method4(ArrayList super Integer> arrayList){}