集合使用有限制条件的通配符之后的数据的读取和写入
读取:
? extends T ---- 类型上限
如果是? extends T的形式,这个时候我们读取的时候就可以通过T类型接收返回值,而不是通过使用Object类型,因为这个时候我们最多能配对的泛型的类型就是T类型,这个时候如果我们使用T类型的引用就足以接收
package 泛型.通配符;
import java.util.ArrayList;
import java.util.List;
public class Demo2 {
public static void main(String[] args) {
List<? extends Number> list1 = null;
List<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
list2.add(4);
list1 = list2;
/*
这个时候我们就使用Number类型的引用就可以了,不用使用Object类型的引用,因为这个时候这个类型通配符中最多能匹配的就是
Number类型的数据了,如果是Object类型,这个时候肯定就不能匹配,那么我们使用Number类型的引用这个时候就可以和集合中的返回值形成多态
,就已经足够了
*/
Number number = list1.get(0);
System.out.println(number);
}
}
? super T — 类型下限
如果是? super T的形式,这个时候我们就还必须是Object类型的引用去接收我们使用这个泛型为? super T 类型的类创建出来的对象调用的get()方法的返回值,这个时候我们的这个通配符可以接收T类型的数据,也可以接收T类型的父类类型的数据,这个时候我们T类型的最终的父类也就是肯定是Object类型,所以我们就要使用Object 类型去接收这个返回值,才能和我们这方法的实际返回值形成多态
eg:
package 泛型.通配符;
import java.util.ArrayList;
import java.util.List;
public class Demo3 {
public static void main(String[] args) {
List<? super Number> list1 = null;
List<Object> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
list2.add(4);
list1 = list2;
/*
这个时候我们必须要使用Object类型的引用来接收这个返回值,因为这个时候我们的? super Number可以配Number 类型和Number的父类类型,这个时候
我们的Number类型的父类类型就是Object类型,这个时候如果我们要接收这个get()方法的返回值,那么这个时候自然我们要使用Object类型的引用
来接收这个方法的返回值
*/
Object number = list1.get(0);
System.out.println(number);
}
}
写入:
? extends T ---- 类型上限
如果是? extends T 这个时候我们是不可以写入数据的,除了null
-
因为 ? extends T是可以匹配T类型和T类型的子类类型,这个时候我们不知道这个类的具体泛型
-
这个时候我们可以有数学归纳法得出结论:
这个时候假设集合G< A>继承了集合G< B>这个时候对于G<? extends B>的集合来说,这个时候如果我们要在这个集合中写入数据,这个时候我们写入G< A>类型的数据,这个时候可以写入成功? —显然是不可能写入成功的,这个时候我们的G< A>肯定也有子类,比如说是G< C>继承了G< A>,这个时候如果我们的泛型通配符配的是G< C>,那么这个时候我们肯定就推翻了之前写入G< A>类型数据的想法,因为这个时候我们声明为G< C> ,这个时候只能填入G< C>类型的对象或者是G< C>类型的子类类型的对象,这个时候我们的G< C>又有子类,G< C>的子类还有子类 — 这个时候就无限循环了下去 -------- 这个时候最终就得出一个结果 : ? extends T的形式不可以写入数据 (除了null之外)
-
-
null可以写入,但是写入null其实就相当于没写,所以我们也就说? extends T的形式的通配符不可写入数据
eg:
package 泛型.通配符;
import java.util.ArrayList;
import java.util.List;
public class Demo4 {
public static void main(String[] args) {
List<? extends Number> list1 = null;
List<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
list2.add(4);
list1 = list2;
/*
这个时候我们就使用Number类型的引用就可以了,不用使用Object类型的引用,因为这个时候这个类型通配符中最多能匹配的就是
Number类型的数据了,如果是Object类型,这个时候肯定就不能匹配,那么我们使用Number类型的引用这个时候就可以和集合中的返回值形成多态
,就已经足够了
*/
list1.add(null);//这个时候编译是通过的因为我们? extends T的形式只可以添加null
list1.add(1); //这个时候编译就会出错,? extends T只可以添加null,其余任何都不可以添加
}
}
? super T ---- 类型下界
如果是? super T 形式的,这个时候我们就可以写入T类型的数据,或者是写入T类型的子类类型的数据,这个时候因为我们的? super T 的形式可以配对 T类型和T类型的父类类型,这个时候我们的这个通配符最小类型也要为T类型,这个时候如果我们在这个通配符为泛型的类中,那么肯定就可以以多态的形式添加T类型子类类型的数据,当然也可以添加T类型的数据,如果是添加的T类型的数据,这个时候有可能是以多态的形式添加,也有可能是以本类的引用指向本类的对象
eg:
package 泛型.通配符;
import java.util.ArrayList;
import java.util.List;
public class Demo5 {
public static void main(String[] args) {
List<? super Number> list1 = null;
List<Object> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
list2.add(4);
list1 = list2;
/*
这个时候我们写入了一个Double类型的数据,这个时候我们写入的Double类型的数据就是以Number类型的子类类型的形式写入的,
这个时候我们输入的Double类型的对象就也这个具体泛型类的引用以多态的方式进行连接
*/
list1.add(1.0); //这个时候编译是通过的,这个时候我们可以写入Number类型的数据,和Number类型的子类类型的数据
}
}
读取:
如果是? extends T