当前位置: 首页 > news >正文

java学习笔记11——泛型

泛型

在集合中使用泛型

总结:

 

01-泛型在集合、比较器中的使用
1.什么是泛型?
所谓泛型,就是允许在定义类、接口时通过一个、标识`表示类中某个`属性的类型`或者是某个方法的
`返回值或参数的类型`。这个类型参数将在使用时(例如,继承或实现这个接口、创建对象或调用方法时)
确定(即传入实际的类型参数,也称为类型实参)。
2.在集合中使用泛型之前可能存在的问题
问题1:类型不安全,因为add()的参数是Object类型,意味着任何类型的对象都可以添加成功
问题2:需要使用强转操作,繁琐,还有可能导致ClassCastException
3.在集合、比较器中使用泛型(重点)
见代码
4.使用说明
> 集合框架在声明接口和其实现类时,使用了泛型(jdk5.0)在实例化集合对象时,
  如果没有使用泛型,则认为操作的是Object类型的数据
  如果使用了泛型,则需要指明泛型的具体类型。一旦指明了泛型的具体类型,则在集合的相关的方法中,凡是使用类的泛型的位置,都替换为具体的泛型类型。

 代码:

public class CollectionMapTest {
    //在集合中使用泛型前的场景
    @Test
    public void test1(){
        List list = new ArrayList();
        list.add(67);
        list.add(78);
        list.add(76);
        list.add(99);
        //1.问题1:类型不安全,因为add()的参数是Object类型,意味着任何类型的对象都可以添加成功
        list.add("A");
        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            //2.问题2:需要使用强转操作,繁琐,还有可能导致ClassCastException
            Integer i = (Integer)iterator.next();
            int score = i;
            System.out.println(score);
        }
    }
    //在集合中使用泛型的例子
    @Test
    public void test2(){
        List<Integer> list = new ArrayList<Integer>();
        list.add(78);
        list.add(76);
        list.add(69);
        list.add(99);
        //编译报错。保证类型安全
//        list.add("AA");
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            //因为添加的都是Integer类型,避免了强转操作
            Integer i = iterator.next();
            int score = i;
            System.out.println(score);
        }
    }
    /**
     * 泛型在Map中使用的例子
     */
    @Test
    public void test3(){
//        HashMap<String,Integer> map = new HashMap<String,Integer>();
        //jdk7新特性
        HashMap<String,Integer> map = new HashMap(); //类型推断
        map.put("Tom", 67);
        map.put("Jerry", 87);
        map.put("Jack", 99);
//        Set<Map.Entry<String,Integer>> entrySet = map.entrySet();
//        Iterator<Map.Entry<String,Integer>> iterator = entrySet.iterator();
        var entrySet = map.entrySet();
        var iterator = entrySet.iterator();
        while(iterator.hasNext()){
            Map.Entry<String,Integer> entry = iterator.next();
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + " ---> " +value);
        }
    }
}

 练习2:

1.定义一个Employee类。该类包含:private成员变量name,age,birthday,其中 birthday 为 MyDate 类的对象;
    并为每一个属性定义 getter,setter方法;并重写 toString 方法输出 name,age,birthday
2.MyDate类包含:
    private成员变量year,month,day;并为每一个属性定义getter,setter 方法;
3.创建该类的 5 个对象,并把这些对象放入 TreeSet 集合中(TreeSet 需使用泛型来定义)
4.分别按以下两种方式对集合中的元素进行排序,并遍历输出:4.
1),使Employee实现Comparable 接口,并按name 排序
 2)创建 TreeSet 时传入 Comparator对象,按生日日期的先后排序,
代码:
Employee 
public class Employee implements Comparable<Employee>{
    private String name;
    private int age;
    private MyDate birthday;

    public Employee() {
    }

    public Employee(String name, int age, MyDate birthday) {
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public MyDate getBirthday() {
        return birthday;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=[" + birthday +
        "]}";
    }

    //按照name从低到高排序
    @Override
    public int compareTo(Employee o) {
        return this.name.compareTo(o.name);
    }
}
MyDate 
public class MyDate implements Comparable<MyDate>{
    private int year;
    private int month;
    private int day;

    public MyDate() {
    }

    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    @Override
    public String toString() {
        return year + "年" + month + "月" + day + "日";
    }

    //按照生日从小到大排序
    @Override
    public int compareTo(MyDate o) {
        int yearDistince = this.getYear() - o.getYear();
        //正确的:
        if(yearDistince != 0){
            return yearDistince;
        }
        int monthDistince = this.getMonth() - o.getMonth();
        if(monthDistince != 0){
            return monthDistince;
        }
        return this.getDay() - o.getDay();
    }
}
EmployeeTest 
public class EmployeeTest {
    //需求1:使Employee实现Comparable 接口,并按name 排序
    @Test
    public void test1(){
        TreeSet<Employee> set = new TreeSet<>();
        Employee e1 = new Employee("HanMeimei",18,new MyDate(1998,12,21));
        Employee e2 = new Employee("LiLei",20,new MyDate(1996,11,21));
        Employee e3 = new Employee("LiHua",21,new MyDate(2000,9,12));
        Employee e4 = new Employee("ZhangLei",19,new MyDate(1997,5,31));
        Employee e5 = new Employee("ZhangYi",23,new MyDate(2001,11,2));
        set.add(e1);
        set.add(e2);
        set.add(e3);
        set.add(e4);
        set.add(e5);
        //遍历
        Iterator<Employee> iterator = set.iterator();
        while(iterator.hasNext()){
            Employee employee = iterator.next();
            System.out.println(employee);
        }
    }
    //需求2: 创建 TreeSet 时传入 Comparator对象,按生日日期的先后排序,
    @Test
    public void test2(){
        Comparator<Employee> comparator = new Comparator<Employee>(){
            public int compare(Employee o1, Employee o2) {
//                return o1.getBirthday().toString().compareTo(o2.getBirthday().toString());
                int yearDistince = o1.getBirthday().getYear() - o2.getBirthday().getYear();
                //正确的写法1:
//                if(yearDistince != 0){
//                    return yearDistince;
//                }
//                int monthDistince = o1.getBirthday().getMonth() - o2.getBirthday().getMonth();
//                if(monthDistince != 0){
//                    return monthDistince;
//                }
//               return o1.getBirthday().getDay() - o2.getBirthday().getDay();
                //正确的写法2:
                return o1.getBirthday().compareTo(o2.getBirthday());
            }
        };
        TreeSet<Employee> set = new TreeSet<>(comparator);
        Employee e1 = new Employee("HanMeimei",18,new MyDate(1998,12,21));
        Employee e2 = new Employee("LiLei",20,new MyDate(1996,11,21));
        Employee e3 = new Employee("LiHua",21,new MyDate(2000,9,12));
        Employee e4 = new Employee("ZhangLei",19,new MyDate(1997,5,31));
        Employee e5 = new Employee("ZhangYi",23,new MyDate(2000,9,2));
        set.add(e1);
        set.add(e2);
        set.add(e3);
        set.add(e4);
        set.add(e5);
        //遍历
        Iterator<Employee> iterator = set.iterator();
        while(iterator.hasNext()){
            Employee employee = iterator.next();
            System.out.println(employee);
        }
    }
}

 练习2:

(1)创建一个ArrayList集合对象,并指定泛型为<Integer>
(2)添加5个[0,100)以内的随机整数到集合中
(3)使用foreach遍历输出5个整数
(4)使用集合的removeIf方法删除偶数,为Predicate接口指定泛型<Ineteger>
(5)再使用Iterator选代器输出剩下的元素,为Iterator接口指定泛型<Integer>

代码:
 

public class Exer02 {
    public static void main(String[] args) {
//        (1)创建一个ArrayList集合对象,并指定泛型为<Integer>
        ArrayList<Integer> list = new ArrayList<>();
        //(2)添加5个[0,100)以内的随机整数到集合中
        for(int i = 0; i < 5; i++){
            int random = (int) (Math.random() * (99 - 0) + 1);
            list.add(random);
        }
        //(3)使用foreach遍历输出5个整数
        for(Integer value : list){
            System.out.println(value);
        }
        // (4)使用集合的removeIf方法删除偶数,为Predicate接口指定泛型<Ineteger>
        list.removeIf(new Predicate<Integer>(){
            @Override
            public boolean test(Integer value) {
                return value % 2 == 0;
            }
        });
        System.out.println();
        // (5)再使用Iterator选代器输出剩下的元素,为Iterator接口指定泛型<Integer>
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer value = iterator.next();
            System.out.println(value);
        }
    }
}

自定义泛型结构

 总结:

1.自定义泛型类\接口
1.1 格式
class A<T>{

}
interface B<T1,T2>{

}
1.2 使用说明
① 我们在声明完自定义泛型类以后,可以在类的内部(比如:属性、方法、构造器中)使用类的泛型。
② 我们在创建自定义泛型类的对象时,可以指明泛型参数类型。一旦指明,内部凡是使用类的泛型参数的位置,都具体化为指定的类的泛型类型。
③ 如果在创建自定义泛型类的对象时,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类型均按照object处理,但不等价于object。
  - 经验:泛型要使用一路都用。要不用,一路都不要用。
④ 泛型的指定中必须使用引用数据类型。不能使用基本数据类型,此时只能使用包装类替换。
⑤ 除创建泛型类对象外,子类继承泛型类时、实现类实现泛型接口时,也可以确定泛型结构中的泛型参数。比如:SubOrder2
如果我们在给泛型类提供子类时,子类也不确定泛型的类型,则可以继续使用泛型参数。比如:SubOrder3
我们还可以在现有的父类的泛型参数的基础上,新增泛型参数。比如:SubOrder5

1.3 注意点
泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
JDK7.0 开始,泛型的简化操作:ArrayList<Fruit> flist = new ArrayList<>();
③如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
④ 不能使用new E[]。但是可以:E[] elements =(E[])new Object[capacity];
    参考:ArrayList源码中声明:Object[] elementData,而非型参数类型数组。
⑤在类/接口上声明的泛型,在本类或本接口中即代表某种类型,但不可以在静态方法中使用类的泛型。
⑥ 异常类不能是带泛型的。

2.自定义泛型方法
2.1 问题:在泛型类的方法中,使用了类的泛型参数。那么此方法是泛型方法吗?

2.2 格式
杈限修饰符 <T> 返回值类型 方法名(形参列表){ //通常在形参列表或返回值类型的位置会出现泛型参数T
}
2.3 举例
public <E> E method(E e){

}
2.4 说明
> 声明泛型方法时,一定要添加泛型参数<T>
> 泛型参数在方法调用时,指明其具体的类型
> 泛型方法可以根据需要声明为static的
> 泛型方法所属的类是否是一个泛型类,都可以。

代码:

GenericTest 
public class GenericTest {
    @Test
    public void test1(){
        Person p1 = new Person();
//        Person<String> p2 = new Person<>();
        ArrayList<String> list = new ArrayList<>();
        list.add("AA");
//        list.add(123); //编译器警告

        ArrayList list1 = new ArrayList();//向下兼容,jdk5.0之前集合是没有声明为泛型
        list1.add(123);
        list.add("AA");
    }
    //测试自定义的泛型类
    @Test
    public void test2(){
        //实例化时,就可以指明类的泛型参数的类型
        Order order = new Order(); //Order<Object> order3 = new Order<>();
        Object obj = order.getT();

        //泛型参数在指明时,是不可以使用基本数据类型的!但是可以使用包装类替代基本数据类型
//        Order<int> order1 = new Order<>();
        //在实例化时,可以指明类的泛型参数的具体类型!一旦指明了泛型的类型,则在泛型类中使用泛型参数的位置
        //就替换为指定的类型
        Order<Integer> order2 = new Order<>();
        Integer t = order2.getT();

        Order<String> order3 = new Order<>();
        order3.setT("AA");
    }
    //测试Order的子类
    @Test
    public void test3(){
        //实例化SubOrder1
        SubOrder1 sub1 = new SubOrder1();
        Object t = sub1.getT();
//        SubOrder1<Integer> sub2 = new SubOrder1<>(); //因为SubOrder1不是泛型类,此处变编译错误

    }
    @Test
    public void test4(){
        SubOrder2 sub1 = new SubOrder2();
        Integer t = sub1.getT();
        SubOrder3<String> sub2 = new SubOrder3<>();
        String t1 = sub2.getT();
        sub2.show("AA");

        SubOrder4<String> sub3 = new SubOrder4<>();
        Integer t2 = sub3.getT();
        String e = sub3.getE();

        SubOrder5<String,Integer> sub4 = new SubOrder5<>();
        String t3 = sub4.getT();
        Integer e1 = sub4.getE();
    }
    //测试泛型方法的使用
    @Test
    public void test5(){
        Order<String> order1 = new Order<>();
        Integer[] arr = new Integer[]{1,2,3,4,5};
        ArrayList<Integer> list = order1.copyFromArrayToList(arr);
        for(Integer i : list){
            System.out.println(i);
        }
    }
}

 Order

Order
public class Order<T> {
    //声明了类的泛型参数以后,就可以在类的内部使用此泛型参数
    T t;
    int orderId;
//    static T t1;

    public Order() {
    }

    public Order(T t, int orderId) {
        this.t = t;
        this.orderId = orderId;
    }

    //如下的两个方法不是泛型方法
    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }

    public int getOrderId() {
        return orderId;
    }

    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }

    @Override
    public String toString() {
        return "Order{" +
                "t=" + t +
                ", orderId=" + orderId +
                '}';
    }
    //不可以在静态方法中使用类的泛型
//    public static void method1(){
//        System.out.println("t : " + t1);
//    }
    //自定义泛型方法
    public <E> E method(E e){
        return null;
    }
    //定义泛型方法。按E[]数组元素添加到对应类型的ArrayList中,并返回
    public static <E> ArrayList<E> copyFromArrayToList(E[] arr){
        ArrayList<E> list = new ArrayList<>();
        for(E e : arr){
            list.add(e);
        }
        return list;
    }
}

Person 
public class Person {
}
SubOrder1 
public class SubOrder1 extends Order {

}

SubOrder2 
public class SubOrder2 extends Order<Integer>{

}
SubOrder3
public class SubOrder3<T> extends Order<T>{
    public void show(T t){
        System.out.println(t);
    }
}

SubOrder4
public class SubOrder4<E> extends Order<Integer> {
    E e;

    public SubOrder4() {
    }

    public SubOrder4(E e) {
        this.e = e;
    }

    public SubOrder4(Integer integer, int orderId, E e) {
        super(integer, orderId);
        this.e = e;
    }

    public E getE() {
        return e;
    }

    public void setE(E e) {
        this.e = e;
    }
}
SubOrder5
public class SubOrder5<T,E> extends Order<T> {
    E e;

    public SubOrder5() {
    }

    public SubOrder5(T t, int orderId, E e) {
        super(t, orderId);
        this.e = e;
    }

    public E getE() {
        return e;
    }

    public void setE(E e) {
        this.e = e;
    }
}

apply:

DAO
public class DAO<T> {
    //增
    public void insert(T bean){
        //通过相应的sql语句,将bean对象的属性值
    }
    //删
    public T deleteByID(int id){
        //略
        return null;
    }
    //改
    public void update(int id,T bean){
        //略
    }
    //查
    //查询一条记录
    public T queryForInstance(int id){
        //略
        return null;
    }
    //查询多条记录构成的集合
    public List<T> queryForList(int id){
        return null;
    }
    //定义泛型方法
    //比如:查询表中的记录数,(E:long类型)
    //比如:查询表中最大的生日(E:Date类型)
    public <E> E getValue(String sql){
        return null;
    }
}
DAOTest 
public class DAOTest {
    @Test
    public void test1(){
        CustomerDAO dao1 = new CustomerDAO();
        dao1.insert(new Customer());
        Customer customer = dao1.queryForInstance(1);
    }
    @Test
    public void test2(){
        OrderDAO dao1 = new OrderDAO();
        dao1.insert(new Order());
        List<Order> list = dao1.queryForList(1);
    }
}
Order 
public class Order {
    int orderId;
    String orderName;
    Date orderDate;
}
OrderDAO 
public class OrderDAO extends DAO<Order>{

}
CustomerDAO 
public class CustomerDAO extends DAO<Customer> {

}
Customer 
public class Customer {
    int id;
    String name;
    String email;
    Date birth;
}

 练习1:

定义个泛型类 DAO<T>,在其中定义一个Map 成员变量,Map 的键为 String 类型,值为 T 类型。

分别创建以下方法:
public void save(String id,T entity): 保存 T 类型的对象到 Map 成员变量中
public T get(string id):从 map 中获取 id 对应的对象
public void update(String id,T entity):替换 map 中key为id的内容,改为 entity 对象
public List<T> list():返回 map 中存放的所有 T 对象
public void delete(String id):删除指定 id 对象
定义一个 User 类:
该类包含:private成员变量(int类型)id,age;(String 类型)name.
定义一个测试类:创建 DA0 类的对象,分别调用其 save、get、update、list、delete 方法来操作 User 对象
使用 Junit 单元测试类进行测试。

练习:

DAO
public class DAO<T> {
    Map<String, T> map;

    {
        map = new HashMap<>();
    }
    public void save(String id,T entity){ //保存 T 类型的对象到Map成员变量中
        if(!map.containsKey(id)){
            map.put(id,entity);
        }
    }
    public T get(String id){ //从map中获取id对应的对象
        return map.get(id);
    }
    public void update(String id,T entity){ //替换map中key为id的内容,改为entity对象
        if(map.containsKey(id)){
            map.put(id,entity);
        }
    }
    public List<T> list(){ //返回map中存放的所有T对象
        //错误的:
//        Collection<T> values = map.values();
//        System.out.println(values.getClass());
//        return (List<T>)values; //报ClassCastException异常。
        //正确的(方式1):
//        Collection<T> values = map.values();
//        ArrayList<T> list = new ArrayList<>();
//        list.addAll(values);
//        return list;
        //正确的(方式2):
        Collection<T> values = map.values();
        ArrayList<T> list = new ArrayList<>(values);
        return list;
    }
    public void delete(String id){ //删除指定 id 对象
        map.remove(id);
    }
}
User 
public class User {
    private int id;
    private int age;
    private String name;

    public User() {
    }

    public User(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return id == user.id && age == user.age && Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, age, name);
    }
}
DAOTest 
public class DAOTest {
    public static void main(String[] args) {
        DAO<User> dao = new DAO<>();
        dao.save("1001",new User(1,23,"周杰伦"));
        dao.save("1002",new User(1,23,"昆凌"));
        dao.update("1002",new User(3,26,"蔡依林"));
        dao.delete("1002");
        List<User> list = dao.list();
        for(User user : list){
            System.out.println(user);
        }
    }
}

 练习2:

练习1:泛型方法
编写一个泛型方法,实现任意引用类型数组指定位置元素交换。
public static <E> void method1( E[] e,int a,int b)
练习2:泛型方法
编写一个泛型方法,接收一个任意引用类型的数组,并反转数组中的所有元素
public static <E> void method2( E[] e)

代码:

public class Exer02 {
    //编写一个泛型方法,实现任意引用类型数组指定位置元素交换。
    public static <E> void method1(E[] e,int a,int b){
        E temp = e[a];
        e[a] = e[b];
        e[b] = temp;
    }
    @Test
    public void test1(){
        String[] arr = new String[]{"AA","BB","CC"};
        method1(arr,0,2);
        System.out.println(Arrays.toString(arr));
    }
    //编译一个泛型方法,接收一个任意引用类型的数组,并反转数组中所有的元素
    public static <E> void method2(E[] e){
        for(int i = 0, j = e.length - 1; i < j; i++,j--){
            E temp = e[i];
            e[i] = e[j];
            e[j] = temp;
        }
    }
    @Test
    public void test2(){
        Integer[] arr = new Integer[]{1,2,3,4,5};
        method2(arr);
        System.out.println(Arrays.toString(arr));
    }
}

 练习3:

案例:
我们要声明一个学生类,该类包含姓名、成绩。而此时学生的成绩类型不确定,为什么呢?
因为,语文老师希望成绩是“优秀”、“良好”、“及格”、“不及格”,数学老师希望成绩是89.5,65.0,
英语老师希望成绩是'A','B','C’,"D',"E'。那么我们在设计这个学生类时,就可以使用泛型。

代码:

public class Student<T> {
    private String name;
    private T score; //成绩

    public Student() {
    }

    public Student(String name, T score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public T getScore() {
        return score;
    }

    public void setScore(T score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}
StudentTest 
public class StudentTest {
    public static void main(String[] args) {
        /**
         * 语文老师希望成绩是“优秀”、“良好”、“及格”、“不及格”,数学老师希望成绩是89.5,65.0,
         * 英语老师希望成绩是'A','B','C’,"D',"E'
         */
        Student<String> s1 = new Student<>("Tom","良好");
        Student<Double> s2 = new Student<>("Jerry",87.5); //存在87.5自动装箱
        Student<Character> s3 = new Student<>("Rose",'A'); //'A'自动装箱
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
    }
}

泛型在继承上的体现

  通配符的使用

有限制的通配符

 总结:

03-泛型在继承上的体现
1.类SuperA是类A的父类,则G<SuperA>与G<A>的关系:G<SuperA>和 G<A>是并列的两个类,没有任何子父类的关系。
比如:ArrayList<Object>、ArrayList<String>没有关系
2.类SuperA是类A的父类或接口,SuperA<G>与A<G>的关系:SuperA<G>与A<G> 有继承或实现的关系。
即A<G>的实例可以赋值给SuperA<G>类型的引用(或变量)
比如:List<String>与 ArrayList<string>
04-通配符的使用
1.通配符:?

2.使用说明:
举例:ArrayList<?>
G<?> 可以看做是G<A>类型的父类,即可以将G<A>的对象赋值给G<?>类型的引用(或变量)
3.读写数据的特点(以集合为例说明)
> 读取数据:允许的,读取的值的类型为Obejct类型
> 写入数据:不允许的,特例:写入null值
4.有限制条件的通配符
    List<? extends A>: 可以将List<A>或List<B>赋值给List<? extends A>,其中B类是A类的子类
    List <? super A> : 可以将List<A>或List<B>赋值给List<? super A>,其中B类是A类的父类

5.有限制条件的统配符的读写操作(难、了解)
技巧:开发中,遇到了带限制条件的通配符,在赋值时,如果没报错,那就正常使用。
    如果报错了,知道不能这样写。改改!

代码:

GenericTest
public class GenericTest {
    @Test
    public void test1(){
        //1.
        Object obj = null;
        String str = "AA";
        obj = str; //基于继承性的多态的使用

        //2.
        Object[] arr = null;
        String[] arr1 = null;
        arr = arr1; //基于继承性的多态的使用
    }

    /**
     * 1.类SuperA是类A的父类,则G<SuperA>与G<A>的关系:G<SuperA>和 G<A>是并列的两个类,没有任何子父类的关系。
     */
    @Test
    public void test2(){
        ArrayList<Object> list1 = null;
        ArrayList<String> list2 = null;
//        list1 = list2; //会报错
        /**
         * 反证法:
         * 假设list1 = list2是可以的
         * list2.add("AA");
         *
         * list1.add(123);
         *
         * String str = list2.get(1); //相当于取出的123赋值给了str,是错误的
         */
        method(list1);
//        method(list2); //错误的
    }
    @Test
    public void test3(){
        Person<Object> per = null;
        Person<String> per1 = null;
//        per = per1; //报错
    }
    public void method(ArrayList<Object> list){

    }

    /**
     * 2.类SuperA是类A的父类或接口,SuperA<G>与A<G>的关系:SuperA<G>与A<G> 有继承或实现的关系。
     * 即A<G>的实例可以赋值给SuperA<G>类型的引用(或变量)
     */
    @Test
    public void test4(){
        List<String> list1 = null;
        ArrayList<String> list2 = null;
        list1 = list2;
        list1.add("AA");
        method(list2);
    }
    public void method(List<String> list){

    }
}
Person
public class Person<T> {
    String name;
    T t;

}
Son 
public class Son extends Father {
}
Father 
public class Father {
}
GenericTest1 
public class GenericTest1 {
    /**
     * 测试:通配符?的使用
     */
    @Test
    public void test1(){
        List<?> list = null;
        List<Object> list1 = null;
        List<String> list2 = new ArrayList<>();
        list = list1;
        list = list2;
        method(list1);
        method(list2);
        list.get(0);
    }
    public void method(List<?> list){
        for(Object obj : list){
            System.out.println(obj);
        }
//        list.add("AA"); //错误的
    }
    @Test
    public void test2(){
        List<?> list = null;
        List<String> list1 = new ArrayList<>();
        list1.add("AA");
        list = list1;
        //读取数据(以集合为例说明)
        Object obj = list.get(0);
        System.out.println(obj);
        //写入数据(以集合为例)
        // 写入数据,操作失败
//        list.add("BB");
        //特例:可以将null写入集合中
        list.add(null);
    }
    /**
     * 测试:有限制条件的通配符的使用
     * ? extends A:
     * ? super A:
     */
    @Test
    public void test3(){
        //<=
        List<? extends Father> list = null;
        List<Object> list1 = null;
        List<Father> list2 = null;
        List<Son> list3 = null;
//        list = list1; //报错
        list = list2;
        list = list3;
    }

    /**
     * 针对于 ? extends A 的读写
     */
    @Test
    public void test3_1(){
        List<? extends Father> list = null;
        List<Father> list1 = new ArrayList<>();
        list1.add(new Father());
        list = list1;
        //读取数据:可以的
        Father father = list.get(0);
        //写入数据:不可以的,例外null
        list.add(null);
//        list.add(new Father());
//        list.add(new Son());
    }
    @Test
    public void test4(){
        //>=
        List<? super Father> list = null;
        List<Object> list1 = null;
        List<Father> list2 = null;
        List<Son> list3 = null;
        list = list1;
        list = list2;
//        list = list3; //报错
    }
    @Test
    public void test4_1(){
        List<? super Father> list = null;
        List<Father> list1 = new ArrayList<>();
        list1.add(new Father());
        list = list1;
        //读取数据:可以的
        Object obj = list.get(0);
        //写入数据:可以将Father及其子类的对象添加进来
        list.add(null);
//        list.add(new Object()); //报错
        list.add(new Father());
        list.add(new Son());
    }
}

相关文章:

  • 【Unity】 HTFramework框架(六十四)SaveDataRuntime运行时保存组件参数、预制体
  • Python WebSockets 库详解:从基础到实战
  • MySQL 5.7 Online DDL 技术深度解析
  • C++和C#接口对应关系
  • 【运维】Centos硬盘满导致开机时处于加载状态无法开机解决办法
  • Docker基础详解
  • Linux 高级路由策略控制配置:两个不同路由子网间通信
  • 沉浸式体验测评|AI Ville:我在Web3小镇“生活”了一周
  • 基于MODIS观测的全球格点数据处理与GeoTIFF栅格化存储
  • 新闻发布管理系统带万字文档新闻管理系统java项目java课程设计java毕业设计
  • ES5内容之String接口
  • python-leetcode 62.搜索插入位置
  • 阻止上传可执行程序
  • 实现Excel导入和导出
  • 服务器自动备份到本地,服务器自动备份到本地的方法有哪些?
  • [250401] OpenAI 向免费用户开放 GPT-4o 图像生成功能 | Neovim 0.11 新特性解读
  • MySQL和Oracle批量插入SQL差异详解
  • 青少年编程与数学 02-014 高中数学知识点 06课题、数学建模与探究活动
  • 第十一届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组
  • Java面试黄金宝典28
  • 价格周报|供需回归僵局,本周生猪均价与上周基本持平
  • 巴基斯坦称对印精准打击造成设施损坏和人员伤亡
  • 代理销售保险存在误导行为,农业银行重庆市分行相关负责人被罚款0.1万元
  • 105岁八路军老战士、抗美援朝老战士谭克煜逝世
  • 中信银行:拟出资100亿元全资设立信银金融资产投资有限公司
  • 趣看 | 五一黄金周:你拍风景,拍风景的人在拍你