集合框架背后的 Java 知识
集合框架背后的 Java 知识
泛型
1.1什么是泛型
[!NOTE]
⼀般的类和⽅法,只能使⽤具体的类型: 要么是基本类型,要么是⾃定义的类。如果要编写可以应⽤于多种类型的代码,这种刻板的限制对代码的束缚就会很⼤。泛型是在JDK1.5引⼊的新的语法,通俗讲,泛型:就是适⽤于许多许多类型。从代码上讲,就是对类型实现了参数化。
1.2泛型引入
[!NOTE]
需求:实现⼀个类,类中包含⼀个数组成员,使得数组中可以存放任何类型的数据,也可以根据成员⽅法返回数组中某个下标的值?
[!TIP]
思路我们以前学过的数组,只能存放指定类型的元素,例如:
int[] array = new int[10]; String[] strs = new String[10];所有类的⽗类,默认为Object类。数组是否可以创建为Object?
[!NOTE]
代码实现class MyArray { public Object[] array = new Object[10]; public Object getPos(int pos) { return this.array[pos]; } public void setVal(int pos,Object val) { this.array[pos] = val; } } public class TestDemo { public static void main(String[] args) { MyArray myArray = new MyArray(); myArray.setVal(0,10); myArray.setVal(1,"hello");//字符串也可以存放 String ret = myArray.getPos(1);//编译报错 System.out.println(ret); } }
[!WARNING]
[!NOTE]
思考与总结:
- 任何类型数据都可以存放
- 1号下标本⾝就是字符串,但是却编译报错。必须进⾏强制类型转换
虽然在这种情况下,当前数组任何数据都可以存放,但是,更多情况下,我们还是希望他只能够持有⼀种数据类型。⽽不是同时持有这么多类型。
所以,泛型的主要⽬的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传⼊什么类型。
1.3泛型的定义
1.3.1泛型类
[!NOTE]
格式:public class 类名<E> { }
[!NOTE]
1.泛型<>里面填的是引用类型,如果想操作基本类型,需要写包装类;如果
<>里面啥也不写,默认类型Object类型。2.类名后面的
<>表示占位符,表示当前的类是泛型类。3.里面的 E 叫做类型形参,类型形参一般使用一个大写字母表示
常见的名称有:E 表示 Element;K 表示 Key;V 表示 Value;N 表示 Number;T 表示 Type
4.当 new 对象时,泛型的类型就确定了,这样代码可以更加灵活。
[!NOTE]
将上面的代码改造成泛型类:class MyArray<T> { public Object[] array = new Object[10]; public T getPos(int pos) { return (T)this.array[pos]; } public void setVal(int pos,T val) { this.array[pos] = val; } } public class TestDemo { public static void main(String[] args) { MyArray<Integer> myArray = new MyArray<>(); myArray.setVal(0,10); int ret = myArray.getPos(1); System.out.println(ret); myArray.setVal(1,"bit"); } }代码分析:
在第 15 行的时候我们将 myArray 定义为 Integer 类型了,故第 19 行添加一个 String 类型的字符串是会提示编译错误
就达成上面所说的泛型的主要⽬的:就是指定当前的容器,要持有什么类型的对象。让编译器去做检查。此时,就需要把类型,作为参数传递。需要什么类型,就传⼊什么类型。
1.3.2 泛型方法
[!NOTE]
格式修饰符 <E> 返回值类型 方法名(形参){ 方法体 return 结果 }
[!NOTE]
1.方法自己独立声明泛型参数,不受类的泛型约束,静态方法只能用泛型方法定义泛型。2.调用泛型的时候,其类型就确定了
[!TIP]
public class MyCollections { // 构造器私有化 -> 禁止外部通过new MyCollections()实例化这个类 private MyCollections(){ } // 泛型方法 public static <E> void addAll(ArrayList<E> list, E... elements){ for (E e : elements) { list.add(e); } } }public void test03() { // 创建一个名为 list 的 String 类型的集合 ArrayList<String> list = new ArrayList<>(); // 调用该泛型方法的时候,泛型类型就确定了 MyCollections.addAll(list, "小猫", "小狗", "小猪", "小牛", "小羊"); System.out.println(list); }运行结果: [小猫, 小狗, 小猪, 小牛, 小羊]
1.3.3 泛型接口
[!NOTE]
定义泛型接口Storage<T>。接口抽象方法:
void save(T item)T get()要求:
- 创建实现类
StringStorage,实现Storage<String>,使用成员变量存储单个字符串。- 创建实现类
ObjectStorage<T>,实现Storage<T>,支持任意类型。- main 方法测试:
StringStorage存入"读书",调用get()打印;ObjectStorage<Integer>存入99,调用get()打印。
[!TIP]
interface Storage<T> { void save(T item); T get(); } class StringStorage implements Storage<String> { private String item; @Override public void save(String item) { this.item = item; } @Override public String get() { return item; } } class ObjectStorage<T> implements Storage<T> { private T item; @Override public void save(T item) { this.item = item; } @Override public T get() { return item; } } public class Test1 { public static void main(String[] args) { StringStorage s1 = new StringStorage(); s1.save("读书"); System.out.println(s1.get()); ObjectStorage<Integer> o1 = new ObjectStorage<>(); o1.save(99); System.out.println(o1.get()); } }
1.4泛型通配符
? -> 一般情况出现在方法的参数位置
public void test01() {
ArrayList<String> list = new ArrayList<>();
list.add("小猫");
list.add("小狗");
list.add("小猪");
list.add("小牛");
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
method(list);
method(list2);
}
// 此处的?表示可以接收任意的数据类型
public void method(ArrayList<?> list) {
for (Object o : list) {
System.out.println(o);
}
}
1.5泛型的上下限
1、上界(上限) ? extends 父类
? extends Number
传过来的类型只能是 Number 类型或者是 Number 类型的子类
2、下界(下限)? super 子类
? super Integer
传过来的类型只能是 Integer 类型或者是 Integer 类型的父类
/**
* Integer -> Number -> Object
* String -> Object
*/
public class Demo03FanXing {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<Number> list3 = new ArrayList<>();
ArrayList<Object> list4 = new ArrayList<>();
//get1(list1); 可以
//get1(list2); 不行
//get1(list3); 可以
//get1(list4); 不行
System.out.println("=================");
//get2(list1); 不行
//get2(list2); 不行
//get2(list3); 可以
//get2(list4); 可以
}
//上限 ?只能接收extends后面的本类类型以及子类类型
public static void get1(Collection<? extends Number> collection){
}
//下限 ?只能接收super后面的本类类型以及父类类型
public static void get2(Collection<? super Number> collection){
}
}
[!TIP]
应用场景:1.如果我们在定义类,方法,接口的时候,如果类型不确定,我们可以考虑定义含有泛型的类,方法,接口
2.如果类型不确定,但是能知道以后只能传递某个类的继承体系中的子类或者父类,就可以使用泛型的上限或者下限 -> 给泛型类型规定了范围



浙公网安备 33010602011771号