25.泛型

1.泛型

泛型是Java编程语言的一项重要功能,它允许开发人员在定义类、接口和方法时使用类型参数。使用泛型可以实现类型的参数化,使代码更加灵活和可重用。

1.1 概述

问题】没有泛型的时候,集合如何存储数据?

结论:如果我们没有给集合指定类型,默认认为所有的数据类型都是Object类型的,此时可以往集合里添加任意的数据类型(带来一个坏处:我们在获取数据的时候,无法使用它的特有方法),因此产生了泛型,可以在添加数据的时候对数据进行统一,而且在获取数据的时候,也省的强转了。

使用泛型带来了以下几个优势:

  1. 类型安全性:通过使用泛型,编译器可以在编译时检查代码中的数据类型错误,并提供编译时类型安全性。这意味着在编译时就可以检测到类型不匹配的错误,而不是在运行时出现问题。

  2. 代码重用:使用泛型可以编写更为通用的代码,可适用于多种数据类型。这样可以减少代码的冗余,并增加代码的可重用性和维护性。

  3. 更强的类型检查和自动转换:使用泛型可以在编译时进行更强的类型检查,并自动执行类型转换,避免了手动的类型转换。

泛型的好处

  1. 统一数据类型

  2. 把运行时期的问题提前到了编译时期,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确认下来。

泛型的细节

  1. 泛型中不能有基本数据类型

  2. 指定泛型的具体类型后,传递数据时,可以传入该类类型或者子类类型

  3. 如果不写泛型,类型默认是Object

1.2 代码示例

  • 代码示例

    package text.text02;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    /*
    泛型:
    没有泛型的时候,集合如何存储数据
    结论:如果我们没有给集合指定类型,默认认为所有的数据类型都是Object类型的,此时可以往集合里添加任意的数据类型(带来一个坏处:我们在获取数据的时候,无法使用它的特有方法),因此产生了泛型,可以在添加数据的时候对数据进行统一,而且在获取数据的时候,也省的强转了。
    
    泛型的好处:
        1.统一数据类型
        2.把运行时期的问题提前到了编译时期,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确认下来。
    
    泛型的细节:
        1.泛型中不能有基本数据类型
        2.指定泛型的具体类型后,传递数据时,可以传入该类类型或者子类类型
        3.如果不写泛型,类型默认是Object
     */
    public class text30 {
        public static void main(String[] args) {
            //没有用泛型的方法
            System.out.print("没有用泛型的方法:");
            method1();
    
            System.out.println();
    
            //有用泛型的方法
            System.out.print("有用泛型的方法:");
            method2();
        }
    
        //没有用泛型的方法
        public static void method1() {
            //创建集合对象并添加元素
            ArrayList list = new ArrayList();
            list.add(123);//添加Integer类型
            list.add("aaa");//添加字符串类型
            list.add(true);//添加布尔类型
    
            //通过迭代器遍历集合
            Iterator it = list.iterator();
            while (it.hasNext()) {
                //多态的弊端是不能访问子类的特有方法
                //我们没有给集合指定类型,默认认为所有的数据类型都是Object类型的
                Object o = it.next();
                //o.length(); 采用字符串中Length()方法获取字符串的长度报错 (可以采用强转,但是集合里面有别的类型的,强转比较麻烦)
                System.out.print(o + "  ");        //没有用泛型的方法:123  aaa  true
            }
        }
    
        //有用泛型的方法
        public static void method2() {
            //创建集合并添加元素
            ArrayList<String> list = new ArrayList<>(); //泛型<String>表明该集合只能存入String类型的数据
            list.add("aaa");
            list.add("bbb");
            list.add("ccc");
            list.add("ddd");
    
            //遍历集合并输出数据
            Iterator<String> it = list.iterator();
            while (it.hasNext()) {
                String str = it.next();
                System.out.print(str + "  ");         //有用泛型的方法:aaa  bbb  ccc  ddd
            }
        }
    
    }
    
    
  • 输出结果
    在这里插入图片描述

2. 泛型类

泛型类是使用泛型参数的类。通过在类名后使用尖括号<>包围的类型参数,可以定义泛型类。这样的泛型类可以在类的任何位置使用类型参数作为参数类型或返回类型。

2.1 概述

泛型类:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类

格式:

	    修饰符 class 类名 <类型> {
	
	    }

举例:

public class ArrayList<E>{  //创建该对象时,E就确定了类型
	    //此处E可以理解为变量,但是不是用来记录数据的,而是记录数据的类型。
	
	        }

2.2 代码示例

要求:自己创建一个带有泛型的ArrayList集合(当在编写一个类的时候,如果不能确认类型,那么这个类可以定义为泛型类)

  • 代码示例

    package text.text02;
    
    import java.util.Arrays;
    
    /*
    泛型类:
        当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类
    
    格式:
        修饰符 class 类名 <类型> {
    
        }
    
    要求:自己创建一个带有泛型的ArrayList集合(当在编写一个类的时候,如果不能确认类型,那么这个类可以定义为泛型类)
     */
    public class text31 {
        public static void main(String[] args) {
            //创建MyArrayList的集合对象并添加数据
            MyArrayList<String> list = new MyArrayList<>();
            list.add("aaa");
            list.add("ddd");
            list.add("ccc");
            System.out.println(list);      //[aaa, ddd, ccc, null, null, null, null, null, null, null]
    
            //获取索引为1的元素
            String str = list.get(1);
            System.out.println("索引为1的元素:" + str);          //索引为1的元素:ddd
        }
    }
    
    class MyArrayList<E> {
        //创建一个长度为10的数组
        Object[] arr = new Object[10];
        //定义变量记录存储数据的长度,初始化为0
        int size;
    
        /*
        E:表示是不确定的类型,该类型在类名后面已经定义过了
        e:形参的名字,变量名
         */
        //添加数据的方法
        public boolean add(E e) {
            arr[size] = e;
            size++;
            return true;
        }
    
        //获取数据的方法
        public E get(int index) {
            return (E) arr[index];
        }
    
        //重写toString方法
        @Override
        public String toString() {
            return Arrays.toString(arr);
        }
    }
    
  • 输出结果
    在这里插入图片描述

3. 泛型方法

泛型方法是在方法定义中使用泛型参数的方法。通过在方法名前使用尖括号<>包围的类型参数,可以定义泛型方法。这样的泛型方法可以在方法的参数类型、返回类型或方法体中使用类型参数。

3.1 概述

泛型方法:

  1. 使用类名后面定义的类型(所有的方法都能使用)

  2. 在方法申明上定义自己的泛型(只有本方法能用)

格式:

 修饰符 <类型> 返回值类型 方法名 (类型  变量名){
   
 }

例如:

public <T> void add(T t){
	    //此处T可以理解为变量,但是不是用来记录数据的,而是记录数据的类型。
	
}

3.2 代码示例

要求:定义一个工具类:ListUtil,类中定义一个静态方法addAll,用来将多个元素添加进集合。

  • 代码示例

    package text.text02;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    
    /*
    泛型方法:
        1.使用类名后面定义的类型(所有的方法都能使用)
        2.在方法申明上定义自己的泛型(只有本方法能用)
    
    格式:
        修饰符 <类型> 返回值类型 方法名 (类型  变量名){
    
        }
    
    要求:定义一个工具类:ListUtil,类中定义一个静态方法addAll,用来将多个元素添加进集合。
     */
    public class text32 {
        public static void main(String[] args) {
            //创建集合对象
            ArrayList<String> list = new ArrayList<>();
    
            //调用addAll方法
            ListUtil.addAll(list, "aaa", "bbb", "ccc", "ddd", "eee", "fff");
    
            System.out.println(list);         //[aaa, bbb, ccc, ddd, eee, fff]
        }
    
    }
    
    //工具类:ListUtil
    class ListUtil {
        //私有化构造方法
        private ListUtil() {
        }
    
        //在方法申明上定义自己的泛型(只有本方法能用) 
        public static <T> void addAll(ArrayList<T> list, T t1, T t2, T t3, T t4, T t5, T t6) {   //添加多个元素:T... t(底层是一个可变数组))
            list.add(t1);
            list.add(t2);
            list.add(t3);
            list.add(t4);
            list.add(t5);
            list.add(t6);
        }
    }
    
    
  • 输出结果
    在这里插入图片描述

4. 泛型接口

泛型接口是在接口定义中使用泛型参数的接口。通过在接口名称后面使用尖括号<>包围的类型参数,可以定义泛型接口。这样的泛型接口可以在接口的方法参数类型、返回类型或方法体中使用类型参数。

4.1 概述

泛型接口:
定义一个接口时,数据类型不确定时,就可以定义带有泛型的接口。

格式:

修饰符 interface 接口名 <类型>{
	
	    }

例如:

 public interface  List<E>{
	    //此处E可以理解为变量,但是不是用来记录数据的,而是记录数据的类型。
	
 }

泛型接口的使用方法:

  • 方法1:实现类给出具体的类型

  • 方法2:实现类延续泛型,创建对象时再确定

4.2 代码示例

  • 代码示例

    package text.text02;
    
    import java.util.*;
    
    /*
    泛型接口:
        定义一个接口时,数据类型不确定时,就可以定义带有泛型的接口。
    
    格式:修饰符 interface 接口名 <类型>{
    
        }
    
    泛型接口的使用方法:
    方法1:实现类给出具体的类型
    方法2:实现类延续泛型,创建对象时再确定
     */
    public class text33 {
        public static void main(String[] args) {
            //方法1:实现类给出具体的类型,创建对象时不用确认数据类型
            MyArrayList1 list1 = new MyArrayList1();
            list1.add("aaa");
            list1.add("bbb");
            list1.add("ccc");
            //list1.add(123);由于在实现类中确认的是字符串类型的,因此添加Integer类型的会报错
            System.out.print("方法1:实现类给出具体的类型,创建对象时不用确认数据类型:");
            for (int i = 0; i < list1.size(); i++) {
                System.out.print(list1.get(i) + "  ");           //aaa  bbb  ccc
            }
    
            System.out.println();
    
            //方法2:实现类延续泛型,创建对象时再确定
            MyArrayList2<String> list2 = new MyArrayList2<>();
            list2.add("ddd");
            list2.add("eee");
            list2.add("fff");
            //list2.add(123);由于在创建对象时确定了是字符串类型的,因此添加Integer类型的会报错
            System.out.print("方法2:实现类延续泛型,创建对象时再确定:");
            for (int i = 0; i < list2.size(); i++) {
                System.out.print(list2.get(i) + "  ");           //ddd  eee  fff
            }
        }
    }
    
    //方法1:实现类给出具体的类型
    class MyArrayList1 implements List<String> {     //List是一个Java已经定义好的为泛型的接口 :public interface List<E> extends Collection<E>
        Object[] arr = new Object[10];
        int size;
    
        @Override
        public int size() {
            return size;
        }
    
        @Override
        public boolean isEmpty() {
            return false;
        }
    
        @Override
        public boolean contains(Object o) {
            return false;
        }
    
        @Override
        public Iterator<String> iterator() {
            return null;
        }
    
        @Override
        public Object[] toArray() {
            return new Object[0];
        }
    
        @Override
        public <T> T[] toArray(T[] a) {
            return null;
        }
    
        @Override
        public boolean add(String s) {
            arr[size] = s;
            size++;
            return true;
        }
    
        @Override
        public boolean remove(Object o) {
            return false;
        }
    
        @Override
        public boolean containsAll(Collection<?> c) {
            return false;
        }
    
        @Override
        public boolean addAll(Collection<? extends String> c) {
            return false;
        }
    
        @Override
        public boolean addAll(int index, Collection<? extends String> c) {
            return false;
        }
    
        @Override
        public boolean removeAll(Collection<?> c) {
            return false;
        }
    
        @Override
        public boolean retainAll(Collection<?> c) {
            return false;
        }
    
        @Override
        public void clear() {
    
        }
    
        @Override
        public String get(int index) {
            return (String) arr[index];
        }
    
        @Override
        public String set(int index, String element) {
            return null;
        }
    
        @Override
        public void add(int index, String element) {
    
        }
    
        @Override
        public String remove(int index) {
            return null;
        }
    
        @Override
        public int indexOf(Object o) {
            return 0;
        }
    
        @Override
        public int lastIndexOf(Object o) {
            return 0;
        }
    
        @Override
        public ListIterator<String> listIterator() {
            return null;
        }
    
        @Override
        public ListIterator<String> listIterator(int index) {
            return null;
        }
    
        @Override
        public List<String> subList(int fromIndex, int toIndex) {
            return null;
        }
    
    
    }
    
    //方法2:实现类延续泛型,创建对象时再确定
    class MyArrayList2<E> implements List<E> {
        Object[] arr = new Object[10];
        int size;
    
        @Override
        public int size() {
            return size;
        }
    
        @Override
        public boolean isEmpty() {
            return false;
        }
    
        @Override
        public boolean contains(Object o) {
            return false;
        }
    
        @Override
        public Iterator<E> iterator() {
            return null;
        }
    
        @Override
        public Object[] toArray() {
            return new Object[0];
        }
    
        @Override
        public <T> T[] toArray(T[] a) {
            return null;
        }
    
        @Override
        public boolean add(E s) {
            arr[size] = s;
            size++;
            return true;
        }
    
        @Override
        public boolean remove(Object o) {
            return false;
        }
    
        @Override
        public boolean containsAll(Collection<?> c) {
            return false;
        }
    
        @Override
        public boolean addAll(Collection<? extends E> c) {
            return false;
        }
    
        @Override
        public boolean addAll(int index, Collection<? extends E> c) {
            return false;
        }
    
        @Override
        public boolean removeAll(Collection<?> c) {
            return false;
        }
    
        @Override
        public boolean retainAll(Collection<?> c) {
            return false;
        }
    
        @Override
        public void clear() {
    
        }
    
        @Override
        public E get(int index) {
            return (E) arr[index];
        }
    
        @Override
        public E set(int index, E element) {
            return null;
        }
    
        @Override
        public void add(int index, E element) {
    
        }
    
        @Override
        public E remove(int index) {
            return null;
        }
    
        @Override
        public int indexOf(Object o) {
            return 0;
        }
    
        @Override
        public int lastIndexOf(Object o) {
            return 0;
        }
    
        @Override
        public ListIterator<E> listIterator() {
            return null;
        }
    
        @Override
        public ListIterator<E> listIterator(int index) {
            return null;
        }
    
        @Override
        public List<E> subList(int fromIndex, int toIndex) {
            return null;
        }     //List是一个Java已经定义好的为泛型的接口 :public interface List<E> extends Collection<E>
    
    }
    
  • 输出结果
    在这里插入图片描述

5. 泛型特点

5.1 概述

泛型是一种参数化类型的机制,它在编程语言中引入了类型参数,使得代码可以更加通用和类型安全。

泛型不具备继承关系,但是数据可以继承

5.2 代码示例

package text.text02;

import java.util.ArrayList;

/*
泛型不具备继承关系,但是数据可以继承
*/
public class text34 {
    public static void main(String[] args) {

    }

    //泛型不具备继承关系,但是数据可以继承
    public static void method1() {
        //创建集合对象
        ArrayList<Ye1> list1 = new ArrayList<>();
        ArrayList<Fu1> list2 = new ArrayList<>();
        ArrayList<Zi1> list3 = new ArrayList<>();

        //泛型不具备继承关系
        //调用method2方法,将创建的集合对象作为参数传进去
        method2(list1);
        //method2(list2);代码报错,说明泛型不具备继承关系
        //method2(list3);代码报错,说明泛型不具备继承关系

        //泛型数据可以继承
        list1.add(new Ye1());
        list1.add(new Fu1());
        list1.add(new Zi1());
    }

    //定义一个方法,只能接受Fu类类型的数据
    public static void method2(ArrayList<Ye1> list) {

    }
}


//定义一个继承关系:Ye1 Fu1 Zi1
class Ye1 {
}

class Fu1 extends Ye1 {
}

class Zi1 extends Fu1 {
}

6. 泛型通配符

泛型通配符(Wildcard)是 Java 泛型中的一个特性,用于在泛型类、接口和方法中表示一种未知类型。它使用问号(?)来表示。通配符允许你使用不具体的类型参数,从而增加代码的灵活性和通用性。

6.1 概述

泛型的通配符:? (限定类型的范围)

  • ? extends E : 表示可以传递E或者E的子类类型

上界通配符:使用上界通配符? extends 类型表示未知类型是给定类型的子类型或本身。它限制了类型的上界,表示类型必须是某个具体类型或其子类型。通常用于限制方法的参数类型或方法的返回类型。

  • ? super E :表示可以传递E或者E的父类类型

下界通配符:使用下界通配符? super 类型表示未知类型是给定类型的父类型或本身。它限制了类型的下界,表示类型必须是某个具体类型或其父类型。通常用于限制方法的参数类型。

泛型通配符的主要特点:

  • 灵活性:通配符允许你使用一种未知的类型,从而使得代码可以处理不同的类型参数。

  • 通用性:通配符可以适用于任何类型,使得代码更加通用和可重用。

  • 安全性:泛型通配符提供了类型安全性,编译器会检查通配符使用的合法性,并在编译时发出警告或错误。

应用场景:

  1. 如果我们在定义类,方法,接口的时候,如果类型不确定,就可以定义泛型类,泛型方法,泛型接口

  2. 如果类型不确定,但是能知道以后只能传递某个继承体系的,就可以使用泛型的通配符

6.2 代码示例

需求:定义一个方法,形参是一个集合,但是集合中的数据类型不能确定

package text.text02;
/*
泛型的通配符:?  (限定类型的范围)
    ? extends E : 表示可以传递E或者E的子类类型
    ? super E :表示可以传递E或者E的父类类型

 */

import java.util.ArrayList;

public class text35 {
    public static void main(String[] args) {
        //创建集合并调用method系列方法
        ArrayList<Ye2> list1 = new ArrayList<>();
        ArrayList<Fu2> list2 = new ArrayList<>();
        ArrayList<Zi2> list3 = new ArrayList<>();
        ArrayList<Student> list4 = new ArrayList<>();

        //利用泛型方法有个弊端,此时method1方法可以接受任意的数据类型
        method1(list1);
        method1(list2);
        method1(list3);
        method1(list4);   //list4是学生类型的,没有继承关系,但是依旧可以调用method1方法


        //利用泛型通配符的方式一:? extends E : 表示可以传递E或者E的子类类型
        method2(list1);
        method2(list2);
        method2(list3);
        //method2(list4);代码报错,因为list4是学生类型的,没有继承关系,因此不能调用method2方法

        //利用泛型通配符的方式二:? super E :表示可以传递E或者E的父类类型
        method3(list1);
        method3(list2);
        method3(list3);
        //method3(list4);代码报错,因为list4是学生类型的,没有继承关系,因此不能调用method3方法
    }

    //可以利用泛型方法的方式,但是利用泛型方法有个弊端,此时method1方法可以接受任意的数据类型
    public static <E> void method1(ArrayList<E> list) {

    }

    //利用泛型通配符的方式
    //? extends E : 表示可以传递E或者E的子类类型
    //? super E :表示可以传递E或者E的父类类型
    public static void method2(ArrayList<? extends Ye2> list) {  //说明该方法只能传递Ye2数据类型的或者继承Ye2数据类型的list对象

    }

    public static void method3(ArrayList<? super Zi2> list) {  //说明该方法只能传递Zi2数据类型的或者Zi2数据类型的父类的list对象

    }
}

//定义一个继承关系:Ye2 Fu2 Zi2
class Ye2 {
}

class Fu2 extends Ye2 {
}

class Zi2 extends Fu2 {
}

//再定义一个没有继承结构的学生类
class Student {
}

7. 综合案例

  • 需求:

      定义一个继承结构:
                                        动物
         					    |                 |
                                 猫                狗
                          |           |        |        |
                       波斯猫        狸花猫     泰迪    哈士奇
    
     属性:名字,年龄
     行为:吃东西
          方法体打印:一只叫做XXX的,X岁的波斯猫,正在吃小饼干
          方法体打印:一只叫做XXX的,X岁的狸花猫,正在吃鱼
          方法体打印:一只叫做XXX的,X岁的泰迪,正在吃骨头,边吃边蹭
          方法体打印:一只叫做XXX的,X岁的哈士奇,正在吃骨头,边吃边拆家
    
     测试类中定义一个方法,用来饲养动物
     public static void keepPet(ArrayList<???> list){
      //遍历集合,调用动物的eat方法
     }
    
     要求1:该方法能养所有品种的猫,但是不能养狗
     要求2:该方法能养所有品种的狗,但是不能养猫
     要求3:该方法能养所有品种的动物,但是不能传递其他类型
    
  • 代码实现:

    • 组父类:Animal类
    package code.code36;
    
    public abstract class Animal {
        private String name;
        private int age;
    
        public Animal() {
        }
    
        public Animal(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        /**
         * 获取
         *
         * @return name
         */
        public String getName() {
            return name;
        }
    
        /**
         * 设置
         *
         * @param name
         */
        public void setName(String name) {
            this.name = name;
        }
    
        /**
         * 获取
         *
         * @return age
         */
        public int getAge() {
            return age;
        }
    
        /**
         * 设置
         *
         * @param age
         */
        public void setAge(int age) {
            this.age = age;
        }
    
        public String toString() {
            return "Animal{name = " + name + ", age = " + age + "}";
        }
    
        public abstract  void eat();
    }
    
    
    • 父类:Cat类
    package code.code36;
    
    public abstract class Cat extends Animal {
    
        public Cat() {
        }
    
        public Cat(String name, int age) {
            super(name, age);
        }
    
    }
    
    
    • 父类:Dog类
    package code.code36;
    
    public abstract class Dog extends Animal {
    
        public Dog() {
        }
    
        public Dog(String name, int age) {
            super(name, age);
        }
    
    }
    
    
    • 子类:波斯猫类
    package code.code36;
    
    //波斯猫
    public class PersianCat extends Cat {
        public PersianCat() {
        }
    
        public PersianCat(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void eat() {
            System.out.println("一只叫做" + this.getName() + "的," + this.getAge() + "岁的波斯猫,正在吃小饼干");
        }
    }
    
    
    • 子类:狸花猫类
     package code.code36;
    
    //狸花猫
    public class LiHuaCat extends Cat {
        public LiHuaCat() {
        }
    
        public LiHuaCat(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void eat() {
            System.out.println("一只叫做" + this.getName() + "的," + this.getAge() + "岁的狸花猫,正在吃鱼");
        }
    
    }
    
    
    • 子类:哈士奇类
    package code.code36;
    
    //哈士奇
    public class HaShiQi extends Dog {
        public HaShiQi() {
        }
    
        public HaShiQi(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void eat() {
            System.out.println("一只叫做" + this.getName() + "的," + this.getAge() + "岁的哈士奇,正在吃骨头,边吃边拆家");
        }
    }
    
    
    • 子类:泰迪类
    package code.code36;
    
    //泰迪
    public class TaiDi extends Dog {
        public TaiDi() {
        }
    
        public TaiDi(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void eat() {
            System.out.println("一只叫做" + this.getName() + "的," + this.getAge() + "岁的泰迪,正在吃骨头,边吃边蹭");
        }
    
    }
    
    
    • 测试类:
    package code.code36;
    
    import java.util.ArrayList;
    
    public class CodeText36 {
       public static <E> void main(String[] args) {
           //创建动物的集合对象,并将动物对象添加进集合
           ArrayList<PersianCat> list1 = new ArrayList<>();
           ArrayList<LiHuaCat> list2 = new ArrayList<>();
           ArrayList<TaiDi> list3 = new ArrayList<>();
           ArrayList<HaShiQi> list4 = new ArrayList<>();
    
           list1.add(new PersianCat("波斯", 2));
           list2.add(new LiHuaCat("狸花", 3));
           list3.add(new TaiDi("泰迪", 4));
           list4.add(new HaShiQi("二哈", 5));
    
           //要求1:该方法能养所有品种的猫,但是不能养狗
           System.out.println("要求1:该方法能养所有品种的猫,但是不能养狗:");
           keepPet1(list1);         //一只叫做波斯的,2岁的波斯猫,正在吃小饼干
           keepPet1(list2);         //一只叫做狸花的,3岁的狸花猫,正在吃鱼
    
           System.out.println("===============================");
    
           //要求2:该方法能养所有品种的狗,但是不能养猫
           System.out.println("要求2:该方法能养所有品种的狗,但是不能养猫:");
           keepPet2(list3);         //一只叫做泰迪的,4岁的泰迪,正在吃骨头,边吃边蹭
           keepPet2(list4);         //一只叫做二哈的,5岁的哈士奇,正在吃骨头,边吃边拆家
    
           System.out.println("===============================");
    
           //要求3:该方法能养所有品种的动物,但是不能传递其他类型
           System.out.println("要求3:该方法能养所有品种的动物,但是不能传递其他类型:");
           keepPet3(list1);        //一只叫做波斯的,2岁的波斯猫,正在吃小饼干
           keepPet3(list2);        //一只叫做狸花的,3岁的狸花猫,正在吃鱼
           keepPet3(list3);        //一只叫做泰迪的,4岁的泰迪,正在吃骨头,边吃边蹭
           keepPet3(list4);        //一只叫做二哈的,5岁的哈士奇,正在吃骨头,边吃边拆家
       }
    
       //要求1:该方法能养所有品种的猫,但是不能养狗
       public static void keepPet1(ArrayList<? extends Cat> list) {
           //遍历集合,调用动物的eat方法
           for (Cat cat : list) {
               cat.eat();
           }
       }
    
       //要求2:该方法能养所有品种的狗,但是不能养猫
       public static void keepPet2(ArrayList<? extends Dog> list) {
           //遍历集合,调用动物的eat方法
           for (Dog dog : list) {
               dog.eat();
           }
       }
    
       //要求3:该方法能养所有品种的动物,但是不能传递其他类型
       public static void keepPet3(ArrayList<? extends Animal> list) {
           //遍历集合,调用动物的eat方法
           for (Animal animal : list) {
               animal.eat();
           }
       }
    }
    
    
    • 输出结果:
      在这里插入图片描述

8. 注意事项

  1. 参数化类型不能使用基本数据类型

泛型的类型参数只能是引用类型,不能是基本数据类型。如果需要使用基本数据类型,可以使用对应的包装类(如Integer代替int)。

  1. 无法创建具体的泛型类型的实例

不能直接使用泛型类型来创建对象,例如List<String> list = new List<String>();是错误的。可以使用原始类型(如List)或通配符来创建对象,例如List<?> list = new ArrayList<>();是正确的。

  1. 泛型类型的类型参数在运行时被擦除

泛型在编译时进行类型检查,但在运行时会被擦除为原始类型。这意味着在运行时无法获得泛型类型的具体参数类型。例如,对于List<String>List<Integer>,在运行时它们都被视为List<Object>

  1. 泛型数组的创建受限

无法直接创建带有泛型类型参数的数组,例如List<String>[] array = new List<String>[10];是错误的。可以使用通配符或原始类型数组,例如List<?>[] array = new List<?>[10];是正确的。

  1. 不能使用基本类型作为类型参数

泛型类型参数不能是基本类型,只能是引用类型。如果需要使用基本类型作为类型参数,可以使用对应的包装类。

  1. 泛型类型的类型参数不能是基本类型的数组

泛型类型的类型参数不能是基本类型的数组,例如List<int[]>是错误的。

  1. 类型擦除可能导致运行时异常

由于泛型的类型参数在运行时被擦除,可能会导致在运行时出现类型相关的异常。因此,在使用泛型时要格外小心,并进行必要的类型检查和转换。

  1. 通配符的使用限制

使用通配符时,不能使用null以外的具体值,只能用于方法声明、参数传递和通配符类型的赋值操作。

  1. 泛型类型的兼容性

泛型类型在类型参数相同的情况下才具有兼容性。例如,List<String>List<Integer>不是兼容类型。

  • 71
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷小洋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值